/* * sympathy.c: * * Copyright (c) 2008 James McKenzie , * All rights reserved. * */ static char rcsid[] = "$Id$"; /* * $Log$ * Revision 1.53 2012/06/22 10:22:24 james * *** empty log message *** * * Revision 1.52 2010/07/27 14:49:34 james * add support for byte logging * * Revision 1.51 2010/07/16 11:04:10 james * ignore tedious return values * * Revision 1.50 2008/05/09 12:56:28 staffcvs * *** empty log message *** * * Revision 1.49 2008/05/09 12:43:49 staffcvs * *** empty log message *** * * Revision 1.48 2008/05/09 12:35:57 james * *** empty log message *** * * Revision 1.47 2008/05/09 12:26:58 staffcvs * *** empty log message *** * * Revision 1.46 2008/05/09 12:19:18 james * *** empty log message *** * * Revision 1.45 2008/03/15 01:44:31 james * *** empty log message *** * * Revision 1.44 2008/03/12 01:30:23 james * *** empty log message *** * * Revision 1.43 2008/03/12 01:26:56 james * *** empty log message *** * * Revision 1.42 2008/03/11 15:02:52 james * *** empty log message *** * * Revision 1.41 2008/03/10 11:49:32 james * *** empty log message *** * * Revision 1.40 2008/03/07 14:16:44 james * *** empty log message *** * * Revision 1.39 2008/03/07 14:13:40 james * *** empty log message *** * * Revision 1.38 2008/03/07 13:56:39 james * *** empty log message *** * * 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 #include "mainloop.h" int use_syslog = 0; extern void usage (void); extern char *expand (const char *, int *); 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); if (use_syslog) { vsyslog (LOG_ERR, fmt, ap); } else { vfprintf (stderr, fmt, ap); putc ('\n', stderr); } va_end (ap); 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) { 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 0; 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, "."); } void send_to_server (Socket * c, char *s) { int n; s = expand (s, &n); if (!n) return; while (n--) { ipc_msg_send_key (c, *(uint8_t *) s); s++; } ipc_msg_send_killme (c); } 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 cs_pipe[2] = { 0 }; int cs = 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, "DBI:NCSRP:vw:utscr:lKHd:pb:fL:Fk:n:")) != EOF) { switch (c) { case ':': case 'h': case '?': usage (); case 'S': use_syslog++; openlog ("sympathy", LOG_PID | LOG_CONS, LOG_DAEMON); /*fall through */ default: if ((c >= 'A') && (c <= 'Z')) { oflags[c]++; oargs[c] = optarg; } else if ((c >= 'a') && (c <= 'z')) { oflags[c]++; oargs[c] = optarg; } else { if (use_syslog) { syslog (LOG_ERR, "unknown option %c\n", c); } 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']; sum += oflags['C']; 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, -C, -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']) { cs++; int result; result = pipe (cs_pipe); switch (pid = fork ()) { case 0: /* child becomes the server */ oflags['c'] = 0; oflags['H'] = 0; oflags['N'] = 0; oflags['I'] = 0; close (cs_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['B'] = 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. We do this even if k is specified to avoid */ /* a race, the server writes to the pipe when the socket */ /* is opened */ close (cs_pipe[1]); if (read (cs_pipe[0], &pid, sizeof (pid)) != sizeof (pid)) fatal_moan ("Failed to receive pid of server process"); close (cs_pipe[0]); if (!oflags['k']) { 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['B'] || oflags['P']) && oflags['c']) fatal_moan ("-c or -r are incompatible with -p, -d, -K, -b, -f, -R, -P, -B or -L"); if (oflags['C'] && (!oflags['d'])) fatal_moan ("-C requires -d"); 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 */ int fish; fish = daemon (1, 0); } garlic (); 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 */ /* and that we've opened the server socket */ if (cs) { pid = getpid (); int fish; fish = write (cs_pipe[1], &pid, sizeof (pid)); close (cs_pipe[1]); } if (!server_socket) fatal_moan ("failed to create socket %s for listening", oargs['k']); } if (oflags['s'] || oflags['t'] || oflags['C']) { 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']) { if (strcmp(oargs['L'],"syslog")) { ctx->l = file_log_new (oargs['L'], oflags['R']); } else { ctx->l = syslog_log_new (LOG_WARNING); } ctx->byte_logging=oflags['B']; if (!ctx->l) fatal_moan ("unable to access log file %s", oargs['L']); } if (oflags['p']) { if (optind < argc) { ctx->t = ptty_open (argv[optind], &argv[optind], &size); } else { 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"); if (!oflags['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); int fish; fish = 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['C']) { ctx->t->close (ctx->t); return 0; } } 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']) { if (!client_socket) fatal_moan ("-I requires either -c or -r", oargs['k']); if (!oargs['I']) fatal_moan ("-I requires an arugment"); send_to_server (client_socket, oargs['I']); } 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) { if (oflags['c'] && oargs['k']) { ansi->set_title (ansi, oargs['k']); } else if ((ctx->t) && (ctx->t->name)) { ansi->set_title (ansi, ctx->t->name); } } } } 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'] && !oflags['I']) printf ("you have now exited sympathy\n"); return 0; }