dgamelaunch/ttyrec.c

409 lines
8.5 KiB
C

/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
* - added Native Language Support
*/
/* 2000-12-27 Satoru Takabayashi <satoru@namazu.org>
* - modify `script' to create `ttyrec'.
*/
/*
* script
*/
#include "dgamelaunch.h"
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
#include <time.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#ifndef NOSTREAMS
# include <stropts.h>
#endif
#include <stdlib.h>
#include "ttyrec.h"
#include "io.h"
#ifndef XCASE
# define XCASE 0
#endif
int slave;
pid_t dgl_parent;
pid_t child, subchild;
pid_t input_child;
char* ipfile = NULL;
volatile int wait_for_menu = 0;
FILE *fscript;
int master;
struct termios tt;
struct winsize win;
void
ttyrec_id(int game, char *username, char *ttyrec_filename)
{
int i;
time_t tstamp;
Header h;
char *buf = (char *)malloc(1024);
char tmpbuf[256];
if (!buf) return;
tstamp = time(NULL);
#define dCLRSCR "\033[2J"
#define dCRLF "\r\n"
snprintf(buf, 1024,
dCLRSCR "\033[1;1H" dCRLF
"Player: %s" dCRLF
"Game: %s" dCRLF
"Server: %s" dCRLF
"Filename: %s" dCRLF
"Time: (%lu) %s" dCRLF
dCLRSCR,
username,
myconfig[game]->game_name,
globalconfig.server_id,
ttyrec_filename,
tstamp, ctime(&tstamp)
);
#undef dCLRSCR
#undef dCRLF
h.len = strlen(buf);
gettimeofday (&h.tv, NULL);
(void) write_header(fscript, &h);
(void) fwrite(buf, 1, h.len, fscript);
free(buf);
}
int
ttyrec_main (int game, char *username, char *ttyrec_path, char* ttyrec_filename)
{
char dirname[100];
/* Note our PID to let children kill the main dgl process for idling */
dgl_parent = getpid();
child = subchild = input_child = 0;
if (!ttyrec_path) {
child = fork();
if (child < 0) {
perror ("fork");
fail ();
}
if (child == 0) {
execvp (myconfig[game]->game_path, myconfig[game]->bin_args);
} else {
int status;
(void) wait(&status);
}
return 0;
}
if (ttyrec_path[strlen(ttyrec_path)-1] == '/')
snprintf (dirname, 100, "%s%s", ttyrec_path, ttyrec_filename);
else
snprintf (dirname, 100, "%s/%s", ttyrec_path, ttyrec_filename);
atexit(&remove_ipfile);
if ((fscript = fopen (dirname, "w")) == NULL)
{
perror (dirname);
fail ();
}
setbuf (fscript, NULL);
fixtty ();
(void) signal (SIGCHLD, finish);
child = fork ();
if (child < 0)
{
perror ("fork");
fail ();
}
if (child == 0)
{
subchild = child = fork ();
if (child < 0)
{
perror ("fork");
fail ();
}
if (child)
{
close (slave);
ipfile = gen_inprogress_lock (game, child, ttyrec_filename);
ttyrec_id(game, username, ttyrec_filename);
dooutput (myconfig[game]->max_idle_time);
}
else
doshell (game, username);
}
(void) fclose (fscript);
wait_for_menu = 1;
input_child = fork();
if (input_child < 0)
{
perror ("fork2");
fail ();
}
if (!input_child)
doinput ();
else
{
while (wait_for_menu)
sleep(1);
}
unlink (ipfile);
child = 0;
return 0;
}
void
doinput ()
{
register int cc;
char ibuf[BUFSIZ];
while ((cc = read (0, ibuf, BUFSIZ)) > 0)
(void) write (master, ibuf, cc);
done ();
}
void
finish (int sig)
{
int status;
register int pid;
register int die = 0;
(void)sig; /* unused */
while ((pid = wait3 (&status, WNOHANG, 0)) > 0)
{
if (pid == child)
die = 1;
}
if (die)
{
if (wait_for_menu && input_child)
{
// Need to kill the child that's writing input to pty.
kill(input_child, SIGTERM);
while ((pid = wait3(&status, WNOHANG, 0)) > 0);
wait_for_menu = 0;
}
else
done ();
}
}
void
game_idle_kill(int signal)
{
kill(child, SIGHUP);
kill(dgl_parent, SIGHUP);
}
void
dooutput (int max_idle_time)
{
int cc;
time_t tvec, time ();
char obuf[BUFSIZ], *ctime ();
setbuf (stdout, NULL);
(void) close (0);
tvec = time ((time_t *) NULL);
/* Set up SIGALRM handler to kill idle games */
signal(SIGALRM, game_idle_kill);
for (;;)
{
Header h;
cc = read (master, obuf, BUFSIZ);
if (cc <= 0)
break;
if (max_idle_time)
alarm(max_idle_time);
h.len = cc;
gettimeofday (&h.tv, NULL);
(void) write (1, obuf, cc);
(void) write_header (fscript, &h);
(void) fwrite (obuf, 1, cc, fscript);
}
done ();
}
void
doshell (int game, char *username)
{
getslave ();
(void) close (master);
(void) fclose (fscript);
(void) dup2 (slave, 0);
(void) dup2 (slave, 1);
(void) dup2 (slave, 2);
(void) close (slave);
/*
if (myconfig[game]->mkdir)
(void) mkdir(myconfig[game]->mkdir, 0755);
if (myconfig[game]->chdir)
(void) chdir(myconfig[game]->chdir);
*/
execvp (myconfig[game]->game_path, myconfig[game]->bin_args);
fail ();
}
void
fixtty ()
{
struct termios rtt;
rtt = tt;
rtt.c_iflag = 0;
rtt.c_lflag &= ~(ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK | ECHONL);
rtt.c_oflag = OPOST;
rtt.c_cc[VINTR] = _POSIX_VDISABLE;
rtt.c_cc[VQUIT] = _POSIX_VDISABLE;
rtt.c_cc[VERASE] = _POSIX_VDISABLE;
rtt.c_cc[VKILL] = _POSIX_VDISABLE;
rtt.c_cc[VEOF] = 1;
rtt.c_cc[VEOL] = 0;
rtt.c_cc[VMIN] = 1;
rtt.c_cc[VTIME] = 0;
(void) tcsetattr (0, TCSAFLUSH, &rtt);
}
void
fail ()
{
(void) kill (0, SIGTERM);
done ();
}
void
done ()
{
time_t tvec, time ();
char *ctime ();
if (subchild)
{
tvec = time ((time_t *) NULL);
(void) fclose (fscript);
(void) close (master);
}
else
{
(void) tcsetattr (0, TCSAFLUSH, &tt);
}
unlink(ipfile);
graceful_exit (0);
}
void
getslave ()
{
(void) setsid ();
/* grantpt( master);
unlockpt(master);
if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) {
perror((const char *)ptsname(master));
fail();
perror("open(fd, O_RDWR)");
fail();
} */
#ifndef NOSTREAMS
if (isastream (slave))
{
if (ioctl (slave, I_PUSH, "ptem") < 0)
{
perror ("ioctl(fd, I_PUSH, ptem)");
fail ();
}
if (ioctl (slave, I_PUSH, "ldterm") < 0)
{
perror ("ioctl(fd, I_PUSH, ldterm)");
fail ();
}
#ifndef _HPUX_SOURCE
if (ioctl (slave, I_PUSH, "ttcompat") < 0)
{
perror ("ioctl(fd, I_PUSH, ttcompat)");
fail ();
}
#endif
}
#endif /* !NOSTREAMS */
(void) ioctl (0, TIOCGWINSZ, (char *) &win);
}
void
remove_ipfile (void)
{
if (ipfile != NULL)
unlink (ipfile);
}