diff --git a/Changelog b/Changelog index e788ba1..9edc000 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,9 @@ 1.4.5 (????/??/??) * Reset offset if necessary to show at least one game to avoid things like "(15-14 of 14)". + * Backup the savefile before starting nethack to help prevent more + lost games. Note this must be explicitly configured in the + configuration file. 1.4.4 (2004/03/07) * Show total number of games in progress below the list, useful if diff --git a/config.l b/config.l index fedf2bb..241a34f 100644 --- a/config.l +++ b/config.l @@ -65,6 +65,7 @@ COMMENT ^#.* "rc_template" { return TYPE_PATH_CANNED; } "passwd" { return TYPE_PATH_PASSWD; } "lockfile" { return TYPE_PATH_LOCKFILE; } +"savefilefmt" { return TYPE_PATH_SAVEFILEFMT; } \n { line++; col = 0; } diff --git a/config.y b/config.y index ead0bb9..250ec74 100644 --- a/config.y +++ b/config.y @@ -28,7 +28,8 @@ static const char* lookup_token (int t); %token TYPE_SUSER TYPE_SGROUP TYPE_SGID TYPE_SUID TYPE_MAX %token TYPE_PATH_NETHACK TYPE_PATH_DGLDIR TYPE_PATH_SPOOL %token TYPE_PATH_BANNER TYPE_PATH_CANNED TYPE_PATH_CHROOT -%token TYPE_PATH_PASSWD TYPE_PATH_LOCKFILE TYPE_MALSTRING +%token TYPE_PATH_PASSWD TYPE_PATH_LOCKFILE TYPE_PATH_SAVEFILEFMT +%token TYPE_MALSTRING %token TYPE_VALUE %token TYPE_NUMBER %type KeyType @@ -147,6 +148,11 @@ KeyPair: KeyType '=' TYPE_VALUE { myconfig->passwd = strdup($3); break; + case TYPE_PATH_SAVEFILEFMT: + if (myconfig->savefilefmt) free(myconfig->savefilefmt); + myconfig->savefilefmt = strdup($3); + break; + default: fprintf(stderr, "%s:%d: token %s does not take a string, bailing out\n", config, line, lookup_token($1)); @@ -215,6 +221,7 @@ KeyType : TYPE_SUSER { $$ = TYPE_SUSER; } | TYPE_PATH_CANNED { $$ = TYPE_PATH_CANNED; } | TYPE_PATH_PASSWD { $$ = TYPE_PATH_PASSWD; } | TYPE_PATH_LOCKFILE { $$ = TYPE_PATH_LOCKFILE; } + | TYPE_PATH_SAVEFILEFMT { $$ = TYPE_PATH_SAVEFILEFMT; } ; %% diff --git a/dgamelaunch.8 b/dgamelaunch.8 index 019322b..b1ad11a 100644 --- a/dgamelaunch.8 +++ b/dgamelaunch.8 @@ -52,7 +52,7 @@ login(1) Ignored; solely for compatibility with .B login(1) -.SH CRASH RECOVERY +.SH "CRASH RECOVERY" .PP If a user somehow disconnects in an unclean way, .I @@ -69,7 +69,17 @@ nethack does not shut down within 10 seconds, .I dgamelaunch will ask the user for permission to send it the SIGTERM signal, which causes -nethack to terminate quickly. +nethack to terminate quickly (without leaving a savefile usually). +.PP +In some cases (e.g. at "Restoring save file...--More--") nethack doesn't leave +a savefile if sent SIGHUP. To avoid loss of games, +.I +dgamelaunch +can backup the savefile. A human must then restore the backup if necessary. +This must be configured with the +.B +savefilefmt +option in the configuration file. .SH AUTHORS .PP M. Drew Streib wrote the original version. diff --git a/dgamelaunch.c b/dgamelaunch.c index c9c4b7b..c73357a 100644 --- a/dgamelaunch.c +++ b/dgamelaunch.c @@ -1294,6 +1294,103 @@ writefile (int requirenew) /* ************************************************************* */ /* ************************************************************* */ +/* + * Backup the savefile, if configured. + * Returns non-zero if successful, otherwise an error message has been + * given already. + */ +int +backup_savefile (void) +{ + char buf[1024]; + char *f, *p, *end; + int ispercent = 0, n; + int in, out; + + f = myconfig->savefilefmt; + + if (*f == '\0') + return 1; + if (me == NULL) + graceful_exit (147); + + p = buf; + end = buf + sizeof(buf) - 10; /* make sure we can add .bak */ + while (*f) + { + if (ispercent) + { + switch (*f) + { + case 'u': + snprintf (p, end + 1 - p, "%d", myconfig->shed_uid); + while (*p != '\0') + p++; + break; + case 'n': + snprintf (p, end + 1 - p, "%s", me->username); + while (*p != '\0') + p++; + break; + default: + *p = *f; + if (p < end) + p++; + } + ispercent = 0; + } + else + { + if (*f == '%') + ispercent = 1; + else + { + *p = *f; + if (p < end) + p++; + } + } + f++; + } + *p = '\0'; + + /*fprintf(stderr, "***\n[SAVEFILE=%s]\n***\n", buf); + sleep(3);*/ + in = open (buf, O_RDONLY); + if (in == -1) + { + if (errno == ENOENT) + return 1; /* Nothing to back up */ + else + { + fprintf (stderr, "Cannot open savefile '%s'\n", buf); + perror ("for input"); + return 0; + } + } + strcpy (p, ".bak"); + out = open (buf, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (out == -1) + { + close (in); + fprintf (stderr, "Cannot open backup savefile '%s'\n", buf); + perror ("for output"); + return 0; + } + + while ((n = read (in, buf, sizeof(buf))) > 0) + { + n = write (out, buf, n); + if (n < 0) + break; + } + close (out); + close (in); + if (n < 0) + perror ("I/O error while backing up savefile"); + return n >= 0; +} + /* TODO: Some of the messages here (sorry no nethack for you!) are nethack specific * as may be some code... don't think so though. Globalize it. */ int @@ -1561,6 +1658,9 @@ main (int argc, char** argv) endwin (); signal(SIGWINCH, SIG_DFL); + if (!backup_savefile ()) + graceful_exit (5); + /* environment */ snprintf (atrcfilename, 81, "@%s", rcfilename); diff --git a/dgamelaunch.conf b/dgamelaunch.conf index 09df739..4130f87 100644 --- a/dgamelaunch.conf +++ b/dgamelaunch.conf @@ -49,3 +49,9 @@ rc_template = "/dgl-default-rcfile" passwd = "/dgl-login" lockfile = "/dgl-lock" + +# From inside the jail, the path to the savefile. %u is replaced by the +# decimal representation of shed_uid, %n is replaced by the player's +# user name. Before starting the game, this file is copied to its name +# with ".bak" appended. Set to an empty string to disable this copying. +savefilefmt = "/var/games/nethack/save/%u%n.gz" diff --git a/dgamelaunch.h b/dgamelaunch.h index 7990531..1c6b282 100644 --- a/dgamelaunch.h +++ b/dgamelaunch.h @@ -57,6 +57,7 @@ struct dg_config uid_t shed_uid; gid_t shed_gid; unsigned long max; + char* savefilefmt; }; /* Global variables */ diff --git a/dgl-common.c b/dgl-common.c index 95338c0..185a34e 100644 --- a/dgl-common.c +++ b/dgl-common.c @@ -29,7 +29,8 @@ struct dg_config defconfig = { /* shed_group = */ "games", /* shed_uid = */ 5, /* shed_gid = */ 60, /* games:games in Debian */ - /* max = */ 64000 + /* max = */ 64000, + /* savefilefmt = */ "" /* don't do this by default */ }; char* config = NULL; @@ -237,4 +238,5 @@ create_config () if (!myconfig->spool) myconfig->spool = defconfig.spool; if (!myconfig->passwd) myconfig->passwd = defconfig.passwd; if (!myconfig->lockfile) myconfig->lockfile = defconfig.lockfile; + if (!myconfig->savefilefmt) myconfig->savefilefmt = defconfig.savefilefmt; }