[
.B \-p
]
+[
+.B \-a
+]
+[
+.B \-e
+]
.ad
.hy 14
.SH DESCRIPTION
.PP
.I dgamelaunch
is a network-based game shell where anyone can sign up for an
-account and start playing any game which suits your fancy - currently, though,
-it only supports NetHack.
+account and start playing any game which suits your fancy - currently
+it supports NetHack and Slash'Em.
The user is presented with a curses-based menu to watch other games, edit the
options or play the game.
Don't print errors pertaining to the server configuration.
.TP
.B
+-a
+Enter user authentication mode.
+.I
+dgamelaunch
+will read user name and password from stdin, check them, check whether the
+user doesn't have a game in progress already, then write the inprogress lock.
+Returns 0 if everything was successful.
+.TP
+.B
+-e
+Start game server to handle incoming NhExt connection.
+.I
+dgamelaunch
+will execute /bin/nethack --proxy after chroot and shedding privs.
+.TP
+.B
-p
Ignored; solely for compatibility with
.B
.PP
Later, Joshua Kwan, Brett Carrington and Jilles Tjoelker added many new
features.
+.PP
+NhExt support was written by J. Ali Harlow.
.SH FILES
.DT
.ta \w'dgldir/ttyrec/<login>/*.ttyrec\ \ \ 'u
.\".SH ENVIRONMENT
.SH "SEE ALSO"
.PP
-nethack(6), telnetd(8), login(1), curses(3), fcntl(2)
+nethack(6), slashem(6), telnetd(8), login(1), curses(3), fcntl(2)
.SH BUGS
.PP
None known.
/* ************************************************************* */
+char*
+gen_nhext_filename ()
+{
+ time_t rawtime;
+ struct tm *ptm;
+ char *nhext_filename = calloc(100, sizeof(char));
+
+ /* append time to filename */
+ time (&rawtime);
+ ptm = gmtime (&rawtime);
+ snprintf (nhext_filename, 100, "%04i-%02i-%02i.%02i:%02i:%02i.nhext",
+ ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
+ ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+ return nhext_filename;
+}
+
+/* ************************************************************* */
+
char*
gen_inprogress_lock (pid_t pid, char* ttyrec_filename)
{
int i, menuchoice, len = 20, offset = 0, doresizewin = 0;
time_t ctime;
struct dg_game **games;
- char ttyrecname[130], *replacestr = NULL;
+ char ttyrecname[130], *replacestr = NULL, gametype[10];
+ int is_nhext[14];
sigset_t oldmask, toblock;
games = populate_games (&len);
if (i + offset >= len)
break;
- mvprintw (7 + i, 1, "%c) %-15s (%3dx%3d) %s %s (%ldm %lds idle)",
- i + 97, games[i + offset]->name,
- games[i + offset]->ws_col, games[i + offset]->ws_row,
+ is_nhext[i] = !strcmp (games[i + offset]->ttyrec_fn + strlen (games[i + offset]->ttyrec_fn) - 6, ".nhext");
+
+ if (is_nhext[i])
+ strcpy (gametype, " NhExt");
+ else
+ snprintf (gametype, sizeof gametype, "%3dx%3d",
+ games[i + offset]->ws_col, games[i + offset]->ws_row);
+
+ mvprintw (7 + i, 1, "%c) %-15s (%s) %s %s (%ldm %lds idle)",
+ i + 97, games[i + offset]->name, gametype,
games[i + offset]->date, games[i + offset]->time,
(time (&ctime) - games[i + offset]->idle_time) / 60,
(time (&ctime) - games[i + offset]->idle_time) % 60);
}
if ((menuchoice - 'a') >= 0 && (menuchoice - 'a') < i)
{
+ if (is_nhext[menuchoice - 97]) /* Cannot watch NhExt game */
+ break;
+
/* valid choice has been made */
snprintf (ttyrecname, 130, "%sttyrec/%s", myconfig->dglroot,
games[menuchoice - 97 + offset]->ttyrec_fn);
}
}
+int
+authenticate ()
+{
+ int i, len, me_index;
+ char user_buf[22], pw_buf[22];
+ struct dg_game **games;
+
+ /* We use simple password authentication, rather than challenge/response. */
+ printf ("\n");
+ fflush(stdout);
+
+ fgets (user_buf, sizeof(user_buf), stdin);
+ len = strlen (user_buf);
+ if (user_buf[len - 1] == '\n')
+ user_buf[--len] = '\0';
+ else
+ {
+ fprintf (stderr, "Username too long (max 20 chars).\n");
+ return 1;
+ }
+
+ fgets (pw_buf, sizeof(pw_buf), stdin);
+ len = strlen (pw_buf);
+ if (pw_buf[len - 1] == '\n')
+ pw_buf[--len] = '\0';
+ else
+ {
+ fprintf (stderr, "Password too long (max 20 chars).\n");
+ return 1;
+ }
+
+ if ((me_index = userexist (user_buf)) != -1)
+ {
+ me = users[me_index];
+ if (passwordgood (pw_buf))
+ {
+ games = populate_games (&len);
+ for (i = 0; i < len; i++)
+ if (!strcmp (games[i]->name, user_buf))
+ {
+ fprintf (stderr, "Game already in progress.\n");
+ return 1;
+ }
+ win.ws_row = win.ws_col = 0;
+ gen_inprogress_lock (getppid (), gen_nhext_filename ());
+ return 0;
+ }
+ }
+
+ sleep (2);
+ fprintf (stderr, "Login failed.\n");
+ return 1;
+}
+
int
main (int argc, char** argv)
{
char atrcfilename[81], *spool;
unsigned int len;
int c;
+ int nhext = 0, nhauth = 0;
- while ((c = getopt(argc, argv, "qh:pp:f:")) != -1)
+ while ((c = getopt(argc, argv, "qh:pp:f:ae")) != -1)
{
switch (c)
{
case 'q':
silent = 1; break;
+ case 'a':
+ nhauth = 1; break;
+
+ case 'e':
+ nhext = 1; break;
+
case 'f':
if (config)
{
}
/* get master tty just before chroot (lives in /dev) */
- ttyrec_getpty ();
+ if (!nhext && !nhauth)
+ ttyrec_getpty ();
- /* chroot */
- if (chroot (myconfig->chroot))
+ if (geteuid () != myconfig->shed_uid)
{
- perror ("cannot change root directory");
- graceful_exit (1);
- }
+ /* chroot */
+ if (chroot (myconfig->chroot))
+ {
+ perror ("cannot change root directory");
+ graceful_exit (1);
+ }
- if (chdir ("/"))
- {
- perror ("cannot chdir to root directory");
- graceful_exit (1);
- }
+ if (chdir ("/"))
+ {
+ perror ("cannot chdir to root directory");
+ graceful_exit (1);
+ }
- /* shed privs. this is done immediately after chroot. */
- if (setgroups (1, &myconfig->shed_gid) == -1)
- {
- perror ("setgroups");
- graceful_exit (1);
- }
+ /* shed privs. this is done immediately after chroot. */
+ if (setgroups (1, &myconfig->shed_gid) == -1)
+ {
+ perror ("setgroups");
+ graceful_exit (1);
+ }
- if (setgid (myconfig->shed_gid) == -1)
- {
- perror ("setgid");
- graceful_exit (1);
+ if (setgid (myconfig->shed_gid) == -1)
+ {
+ perror ("setgid");
+ graceful_exit (1);
+ }
+
+ if (setuid (myconfig->shed_uid) == -1)
+ {
+ perror ("setuid");
+ graceful_exit (1);
+ }
}
- if (setuid (myconfig->shed_uid) == -1)
+ if (nhext)
{
- perror ("setuid");
+ char *myargv[3];
+
+ myargv[0] = myconfig->game_path;
+ myargv[1] = "--proxy";
+ myargv[2] = 0;
+
+ execvp (myconfig->game_path, myargv);
+ perror (myconfig->game_path);
graceful_exit (1);
}
if (readfile (0))
graceful_exit (110);
+ if (nhauth)
+ graceful_exit (authenticate ());
+
initcurses ();
menuloop();
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
+#include <signal.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
struct dg_game **
populate_games (int *l)
{
- int fd, len, n;
+ int fd, len, n, is_nhext, pid;
DIR *pdir;
struct dirent *pdirent;
struct stat pstat;
if (!strcmp (pdirent->d_name, ".") || !strcmp (pdirent->d_name, ".."))
continue;
+ is_nhext = !strcmp (pdirent->d_name + strlen (pdirent->d_name) - 6, ".nhext");
+
snprintf (fullname, 130, "%sinprogress/%s", myconfig->dglroot, pdirent->d_name);
fd = 0;
/* O_RDWR here should be O_RDONLY, but we need to test for
* an exclusive lock */
fd = open (fullname, O_RDWR);
- if ((fd > 0) && fcntl (fd, F_SETLK, &fl) == -1)
+ if (fd >= 0 && (is_nhext || fcntl (fd, F_SETLK, &fl) == -1))
{
/* stat to check idle status */
- snprintf (ttyrecname, 130, "%sttyrec/%s", myconfig->dglroot, pdirent->d_name);
- replacestr = strchr (ttyrecname, ':');
- if (!replacestr)
- graceful_exit (145);
- replacestr[0] = '/';
- if (!stat (ttyrecname, &pstat))
+ if (!is_nhext)
+ {
+ snprintf (ttyrecname, 130, "%sttyrec/%s", myconfig->dglroot, pdirent->d_name);
+ replacestr = strchr (ttyrecname, ':');
+ if (!replacestr)
+ graceful_exit (145);
+ replacestr[0] = '/';
+ }
+ if (is_nhext || !stat (ttyrecname, &pstat))
{
/* now it's a valid game for sure */
games = realloc (games, sizeof (struct dg_game) * (len + 1));
}
else
p = "";
+ pid = atoi(p);
while (*p != '\0' && *p != '\n')
p++;
if (*p != '\0')
if (*p != '\0')
p++;
games[len]->ws_col = atoi(p);
- if (games[len]->ws_row < 4 || games[len]->ws_col < 4)
+ if (is_nhext)
{
- games[len]->ws_row = 24;
- games[len]->ws_col = 80;
- }
-
- len++;
+ if (kill (pid, 0) != 0)
+ {
+ /* Dead game */
+ free (games[len]->ttyrec_fn);
+ free (games[len]->name);
+ free (games[len]->date);
+ free (games[len]->time);
+ free (games[len]);
+ unlink (fullname);
+ }
+ else
+ len++;
+ }
+ else
+ {
+ if (games[len]->ws_row < 4 || games[len]->ws_col < 4)
+ {
+ games[len]->ws_row = 24;
+ games[len]->ws_col = 80;
+ }
+ len++;
+ }
}
}
else