From d9cc1ccfad595d8f2d3909bbba15f1d1072dc1c5 Mon Sep 17 00:00:00 2001 From: Ari Johnson Date: Wed, 19 Mar 2008 22:25:16 +0000 Subject: [PATCH] Added transition.c --- utils/transition.c | 275 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 utils/transition.c diff --git a/utils/transition.c b/utils/transition.c new file mode 100644 index 0000000..07718a2 --- /dev/null +++ b/utils/transition.c @@ -0,0 +1,275 @@ +/* transition.c + * + * This utility will aid in a transition from a CobraMUSH running in one + * directory to one running in a different directory, without disconnecting + * users. + * + * To use this: + * Shell: + * gcc -o transition transition.c -DTARGET="/path/to/new/game/directory" + * cp transition /path/to/old/game/directory + * cd /path/to/old/game/directory + * rm netmush + * ln -s transition netmush + * Game: + * @shutdown/reboot + * Shell: + * cp reboot.db /path/to/new/game/directory + * cp data/* /path/to/new/game/directory/data + * Make sure that the new game directory is ready to run. This includes + * a working netmush link and a suitably matched mush.cnf. + * Shell: + * kill -USR1 pid_of_this_program (use -USR2 to cancel, but first + * replace the netmush link with one to netmud) + * When the restart is complete, check the game to be sure that everything + * worked. + */ + +#include +#include +#include +#include +#include + +#ifndef TARGET +#error Compile with -DTARGET="/path/to/new/game/directory" +#endif + +#define RDBF_SCREENSIZE 0x1 +#define RDBF_TTYPE 0x2 +#define RDBF_PUEBLO_CHECKSUM 0x4 +#define RDBF_SU_EXIT_PATH 0x10000 + +#define BUFFER_LEN 1024 + +struct desc { + struct desc *next; + int fd; +}; + +int sock; +int maxd; +struct desc *dlist; +int signaled; +int start_new; + +int load_reboot_db(void); +void signal_usr1(int); +void signal_usr2(int); +int getref(FILE *); +const char *getstring_noalloc(FILE *); +int safe_chr(char, char *, char **); +void tell_one_guy(int, const char *); +void tell_everyone(const char *); +void deal_with_everyone(void); + +int main(int argc, char *argv[]) { + struct desc *cur; + + signaled = 0; + start_new = 0; + signal(SIGUSR1, signal_usr1); + signal(SIGUSR2, signal_usr2); + + if(!load_reboot_db()) { + fprintf(stderr, "unable to load db\n"); + exit(1); + } + + printf("MUSH Server Transition Utility\n\n"); + + printf("sock = %d\nmaxd = %d\nfds =", sock, maxd); + for(cur = dlist; cur; cur = cur->next) + printf(" %d", cur->fd); + printf("\n"); + + printf("To trigger the action, kill -USR1 %d\n", getpid()); + printf("This will chdir to %s and run netmush mush.cnf\n", TARGET); + printf("You will need to copy reboot.db into the new location for this to work\n"); + printf("To cancel the action and run netmush mush.cnf in the current\ndirectory, kill -USR2 %d\n", getpid()); + printf("Of course, you will want to make sure that netmush points to a MUSH binary\ninstead of to this program before you do that.\n"); + + tell_everyone("GAME: Beginning transition to new server\n"); + + while(!signaled) + deal_with_everyone(); + + if(start_new) { + tell_everyone("GAME: Starting new server\n"); + chdir(TARGET); + } else { + tell_everyone("GAME: Operation cancelled. The old server will be restarted.\n"); + } + + execl("netmush", "netmush", "mush.cnf", NULL); +} + +void tell_one_guy(int fd, const char *str) { + write(fd, str, strlen(str)); +} + +void tell_everyone(const char *str) { + struct desc *cur; + + for(cur = dlist; cur; cur = cur->next) + tell_one_guy(cur->fd, str); +} + +void deal_with_everyone(void) { + fd_set inset; + struct timeval timeout; + struct desc *cur; + + FD_ZERO(&inset); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + for(cur = dlist; cur; cur = cur->next) + FD_SET(cur->fd, &inset); + + select(maxd, &inset, NULL, NULL, &timeout); + + for(cur = dlist; cur; cur = cur->next) + if(FD_ISSET(cur->fd, &inset)) + tell_one_guy(cur->fd, "GAME: Server transition in progress, input ignored\n"); +} + +void signal_usr1(int sig __attribute__ ((__unused__))) { + signaled = 1; + start_new = 1; +} + +void signal_usr2(int sig __attribute__ ((__unused__))) { + signaled = 1; +} + +int load_reboot_db(void) { + FILE *f; + struct desc *cur; + int val; + char c; + long flags = 0, connflags; + int exit_path; + + f = fopen("reboot.db", "r"); + if (!f) + return 0; + + c = getc(f); /* Skip the V */ + if (c == 'V') { + flags = getref(f); + } else { + fclose(f); + return 0; + } + + maxd = 0; + sock = getref(f); + val = getref(f); + if (val > maxd) + maxd = val; + + dlist = NULL; + while ((val = getref(f)) != 0) { + cur = (struct desc *) malloc(sizeof(struct desc)); + + cur->fd = val; + getref(f); /* connected_at */ + getref(f); /* hide */ + getref(f); /* cmds */ + getref(f); /* idle_total */ + getref(f); /* unidle_times */ + getref(f); /* player */ + getref(f); /* last_time */ + getstring_noalloc(f); /* output_prefix */ + getstring_noalloc(f); /* output_suffix */ + getstring_noalloc(f); /* addr */ + getstring_noalloc(f); /* ip */ + getstring_noalloc(f); /* doing */ + connflags = getref(f); + if(flags & RDBF_SCREENSIZE) { + getref(f); /* width */ + getref(f); /* height */ + } + if(flags & RDBF_TTYPE) + getstring_noalloc(f); /* ttype */ + if(flags & RDBF_PUEBLO_CHECKSUM) + getstring_noalloc(f); /* checksum */ + if(flags & RDBF_SU_EXIT_PATH) + for(exit_path = getref(f); exit_path >= 0; exit_path = getref(f)) + ; + + cur->next = dlist; + dlist = cur; + } + + fclose(f); + + return 1; +} + +int getref(FILE *f) { + static char buf[BUFFER_LEN]; + if (!fgets(buf, sizeof(buf), f)) { + fprintf(stderr, "unexpected eof\n"); + exit(1); + } + return strtol(buf, NULL, 10); +} + +const char *getstring_noalloc(FILE *f) { + static char buf[BUFFER_LEN]; + char *p; + int c; + + p = buf; + c = fgetc(f); + if (c == EOF) { + fprintf(stderr, "unexpected eof\n"); + exit(1); + } else if (c != '"') { + for (;;) { + if ((c == '\0') || (c == EOF) || + ((c == '\n') && ((p == buf) || (p[-1] != '\r')))) { + *p = '\0'; + return buf; + } + safe_chr(c, buf, &p); + c = fgetc(f); + } + } else { + for (;;) { + c = fgetc(f); + if (c == '"') { + /* It's a closing quote if it's followed by \r or \n */ + c = fgetc(f); + if (c == '\r') { + /* Get a possible \n, too */ + if ((c = fgetc(f)) != '\n') + ungetc(c, f); + } else if (c != '\n') + ungetc(c, f); + *p = '\0'; + return buf; + } else if (c == '\\') { + c = fgetc(f); + } + if ((c == '\0') || (c == EOF)) { + *p = '\0'; + return buf; + } + safe_chr(c, buf, &p); + } + } +} + +int safe_chr(char c, char *buf, char **bufp) { + /* adds a character to a string, being careful not to overflow buffer */ + + if ((*bufp - buf >= BUFFER_LEN - 1)) + return 1; + + *(*bufp)++ = c; + return 0; +} + -- 2.30.2