/* * sympathy.c: * * Copyright (c) 2008 James McKenzie , * All rights reserved. * */ static char rcsid[] = "$Id$"; /* * $Log$ * Revision 1.37 2008/03/07 13:16:02 james * *** empty log message *** * * Revision 1.36 2008/03/06 21:34:09 james * *** empty log message *** * * Revision 1.35 2008/03/06 21:33:02 james * *** empty log message *** * * Revision 1.34 2008/03/06 16:49:39 james * *** empty log message *** * * Revision 1.33 2008/03/06 16:49:05 james * *** empty log message *** * * Revision 1.32 2008/03/03 06:04:42 james * *** empty log message *** * * Revision 1.31 2008/03/03 06:04:18 james * *** empty log message *** * * Revision 1.30 2008/03/02 10:38:18 james * *** empty log message *** * * Revision 1.29 2008/03/02 10:37:56 james * *** empty log message *** * * Revision 1.28 2008/03/02 10:27:24 james * *** empty log message *** * * Revision 1.27 2008/03/02 09:55:37 james * *** empty log message *** * * Revision 1.26 2008/02/29 14:55:09 james * *** empty log message *** * * Revision 1.25 2008/02/28 22:43:25 james * *** empty log message *** * * Revision 1.24 2008/02/28 22:00:42 james * *** empty log message *** * * Revision 1.23 2008/02/28 16:57:51 james * *** empty log message *** * * Revision 1.22 2008/02/28 01:47:44 james * *** empty log message *** * * Revision 1.21 2008/02/27 16:01:24 james * *** empty log message *** * * Revision 1.20 2008/02/27 10:00:34 james * *** empty log message *** * * Revision 1.19 2008/02/27 09:47:05 james * *** empty log message *** * * Revision 1.18 2008/02/27 09:42:53 james * *** empty log message *** * * Revision 1.17 2008/02/27 09:42:21 james * *** empty log message *** * * Revision 1.16 2008/02/27 01:31:38 james * *** empty log message *** * * Revision 1.15 2008/02/27 01:31:14 james * *** empty log message *** * * Revision 1.14 2008/02/24 00:43:55 james * *** empty log message *** * * Revision 1.13 2008/02/24 00:42:53 james * *** empty log message *** * * Revision 1.12 2008/02/23 11:48:52 james * *** empty log message *** * * Revision 1.11 2008/02/20 20:16:07 james * *** empty log message *** * * Revision 1.10 2008/02/20 19:44:37 james * @@ * * Revision 1.9 2008/02/20 18:49:11 staffcvs * *** empty log message *** * * Revision 1.8 2008/02/20 18:33:37 james * *** empty log message *** * * Revision 1.7 2008/02/20 18:31:44 james * *** empty log message *** * * Revision 1.6 2008/02/20 17:18:33 james * *** empty log message *** * * Revision 1.5 2008/02/20 15:50:14 james * *** empty log message *** * * Revision 1.4 2008/02/20 02:11:35 james * *** empty log message *** * * Revision 1.3 2008/02/14 02:46:44 james * *** empty log message *** * * Revision 1.2 2008/02/14 00:57:58 james * *** empty log message *** * * Revision 1.1 2008/02/05 14:25:49 james * *** empty log message *** * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mainloop.h" extern void usage (void); static char hostname[1024]; char *socket_dirs[] = { "~/.sympathy", "~/sympathy", "/etc/sympathy", "/var/sympathy", NULL }; int safe_atoi (char *a) { char *end; int ret; if (!a) return -1; ret = (int) strtol (a, &end, 0); if (end == a) return -1; return ret; } char * fatal_moan (char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); putc ('\n', stderr); exit (1); } static void sigchld (int dummy) { int status; wait3 (&status, WNOHANG, NULL); } /* Dispell zombies, from for example log compression */ void garlic (void) { struct sigaction sa; sa.sa_handler = sigchld; sa.sa_flags = SA_RESTART; sigaction (SIGCHLD, &sa, NULL); } char * teedious_snprintf (char *fmt, va_list ap) { va_list aq; int size = 1024; char *buf = malloc (size); int n; if (!buf) fatal_moan ("malloc failed"); while (1) { va_copy (aq, ap); n = vsnprintf (buf, size, fmt, aq); va_end (aq); if (n > -1 && n < (size)) return buf; if (n > -1) /* glibc 2.1 */ size = n + 1; else /* glibc 2.0 */ size *= 2; /* twice the old size */ buf = realloc (buf, size); if (!buf) fatal_moan ("malloc failed"); } } char * gloo_paths (char *dir, char *leaf) { int i; char *ret, *ptr; if (!dir) dir = ""; if (!leaf) leaf = ""; ret = ptr = xmalloc (strlen (dir) + strlen (leaf) + 2); while (*dir) *(ptr++) = *(dir++); *(ptr++) = '/'; while (*leaf) *(ptr++) = *(leaf++); *ptr = 0; return ret; } /* make the path in fmt from home (hence the name) */ char * mome (char *fmt, ...) { char *ret, *home, *leaf; va_list ap; home = getenv ("HOME"); if (!home) return NULL; if (fmt) { va_start (ap, fmt); leaf = teedious_snprintf (fmt, ap); va_end (ap); } else { leaf = NULL; } ret = gloo_paths (home, leaf); if (leaf) free (leaf); return ret; } Socket * find_socket (char **retpath, char *fmt, ...) { Socket *ret; char *path, *leaf, *h, **ptr; va_list ap; if (fmt) { va_start (ap, fmt); leaf = teedious_snprintf (fmt, ap); va_end (ap); } else { leaf = NULL; } for (ptr = socket_dirs; *ptr; ptr++) { if (**ptr == '~') { h = mome (*ptr + 1); } else { h = *ptr; } if (!h) continue; path = gloo_paths (h, leaf); if (**ptr == '~') free (h); ret = socket_connect (path); if (ret) { if (retpath) { *retpath = path; } else { free (path); } free (leaf); return ret; } free (path); } free (leaf); return NULL; } int list_sockets_in_dir (char *sockdir) { struct dirent *ent; struct stat buf; char *sn = NULL; Socket *s; DIR *dir = opendir (sockdir); int hostname_len = strlen (hostname); if (!dir) return; rewinddir (dir); while ((ent = readdir (dir))) { sn = gloo_paths (sockdir, ent->d_name); if (stat (sn, &buf) || (!S_ISSOCK (buf.st_mode))) { free (sn); continue; } s = socket_connect (sn); if (s) { printf ("\t%s (Active)\n", sn); socket_free (s); } else { if (strncmp (ent->d_name, hostname, hostname_len)) { printf ("\t%s (Unknown - not this host)\n", sn); } else { printf ("\t%s (Dead, wiped)\n", sn); unlink (sn); } } free (sn); } closedir (dir); return 0; } int list_sockets (void) { char **ptr, *h; for (ptr = socket_dirs; *ptr; ptr++) { if (**ptr == '~') { h = mome (*ptr + 1); } else { h = *ptr; } if (!h) continue; list_sockets_in_dir (h); if (**ptr == '~') free (h); } return 0; } void get_hostname (void) { struct utsname name; if (uname (&name)) { strcpy (hostname, "unknown."); return; } strcpy (hostname, name.nodename); strcat (hostname, "."); } int main (int argc, char *argv[]) { Context ctx_store = { 0 }, *ctx = &ctx_store; int c; extern char *optarg; extern int optind, opterr, optopt; CRT_Pos size = { VT102_COLS_80, VT102_ROWS_24 }; ANSI *ansi = NULL; int csnok_pipe[2] = { 0 }; int csnok = 0; int oflags[128]; char *oargs[128]; Socket *server_socket = NULL, *client_socket = NULL; int history = 200; int pid; char *pid_file = NULL; get_hostname (); memset (oflags, 0, sizeof (oflags)); memset (oargs, 0, sizeof (oargs)); while ((c = getopt (argc, argv, "I:NRP:vw:utscr:lKHd:pb:fL:Fk:n:")) != EOF) { switch (c) { case ':': case 'h': case '?': usage (); default: if ((c > 64) && (c < 91)) { oflags[c]++; oargs[c] = optarg; } else if ((c > 96) && (c < 123)) { oflags[c]++; oargs[c] = optarg; } else { fprintf (stderr, "unknown option %c\n", c); usage (); } } } /* Compatability for screen's ls */ if (oflags['l']) oflags['s'] = 0; { int sum = 0; sum += oflags['t']; sum += (oflags['s'] || oflags['c']) ? 1 : 0; sum += oflags['r']; sum += oflags['l']; sum += oflags['v']; if (!sum) { /* If no mode is specified behave like screen */ oflags['s']++; oflags['c']++; sum++; } if (sum != 1) fatal_moan ("specifiy exactly one of ( -c and or -s ), -t, -r, -l and -v"); } if (oflags['v']) { fprintf (stderr, "Version: %s\n", libsympathy_version ()); fprintf (stderr, "Version: %s\n", rcsid); return 0; } if (oflags['l']) return list_sockets (); if (oflags['r'] && oflags['k']) fatal_moan ("-k is incompatible with -r"); if (oflags['n']) { history = safe_atoi (oargs['n']); if (history < 0) fatal_moan ("cannot parse -n %s as an integer", oargs['n']); if (!history) fatal_moan ("agrument to -n must be greater than zero"); } /* Fold -r implies -c */ if (oflags['r']) oflags['c']++; if (oflags['p'] && oflags['d']) fatal_moan ("-p incompatible with -d"); if (oflags['c'] && oflags['s'] && oflags['F']) fatal_moan ("-F is incompatible with -c -s"); if (oflags['H'] && oflags['N']) fatal_moan ("-H is incompatible with -N"); /* implement server and client: this process forks. The parent */ /* becomes the client and the child forks again to become the */ /* server. If there's no -k argument the client (parent) needs */ /* to find out the pid of the server, we use a pipe */ if (oflags['s'] && oflags['c']) { if (!oflags['k']) { csnok++; pipe (csnok_pipe); } switch (pid = fork ()) { case 0: /* child becomes the server */ oflags['c'] = 0; oflags['H'] = 0; oflags['N'] = 0; oflags['I'] = 0; if (csnok) close (csnok_pipe[0]); break; case -1: fatal_moan ("fork failed"); default: /* parent becomes client */ oflags['s'] = 0; oflags['K'] = 0; oflags['d'] = 0; oflags['p'] = 0; oflags['b'] = 0; oflags['f'] = 0; oflags['L'] = 0; oflags['R'] = 0; oflags['P'] = 0; oflags['n'] = 0; oflags['w'] = 0; /* Collect the child */ waitpid (pid, NULL, 0); /* if there was no k argument we need to find the */ /* pid of the server process so that we can work out */ /* what the socket is called. The server tells us on */ /* a pipe */ if (csnok) { close (csnok_pipe[1]); if (read (csnok_pipe[0], &pid, sizeof (pid)) != sizeof (pid)) fatal_moan ("Failed to receive pid of server process"); close (csnok_pipe[0]); oargs['k'] = mome ("/.sympathy/%s%d", hostname, pid); oflags['k']++; } } } if (oflags['c'] && !oflags['k'] && !oflags['r']) fatal_moan ("-c requires a socket to be specified with -s or -k or -r"); if ((oflags['H'] || oflags['N'] || oflags['I']) && oflags['s']) fatal_moan ("-s is incompatible with -H, -N and -I"); if ((oflags['p'] || oflags['d'] || oflags['K'] || oflags['b'] || oflags['f'] || oflags['L'] || oflags['R'] || oflags['P']) && oflags['c']) fatal_moan ("-c or -r are incompatible with -p, -d, -K, -b, -f, -R, -P or -L"); if (oflags['t'] || oflags['s']) { if (!oflags['p'] && !oflags['d']) oflags['p']++; } if (oflags['w']) { char buf[128], *ptr; strcpy (buf, oargs['w']); ptr = index (buf, 'x'); if (ptr) { *ptr = 0; ptr++; size.y = safe_atoi (ptr); } size.x = safe_atoi (buf); if ((size.x > VT102_MAX_COLS) || (size.x < 1)) fatal_moan ("-w requires a width between 1 and %d\n", VT102_MAX_COLS); if ((size.y > VT102_MAX_ROWS) || (size.y < 1)) fatal_moan ("-w requires a height between 1 and %d\n", VT102_MAX_ROWS); } if (oflags['s'] && !oflags['F']) { /* nochdir incase socket is relative path, unlink then will fail */ daemon (1, 0); } if (oflags['s']) { char *path; path = mome ("/.sympathy"); mkdir (path, 0700); free (path); if (!oflags['k']) { pid = getpid (); oargs['k'] = mome ("/.sympathy/%s%d", hostname, pid); oflags['k']++; } server_socket = socket_listen (oargs['k']); /* Tell our parent's parent what our pid is */ if (csnok) { pid = getpid (); write (csnok_pipe[1], &pid, sizeof (pid)); close (csnok_pipe[1]); } if (!server_socket) fatal_moan ("failed to create socket %s for listening", oargs['k']); } if (oflags['s'] || oflags['t']) { if (oflags['P']) { FILE *fp; pid_file = oargs['P']; fp = fopen (pid_file, "w"); if (fp) { fprintf (fp, "%d", getpid ()); fclose (fp); } } if (oflags['L']) { ctx->l = file_log_new (oargs['L'], oflags['R']); if (!ctx->l) fatal_moan ("unable to access log file %s", oargs['L']); } if (oflags['p']) { ctx->t = ptty_open (NULL, NULL, &size); if (!ctx->t) fatal_moan ("unable to open a ptty"); } else { /* HACK-- check that console=device does not occur in */ /* /proc/cmdline */ if (!oargs['d']) fatal_moan ("no argument to -d"); { char kernel_cmdline[4096] = { 0 }; char search_string[1024] = "console="; char *ptr = oargs['d']; int fd; if (!strncmp ("/dev/", ptr, 5)) ptr += 5; strcat (search_string, ptr); fd = open ("/proc/cmdline", O_RDONLY); read (fd, kernel_cmdline, sizeof (kernel_cmdline)); close (fd); kernel_cmdline[sizeof (kernel_cmdline) - 1] = 0; if (strstr (kernel_cmdline, search_string)) fatal_moan ("/proc/cmdline contains %s", search_string); } ctx->t = serial_open (oargs['d'], oflags['K'] ? SERIAL_LOCK_ACTIVE : SERIAL_LOCK_PASSIVE); if (!ctx->t) fatal_moan ("unable to open serial port %s", oargs['d']); } if (oflags['b']) { int baud = safe_atoi (oargs['b']); if (baud < 0) fatal_moan ("Unable to parse baudrate %s", oargs['b']); tty_set_baud (ctx->t, baud); } tty_set_flow (ctx->t, oflags['f'] ? 1 : 0); } if (oflags['r']) { char *id = oargs['r']; if (!id) fatal_moan ("-r requires an argument"); if (safe_atoi (id) > 0) { client_socket = find_socket (&oargs['k'], "%s%d", hostname, safe_atoi (id)); } else { client_socket = find_socket (&oargs['k'], "%s", id); } if (!client_socket) fatal_moan ("failed to find socket %s", oargs['r']); } else if (oflags['c']) { client_socket = socket_connect (oargs['k']); if (!client_socket) fatal_moan ("failed to connect to socket %s", oargs['k']); } if (oflags['I']) { // FIXME ... } else { if (client_socket) ipc_msg_send_initialize(client_socket); if (oflags['c'] || oflags['t']) { if (oflags['N']) { ctx->r = rx_new_raw (0, 1); ansi = ansi_new_raw (0, 1); } else if (oflags['H']) { ansi = ansi_new_html (stdout); } else { terminal_register_handlers (); ansi = ansi_new_from_terminal (terminal_open (0, 1), oflags['u'] ? 0 : 1); ansi->reset (ansi, NULL); if (ansi->set_title) ansi->set_title (ansi, oargs['k']); } } ctx->v = vt102_new (&size); ctx->h = history_new (history); mainloop (ctx, ansi, server_socket, client_socket); } if (ansi) { ansi->close (ansi); terminal_atexit (); } if (ctx->t) ctx->t->close (ctx->t); if (ctx->l) ctx->l->close (ctx->l); if (server_socket) socket_free (server_socket); if (client_socket) socket_free (client_socket); if (pid_file) unlink (pid_file); if (!oflags['H']) printf ("you have now exited sympathy\n"); return 0; }