#include <stdio.h>
#include <string.h>
#include <stdint.h>
+#include <unistd.h>
#include <assert.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include "serverpool.h"
#include "input.h"
#include "stats.h"
+#include "util.h"
#include "version.h"
#include "state.pb.h"
hupped = true;
}
-// Serialize the given state to a file descriptor, and return the (still open)
-// descriptor.
-int make_tempfile(const CubemapStateProto &state)
-{
- char tmpl[] = "/tmp/cubemapstate.XXXXXX";
- int state_fd = mkstemp(tmpl);
- if (state_fd == -1) {
- perror("mkstemp");
- exit(1);
- }
-
- string serialized;
- state.SerializeToString(&serialized);
-
- const char *ptr = serialized.data();
- size_t to_write = serialized.size();
- while (to_write > 0) {
- ssize_t ret = write(state_fd, ptr, to_write);
- if (ret == -1) {
- perror("write");
- exit(1);
- }
-
- ptr += ret;
- to_write -= ret;
- }
-
- return state_fd;
-}
-
CubemapStateProto collect_state(const timeval &serialize_start,
const vector<Acceptor *> acceptors,
const vector<Input *> inputs,
return state;
}
-// Read the state back from the file descriptor made by make_tempfile,
-// and close it.
-CubemapStateProto read_tempfile(int state_fd)
-{
- if (lseek(state_fd, 0, SEEK_SET) == -1) {
- perror("lseek");
- exit(1);
- }
-
- string serialized;
- char buf[4096];
- for ( ;; ) {
- ssize_t ret = read(state_fd, buf, sizeof(buf));
- if (ret == -1) {
- perror("read");
- exit(1);
- }
- if (ret == 0) {
- // EOF.
- break;
- }
-
- serialized.append(string(buf, buf + ret));
- }
-
- close(state_fd); // Implicitly deletes the file.
-
- CubemapStateProto state;
- if (!state.ParseFromString(serialized)) {
- fprintf(stderr, "PANIC: Failed deserialization of state.\n");
- exit(1);
- }
-
- return state;
-}
-
// Find all port statements in the configuration file, and create acceptors for htem.
vector<Acceptor *> create_acceptors(
const Config &config,
for (unsigned i = 0; i < config.streams.size(); ++i) {
const StreamConfig &stream_config = config.streams[i];
if (deserialized_stream_ids.count(stream_config.stream_id) == 0) {
- servers->add_stream(stream_config.stream_id);
+ servers->add_stream(stream_config.stream_id, stream_config.backlog_size);
}
expecting_stream_ids.erase(stream_config.stream_id);
}
}
}
+
+bool dry_run_config(const std::string &argv0, const std::string &config_filename)
+{
+ char *argv0_copy = strdup(argv0.c_str());
+ char *config_filename_copy = strdup(config_filename.c_str());
+
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ perror("fork()");
+ free(argv0_copy);
+ free(config_filename_copy);
+ return false;
+ case 0:
+ // Child.
+ execlp(argv0_copy, argv0_copy, "--test-config", config_filename_copy, NULL);
+ perror(argv0_copy);
+ _exit(1);
+ default:
+ // Parent.
+ break;
+ }
+
+ free(argv0_copy);
+ free(config_filename_copy);
+
+ int status;
+ pid_t err;
+ do {
+ err = waitpid(pid, &status, 0);
+ } while (err == -1 && errno == EINTR);
+
+ if (err == -1) {
+ perror("waitpid()");
+ return false;
+ }
+
+ return (WIFEXITED(status) && WEXITSTATUS(status) == 0);
+}
int main(int argc, char **argv)
{
config_filename = argv[optind++];
}
- struct timeval serialize_start;
-
Config config;
if (!parse_config(config_filename, &config)) {
exit(1);
exit(0);
}
+start:
fprintf(stderr, "\nCubemap " SERVER_VERSION " starting.\n");
servers = new ServerPool(config.num_servers);
CubemapStateProto loaded_state;
+ struct timeval serialize_start;
set<string> deserialized_stream_ids;
map<string, Input *> deserialized_inputs;
map<int, Acceptor *> deserialized_acceptors;
if (state_fd != -1) {
fprintf(stderr, "Deserializing state from previous process... ");
- loaded_state = read_tempfile(state_fd);
+ string serialized;
+ if (!read_tempfile(state_fd, &serialized)) {
+ exit(1);
+ }
+ if (!loaded_state.ParseFromString(serialized)) {
+ fprintf(stderr, "ERROR: Failed deserialization of state.\n");
+ exit(1);
+ }
serialize_start.tv_sec = loaded_state.serialize_start_sec();
serialize_start.tv_usec = loaded_state.serialize_start_usec();
if (!config.stats_file.empty()) {
stats_thread = new StatsThread(config.stats_file, config.stats_interval);
stats_thread->run();
- } else if (config.stats_interval != -1) {
- fprintf(stderr, "WARNING: 'stats_interval' given, but no 'stats_file'. No statistics will be written.\n");
}
signal(SIGHUP, hup);
servers->stop();
fprintf(stderr, "Serializing state and re-execing...\n");
- state_fd = make_tempfile(collect_state(
- serialize_start, acceptors, inputs, servers));
+ CubemapStateProto state = collect_state(
+ serialize_start, acceptors, inputs, servers);
+ string serialized;
+ state.SerializeToString(&serialized);
+ state_fd = make_tempfile(serialized);
+ if (state_fd == -1) {
+ exit(1);
+ }
delete servers;
+
+ if (!dry_run_config(argv[0], config_filename)) {
+ fprintf(stderr, "ERROR: %s --test-config failed. Restarting old version instead of new.\n", argv[0]);
+ hupped = false;
+ goto start;
+ }
char buf[16];
sprintf(buf, "%d", state_fd);