X-Git-Url: https://git.sesse.net/?p=cubemap;a=blobdiff_plain;f=main.cpp;h=de3900b9ac6acc676143e9ae019099b77c9175e7;hp=dc52bdc29a17ab85f1ad1d5c8f23ef34a54c0604;hb=92aebf172247328aa8928aa421a69854975236f4;hpb=3b2b74d6be17096c2fe830bfa07fbcecb4460b81 diff --git a/main.cpp b/main.cpp index dc52bdc..de3900b 100644 --- a/main.cpp +++ b/main.cpp @@ -25,6 +25,7 @@ #include "server.h" #include "serverpool.h" #include "input.h" +#include "stats.h" #include "state.pb.h" using namespace std; @@ -37,68 +38,6 @@ void hup(int ignored) hupped = true; } -struct StatsThreadParameters { - string stats_file; - int stats_interval; -}; - -void *stats_thread_run(void *arg) -{ - const StatsThreadParameters *parms = reinterpret_cast(arg); - while (!hupped) { - int fd; - FILE *fp; - time_t now; - vector client_stats; - - // Open a new, temporary file. - char *filename = strdup((parms->stats_file + ".new.XXXXXX").c_str()); - fd = mkostemp(filename, O_WRONLY); - if (fd == -1) { - perror(filename); - free(filename); - goto sleep; - } - - fp = fdopen(fd, "w"); - if (fp == NULL) { - perror("fdopen"); - close(fd); - unlink(filename); - free(filename); - goto sleep; - } - - now = time(NULL); - client_stats = servers->get_client_stats(); - for (size_t i = 0; i < client_stats.size(); ++i) { - fprintf(fp, "%s %s %d %llu\n", - client_stats[i].remote_addr.c_str(), - client_stats[i].stream_id.c_str(), - int(now - client_stats[i].connect_time), - (long long unsigned)(client_stats[i].bytes_sent)); - } - if (fclose(fp) == EOF) { - perror("fclose"); - unlink(filename); - free(filename); - goto sleep; - } - - if (rename(filename, parms->stats_file.c_str()) == -1) { - perror("rename"); - unlink(filename); - } - -sleep: - int left_to_sleep = parms->stats_interval; - do { - left_to_sleep = sleep(left_to_sleep); - } while (left_to_sleep > 0 && !hupped); - } - return NULL; -} - // Serialize the given state to a file descriptor, and return the (still open) // descriptor. int make_tempfile(const CubemapStateProto &state) @@ -214,6 +153,94 @@ MarkPool *parse_mark_pool(map, MarkPool *> *mark_pools, const str return get_mark_pool(mark_pools, from, to); } + +// Find all streams in the configuration file, and create inputs for them. +vector create_inputs(const vector &config, + map *deserialized_inputs) +{ + vector inputs; + for (unsigned i = 0; i < config.size(); ++i) { + if (config[i].keyword != "stream") { + continue; + } + assert(config[i].arguments.size() == 1); + string stream_id = config[i].arguments[0]; + + map::const_iterator src_it = + config[i].parameters.find("src"); + if (src_it == config[i].parameters.end()) { + fprintf(stderr, "WARNING: stream '%s' has no src= attribute, clients will not get any data.\n", + stream_id.c_str()); + continue; + } + + string src = src_it->second; + Input *input = NULL; + map::iterator deserialized_input_it = + deserialized_inputs->find(stream_id); + if (deserialized_input_it != deserialized_inputs->end()) { + input = deserialized_input_it->second; + if (input->get_url() != src) { + fprintf(stderr, "INFO: Stream '%s' has changed URL from '%s' to '%s', restarting input.\n", + stream_id.c_str(), input->get_url().c_str(), src.c_str()); + delete input; + input = NULL; + } + deserialized_inputs->erase(deserialized_input_it); + } + if (input == NULL) { + input = new Input(stream_id, src); + } + input->run(); + inputs.push_back(input); + } + return inputs; +} + +void create_streams(const vector &config, + const set &deserialized_stream_ids, + map *deserialized_inputs) +{ + set expecting_stream_ids = deserialized_stream_ids; + map, MarkPool *> mark_pools; + for (unsigned i = 0; i < config.size(); ++i) { + if (config[i].keyword != "stream") { + continue; + } + if (config[i].arguments.size() != 1) { + fprintf(stderr, "ERROR: 'stream' takes exactly one argument\n"); + exit(1); + } + string stream_id = config[i].arguments[0]; + if (deserialized_stream_ids.count(stream_id) == 0) { + servers->add_stream(stream_id); + } + expecting_stream_ids.erase(stream_id); + + // Set up marks, if so desired. + map::const_iterator mark_parm_it = + config[i].parameters.find("mark"); + if (mark_parm_it != config[i].parameters.end()) { + MarkPool *mark_pool = parse_mark_pool(&mark_pools, mark_parm_it->second); + servers->set_mark_pool(stream_id, mark_pool); + } + } + + // Warn about any servers we've lost. + // TODO: Make an option (delete=yes?) to actually shut down streams. + for (set::const_iterator stream_it = expecting_stream_ids.begin(); + stream_it != expecting_stream_ids.end(); + ++stream_it) { + string stream_id = *stream_it; + fprintf(stderr, "WARNING: stream '%s' disappeared from the configuration file.\n", + stream_id.c_str()); + fprintf(stderr, " It will not be deleted, but clients will not get any new inputs.\n"); + if (deserialized_inputs->count(stream_id) != 0) { + delete (*deserialized_inputs)[stream_id]; + deserialized_inputs->erase(stream_id); + } + } +} int main(int argc, char **argv) { @@ -265,43 +292,7 @@ int main(int argc, char **argv) } // Find all streams in the configuration file, and create them. - set expecting_stream_ids = deserialized_stream_ids; - map, MarkPool *> mark_pools; - for (unsigned i = 0; i < config.size(); ++i) { - if (config[i].keyword != "stream") { - continue; - } - if (config[i].arguments.size() != 1) { - fprintf(stderr, "ERROR: 'stream' takes exactly one argument\n"); - exit(1); - } - string stream_id = config[i].arguments[0]; - if (deserialized_stream_ids.count(stream_id) == 0) { - servers->add_stream(stream_id); - } - expecting_stream_ids.erase(stream_id); - - // Set up marks, if so desired. - if (config[i].parameters.count("mark")) { - MarkPool *mark_pool = parse_mark_pool(&mark_pools, config[i].parameters["mark"]); - servers->set_mark_pool(stream_id, mark_pool); - } - } - - // Warn about any servers we've lost. - // TODO: Make an option (delete=yes?) to actually shut down streams. - for (set::const_iterator stream_it = expecting_stream_ids.begin(); - stream_it != expecting_stream_ids.end(); - ++stream_it) { - string stream_id = *stream_it; - fprintf(stderr, "WARNING: stream '%s' disappeared from the configuration file.\n", - stream_id.c_str()); - fprintf(stderr, " It will not be deleted, but clients will not get any new inputs.\n"); - if (deserialized_inputs.count(stream_id) != 0) { - delete deserialized_inputs[stream_id]; - deserialized_inputs.erase(stream_id); - } - } + create_streams(config, deserialized_stream_ids, &deserialized_inputs); // Open a new server socket if we do not already have one, or if we changed ports. if (server_sock != -1 && port != old_port) { @@ -322,42 +313,13 @@ int main(int argc, char **argv) servers->run(); - pthread_t acceptor_thread; - pthread_create(&acceptor_thread, NULL, acceptor_thread_run, reinterpret_cast(server_sock)); - - // Find all streams in the configuration file, and create inputs for them. - vector inputs; - for (unsigned i = 0; i < config.size(); ++i) { - if (config[i].keyword != "stream") { - continue; - } - assert(config[i].arguments.size() == 1); - string stream_id = config[i].arguments[0]; + AcceptorThread acceptor_thread(server_sock); + acceptor_thread.run(); - if (config[i].parameters.count("src") == 0) { - fprintf(stderr, "WARNING: stream '%s' has no src= attribute, clients will not get any data.\n", - stream_id.c_str()); - continue; - } - - string src = config[i].parameters["src"]; - Input *input = NULL; - if (deserialized_inputs.count(stream_id) != 0) { - input = deserialized_inputs[stream_id]; - if (input->get_url() != src) { - fprintf(stderr, "INFO: Stream '%s' has changed URL from '%s' to '%s', restarting input.\n", - stream_id.c_str(), input->get_url().c_str(), src.c_str()); - delete input; - input = NULL; - } - deserialized_inputs.erase(stream_id); - } - if (input == NULL) { - input = new Input(stream_id, src); - } - input->run(); - inputs.push_back(input); - } + vector inputs = create_inputs(config, &deserialized_inputs); + + // All deserialized inputs should now have been taken care of, one way or the other. + assert(deserialized_inputs.empty()); if (is_reexec) { // Put back the existing clients. It doesn't matter which server we @@ -368,16 +330,11 @@ int main(int argc, char **argv) } } - // All deserialized inputs should now have been taken care of, one way or the other. - assert(deserialized_inputs.empty()); - // Start writing statistics. - pthread_t stats_thread; - StatsThreadParameters stats_parameters; // Must live for as long as the stats thread does. + StatsThread *stats_thread = NULL; if (!stats_file.empty()) { - stats_parameters.stats_file = stats_file; - stats_parameters.stats_interval = stats_interval; - pthread_create(&stats_thread, NULL, stats_thread_run, &stats_parameters); + stats_thread = new StatsThread(stats_file, stats_interval); + stats_thread->run(); } signal(SIGHUP, hup); @@ -400,18 +357,10 @@ int main(int argc, char **argv) // OK, we've been HUPed. Time to shut down everything, serialize, and re-exec. gettimeofday(&serialize_start, NULL); - if (!stats_file.empty()) { - pthread_kill(stats_thread, SIGHUP); - if (pthread_join(stats_thread, NULL) == -1) { - perror("pthread_join"); - exit(1); - } - } - pthread_kill(acceptor_thread, SIGHUP); - if (pthread_join(acceptor_thread, NULL) == -1) { - perror("pthread_join"); - exit(1); + if (stats_thread != NULL) { + stats_thread->stop(); } + acceptor_thread.stop(); CubemapStateProto state; state.set_serialize_start_sec(serialize_start.tv_sec);