medit/moo/mooterm/mootermhelper.c

342 lines
9.6 KiB
C

/*
* mooterm/mootermhelper.c
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef __CYGWIN__
#include <windows.h>
#endif /* __CYGWIN__ */
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <poll.h>
#include <stdlib.h>
#include "pty.h"
#define MOOTERM_COMPILATION
#include "helper.h"
#define WRITE_LOG 0
#define WRITE_LOGFILE 1
#define LOGFILE "helper.log"
#if WRITE_LOG
#if WRITE_LOGFILE
static int firsttime = 1;
#define writelog(...) \
{\
FILE *logfile;\
if (firsttime) {\
logfile = fopen (LOGFILE, "w+");\
firsttime = 0;\
}\
else\
logfile = fopen (LOGFILE, "a+");\
if (!logfile) {\
printf ("could not open logfile");\
_exit (1);\
}\
fprintf (logfile, __VA_ARGS__);\
fclose (logfile);\
}
#else /* ! WRITE_LOGFILE */
#define writelog(...) \
{\
fprintf (stderr, __VA_ARGS__);\
}
#endif /* ! WRITE_LOGFILE */
#else /* ! WRITE_LOG */
#define writelog(...)
#endif /* WRITE_LOG */
#define BUFSIZE 1024
int master = -1;
#ifdef HAVE_PID_T
pid_t child_pid = -1;
#else /* ! HAVE_PID_T */
int child_pid = -1;
#endif /* ! HAVE_PID_T */
void doexit (int status)
{
writelog ("helper: exiting\r\n");
write (master, "\4", 1);
_vte_pty_close (master);
close (master);
sleep (1);
if (child_pid != -1)
kill (child_pid, SIGTERM);
exit (status);
}
static void sigchld_handler (int sig)
{
writelog ("helper: child died\r\n");
doexit (0);
}
static void sigint_handler (int sig)
{
writelog ("helper: got SIGINT\r\n");
writelog ("helper: child pid %d\r\n", child_pid);
if (master != -1)
_vte_pty_n_write (master, "\3", 1);
}
static void sigany_handler (int num)
{
writelog ("helper: got signal %d\r\n", num);
doexit (0);
}
int main (int argc, char **argv)
{
int i;
int status, flags;
unsigned width, height;
char *working_dir;
const char *env[] = {"TERM=dumb", NULL};
if (argc < 4 && (argc != 2 || strcmp (argv[1], "--test"))) {
printf ("usage: %s <term_width> <term_height> <binary> [args]\n", argv[0]);
exit (1);
}
writelog ("helper: hi there\r\n");
for (i = 3; i < 10000; ++i)
close (i);
signal(SIGINT, sigint_handler);
signal(SIGCHLD, sigchld_handler);
signal(SIGHUP, sigany_handler);
signal(SIGABRT, sigany_handler);
signal(SIGPIPE, sigany_handler);
signal(SIGTERM, sigany_handler);
width = strtol (argv[1], NULL, 10);
height = strtol (argv[2], NULL, 10);
working_dir = getenv (MOO_TERM_HELPER_ENV);
if (working_dir && strlen (working_dir)) {
writelog ("helper: chdir to '%s'\r\n", working_dir);
chdir (working_dir);
}
else {
writelog ("helper: no %s environment variable\r\n", MOO_TERM_HELPER_ENV);
}
master = _vte_pty_open(&child_pid, (char**)env,
argv[3],
argv + 3,
NULL,
width, height,
0, 0, 0);
writelog ("helper: forked child pid %d\r\n", child_pid);
for (i = 3; argv[i] != NULL; ++i)
writelog ("%s ", argv[i]);
writelog ("\r\n");
if (master == -1) {
writelog ("helper: could not open pty\r\n");
doexit (1);
}
if (waitpid (child_pid, &status, WNOHANG) == -1) {
writelog ("error in waitpid\r\n");
}
if ((flags = fcntl (master, F_GETFL)) < 0) {
writelog ("F_GETFL on master\r\n");
}
else if (-1 == fcntl (master, F_SETFL, O_NONBLOCK | flags)) {
writelog ("F_SETFL on master\r\n");
}
if ((flags = fcntl (0, F_GETFL)) < 0) {
writelog ("F_GETFL on stdin\r\n");
}
else if (-1 == fcntl (0, F_SETFL, O_NONBLOCK | flags)) {
writelog ("F_SETFL on stdin\r\n");
}
while (1) {
int res = 0;
int again = 1;
char buf[BUFSIZE];
int num_read;
struct pollfd fds[] = {
{master, POLLIN | POLLPRI | POLLHUP | POLLERR, 0},
{0, POLLIN | POLLPRI | POLLHUP | POLLERR, 0}
};
while (again) {
res = poll (fds, 2, -1);
if (res != -1 && res != 1 && res != 2) {
writelog ("helper: poll returned bogus value %d\r\n", res);
doexit (1);
}
if (res == -1) {
switch (errno) {
case EAGAIN:
writelog ("helper: poll error: EAGAIN\r\n");
break;
case EINTR:
writelog ("helper: poll error: EINTR\r\n");
break;
default:
writelog ("helper: poll error\r\n");
writelog ("helper: %s\r\n", strerror (errno));
doexit (1);
}
}
else
again = 0;
}
if (fds[0].revents & POLLHUP) {
writelog ("helper: got POLLHUP from child\r\n");
doexit (0);
}
if (fds[1].revents & POLLHUP) {
writelog ("helper: got POLLHUP from parent\r\n");
doexit (0);
}
if ((fds[0].revents & POLLERR) &&
(!errno || (errno != EAGAIN && errno != EINTR)))
{
writelog ("helper: got POLLERR from child\r\n");
doexit (0);
}
if ((fds[1].revents & POLLERR) &&
(!errno || (errno != EAGAIN && errno != EINTR)))
{
writelog ("helper: got POLLERR from parent\r\n");
doexit (0);
}
/********** reading child **********/
if (fds[0].revents & (POLLIN | POLLPRI)) {
num_read = read (master, buf, BUFSIZE);
if (num_read < 1 && errno != EAGAIN && errno != EINTR) {
writelog ("helper: error reading from child\r\n");
doexit (1);
}
if (num_read > 0 && _vte_pty_n_write (1, buf, num_read) != num_read) {
writelog ("helper: error writing to parent\r\n");
doexit (1);
}
}
/********** reading parent **********/
if (fds[1].revents & (POLLIN | POLLPRI)) {
int count = 0;
num_read = read (0, buf, BUFSIZE);
if (num_read < 1 && errno != EAGAIN && errno != EINTR) {
writelog ("helper: error reading from parent\r\n");
doexit (1);
}
while (count < num_read) {
int i;
char cmd;
for (i = count; i < num_read && buf[i] != HELPER_CMD_CHAR; ++i) ;
if (i > count &&
_vte_pty_n_write (master, buf + count, i - count) != i - count) {
writelog ("helper: error writing to child\r\n");
doexit (1);
}
count = i;
if (count == num_read) break;
writelog ("helper: got command\r\n");
++count;
if (count == num_read) {
if (_vte_pty_n_read (0, &cmd, 1) != 1) {
writelog ("helper: error reading command\r\n");
doexit (1);
}
}
else
cmd = buf[count++];
switch (cmd) {
case HELPER_OK:
writelog ("helper: HELPER_OK\r\n");
break;
case HELPER_GOODBYE:
writelog ("helper: HELPER_GOODBYE\r\n");
doexit (0);
break;
case HELPER_SET_SIZE:
writelog ("helper: HELPER_SET_SIZE\r\n");
{
char size[4];
int width, height, j;
for (i = 0; count + i < num_read; ++i)
size[i] = buf[count + i];
count += i;
if (i < 4)
for (j = i; j < 4; ++j)
if (_vte_pty_n_read (0, size + j, 1) != 1) {
writelog ("helper: could not read size\r\n");
doexit (1);
}
width = CHARS_TO_UINT (size[0], size[1]);
height = CHARS_TO_UINT (size[2], size[3]);
writelog ("helper: SET_SIZE %d, %d\r\n", width, height);
if (_vte_pty_set_size (master, width, height) == -1) {
writelog ("helper: could not set size\r\n");
}
else {
writelog ("helper: success\r\n", width, height);
}
}
break;
default:
writelog ("helper: unknown command\r\n");
break;
}
}
}
}
_vte_pty_close (master);
close (master);
master = -1;
writelog ("helper: exit\r\n");
doexit (0);
}