chiark / gitweb /
*** empty log message ***
[sympathy.git] / apps / sympathy.c
1 /*
2  * sympathy.c:
3  *
4  * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] =
10   "$Id$";
11
12 /*
13  * $Log$
14  * Revision 1.26  2008/02/29 14:55:09  james
15  * *** empty log message ***
16  *
17  * Revision 1.25  2008/02/28 22:43:25  james
18  * *** empty log message ***
19  *
20  * Revision 1.24  2008/02/28 22:00:42  james
21  * *** empty log message ***
22  *
23  * Revision 1.23  2008/02/28 16:57:51  james
24  * *** empty log message ***
25  *
26  * Revision 1.22  2008/02/28 01:47:44  james
27  * *** empty log message ***
28  *
29  * Revision 1.21  2008/02/27 16:01:24  james
30  * *** empty log message ***
31  *
32  * Revision 1.20  2008/02/27 10:00:34  james
33  * *** empty log message ***
34  *
35  * Revision 1.19  2008/02/27 09:47:05  james
36  * *** empty log message ***
37  *
38  * Revision 1.18  2008/02/27 09:42:53  james
39  * *** empty log message ***
40  *
41  * Revision 1.17  2008/02/27 09:42:21  james
42  * *** empty log message ***
43  *
44  * Revision 1.16  2008/02/27 01:31:38  james
45  * *** empty log message ***
46  *
47  * Revision 1.15  2008/02/27 01:31:14  james
48  * *** empty log message ***
49  *
50  * Revision 1.14  2008/02/24 00:43:55  james
51  * *** empty log message ***
52  *
53  * Revision 1.13  2008/02/24 00:42:53  james
54  * *** empty log message ***
55  *
56  * Revision 1.12  2008/02/23 11:48:52  james
57  * *** empty log message ***
58  *
59  * Revision 1.11  2008/02/20 20:16:07  james
60  * *** empty log message ***
61  *
62  * Revision 1.10  2008/02/20 19:44:37  james
63  * @@
64  *
65  * Revision 1.9  2008/02/20 18:49:11  staffcvs
66  * *** empty log message ***
67  *
68  * Revision 1.8  2008/02/20 18:33:37  james
69  * *** empty log message ***
70  *
71  * Revision 1.7  2008/02/20 18:31:44  james
72  * *** empty log message ***
73  *
74  * Revision 1.6  2008/02/20 17:18:33  james
75  * *** empty log message ***
76  *
77  * Revision 1.5  2008/02/20 15:50:14  james
78  * *** empty log message ***
79  *
80  * Revision 1.4  2008/02/20 02:11:35  james
81  * *** empty log message ***
82  *
83  * Revision 1.3  2008/02/14 02:46:44  james
84  * *** empty log message ***
85  *
86  * Revision 1.2  2008/02/14 00:57:58  james
87  * *** empty log message ***
88  *
89  * Revision 1.1  2008/02/05 14:25:49  james
90  * *** empty log message ***
91  *
92  */
93
94 #include <sys/types.h>
95 #include <stdarg.h>
96 #include <sympathy.h>
97 #include <stdlib.h>
98 #include <sys/utsname.h>
99 #include <sys/stat.h>
100 #include <dirent.h>
101 #include <string.h>
102 #include <strings.h>
103 #include <malloc.h>
104 #include <fcntl.h>
105
106 #include "mainloop.h"
107
108 extern void usage (void);
109
110 static char hostname[1024];
111
112 int
113 safe_atoi (char *a)
114 {
115   char *end;
116   int ret;
117
118   if (!a)
119     return -1;
120
121   ret = (int) strtol (a, &end, 0);
122
123   if (end == a)
124     return -1;
125
126   return ret;
127 }
128
129 char *
130 fatal_moan (char *fmt, ...)
131 {
132   va_list ap;
133
134
135   va_start (ap, fmt);
136   vfprintf (stderr, fmt, ap);
137   va_end (ap);
138
139   putc ('\n', stderr);
140   exit (1);
141 }
142
143
144
145 /*make the path in fmt from home (hence the name) */
146
147 char *
148 mome (char *fmt, ...)
149 {
150
151   int n;
152   int homelen;
153   char *buf, *home;
154   va_list ap;
155   int size;
156
157
158   home = getenv ("HOME");
159   if (!home)
160     return NULL;
161
162   homelen = strlen (home) + 1;
163
164   size = 1024 + homelen;
165
166   buf = malloc (size);
167
168   if (!buf)
169     fatal_moan ("malloc failed");
170
171   strcpy (buf, home);
172   strcat (buf, "/");
173
174   while (1)
175     {
176
177
178       va_start (ap, fmt);
179       n = vsnprintf (buf + homelen, size - homelen, fmt, ap);
180       va_end (ap);
181
182       if (n > -1 && n < (size - homelen))
183         return buf;
184
185       if (n > -1)               /* glibc 2.1 */
186         size = homelen + n + 1;
187       else                      /* glibc 2.0 */
188         size *= 2;              /* twice the old size */
189
190       buf = realloc (buf, size);
191
192       if (!buf)
193         fatal_moan ("malloc failed");
194     }
195 }
196
197
198 int
199 list_sockets (void)
200 {
201   struct dirent *ent;
202   struct stat buf;
203   char *sn = NULL;
204   Socket *s;
205   char *sockdir = mome ("/.sympathy/");
206   DIR *dir = opendir (sockdir);
207
208
209
210   int hostname_len = strlen (hostname);
211
212   if (!dir)
213     fatal_moan ("can't examine %s for sockets", sockdir);
214
215   rewinddir (dir);
216
217
218   while ((ent = readdir (dir)))
219     {
220       sn = mome (".sympathy/%s", ent->d_name);
221       if (stat (sn, &buf) || (!S_ISSOCK (buf.st_mode)))
222         {
223           free (sn);
224           continue;
225         }
226
227       s = socket_connect (sn);
228
229       if (s)
230         {
231           printf ("\t%s         (Active)\n", ent->d_name);
232           socket_free (s);
233         }
234       else
235         {
236           if (strncmp (ent->d_name, hostname, hostname_len))
237             {
238               printf ("\t%s     (Unknown - not this host)\n", ent->d_name);
239             }
240           else
241             {
242               printf ("\t%s     (Dead, wiped)\n", ent->d_name);
243               unlink (sn);
244             }
245         }
246
247       free (sn);
248     }
249
250   closedir (dir);
251
252   return 0;
253 }
254
255
256 void
257 get_hostname (void)
258 {
259   struct utsname name;
260
261   if (uname (&name))
262     {
263       strcpy (hostname, "unknown.");
264       return;
265     }
266
267
268   strcpy (hostname, name.nodename);
269   strcat (hostname, ".");
270 }
271
272
273 int
274 main (int argc, char *argv[])
275 {
276   int c;
277   extern char *optarg;
278   extern int optind, opterr, optopt;
279   CRT_Pos size = { VT102_COLS_80, VT102_ROWS_24 };
280
281   int csnok_pipe[2] = { 0 };
282   int csnok = 0;
283
284   int oflags[128];
285   char *oargs[128];
286
287   Socket *server_socket = NULL, *client_socket = NULL;
288   ANSI *ansi = NULL;
289   TTY *tty = NULL;
290   Log *log = NULL;
291
292   int history = 200;
293   int pid;
294
295   get_hostname ();
296
297   memset (oflags, 0, sizeof (oflags));
298   memset (oargs, 0, sizeof (oargs));
299   while ((c = getopt (argc, argv, "vw:utscr:lKHd:pb:fL:Fk:n:")) != EOF)
300     {
301       switch (c)
302         {
303         case ':':
304         case 'h':
305         case '?':
306           usage ();
307         default:
308           if ((c > 64) && (c < 91))
309             {
310               oflags[c]++;
311               oargs[c] = optarg;
312             }
313           else if ((c > 96) && (c < 123))
314             {
315               oflags[c]++;
316               oargs[c] = optarg;
317             }
318           else
319             {
320               fprintf (stderr, "unknown option %c\n", c);
321               usage ();
322             }
323         }
324
325     }
326
327   /*Compatability for screen's ls */
328   if (oflags['l'])
329     oflags['s'] = 0;
330
331
332   {
333     int sum = 0;
334     sum += oflags['t'];
335     sum += (oflags['s'] || oflags['c']) ? 1 : 0;
336     sum += oflags['r'];
337     sum += oflags['l'];
338     sum += oflags['v'];
339
340     if (!sum)
341       {
342         /*If no mode is specified behave like screen */
343         oflags['s']++;
344         oflags['c']++;
345         sum++;
346       }
347
348     if (sum != 1)
349       fatal_moan
350         ("specifiy exactly one of ( -c and or -s ), -t, -r, -l and -v");
351   }
352
353   if (oflags['v'])
354     {
355       fprintf (stderr, "Version: %s\n", libsympathy_version ());
356       fprintf (stderr, "Version: %s\n", rcsid);
357       return 0;
358     }
359
360   if (oflags['l'])
361     return list_sockets ();
362
363
364   if (oflags['r'] && oflags['k'])
365     fatal_moan ("-k is incompatible with -r");
366
367
368   if (oflags['n'])
369     {
370       history = safe_atoi (oargs['n']);
371       if (history < 0)
372         fatal_moan ("cannot parse -n %s as an integer", oargs['n']);
373
374       if (!history)
375         fatal_moan ("agrument to -n must be greater than zero");
376     }
377
378   /*Fold -r into -c */
379   if (oflags['r'])
380     {
381       char *id = oargs['r'];
382       if (id < 0)
383         fatal_moan ("cannot parse -r %s as an integer", oargs['r']);
384
385       oflags['k']++;
386       if (safe_atoi (id) > 0)
387         {
388           oargs['k'] = mome ("/.sympathy/%s%d", hostname, safe_atoi (id));
389         }
390       else
391         {
392           oargs['k'] = mome ("/.sympathy/%s", id);
393         }
394       oflags['r'] = 0;
395       oflags['c']++;
396     }
397
398   if (oflags['p'] && oflags['d'])
399     fatal_moan ("-p incompatible with -d");
400
401
402   if (oflags['c'] && oflags['s'] && oflags['F'])
403     fatal_moan ("-F is incompatible with -c -s");
404
405   /*implement server and client: this process forks. The parent */
406   /*becomes the client and the child forks again to become the */
407   /*server. If there's no -k argument the client (parent) needs */
408   /*to find out the pid of the server, we use a pipe */
409
410   if (oflags['s'] && oflags['c'])
411     {
412       if (!oflags['k'])
413         {
414           csnok++;
415           pipe (csnok_pipe);
416         }
417
418       switch (pid = fork ())
419         {
420         case 0:                /*child becomes the server */
421           oflags['c'] = 0;
422           oflags['H'] = 0;
423
424           if (csnok)
425             close (csnok_pipe[0]);
426           break;
427         case -1:
428           fatal_moan ("fork failed");
429         default:              /*parent becomes client*/
430           oflags['s'] = 0;
431           oflags['K'] = 0;
432           oflags['d'] = 0;
433           oflags['p'] = 0;
434           oflags['b'] = 0;
435           oflags['f'] = 0;
436           oflags['L'] = 0;
437           oflags['n'] = 0;
438           oflags['w'] = 0;
439
440           /*Collect the child*/
441           waitpid (pid, NULL, 0);
442
443           /*if there was no k argument we need to find the*/
444           /*pid of the server process so that we can work out*/
445           /*what the socket is called. The server tells us on*/
446           /*a pipe*/
447
448           if (csnok)
449             {
450               close (csnok_pipe[1]);
451
452               if (read (csnok_pipe[0], &pid, sizeof (pid)) != sizeof (pid))
453                 fatal_moan ("Failed to receive pid of server process");
454
455               close (csnok_pipe[0]);
456
457               oargs['k'] = mome ("/.sympathy/%s%d", hostname, pid);
458               oflags['k']++;
459             }
460         }
461     }
462
463
464   if (oflags['c'] && !oflags['k'])
465     fatal_moan ("-c requires a socket to be specified with -s or -k");
466
467   if ((oflags['p'] || oflags['d'] || oflags['K'] || oflags['b'] || oflags['f']
468        || oflags['L']) && oflags['c'])
469     fatal_moan ("-c or -r are incompatible with -p, -d, -K, -b, -f or -L");
470
471   if (oflags['t'] || oflags['s'])
472     {
473       if (!oflags['p'] && !oflags['d'])
474         oflags['p']++;
475     }
476
477   if (oflags['w'])
478     {
479       char buf[128], *ptr;
480       strcpy (buf, oargs['w']);
481       ptr = index (buf, 'x');
482       if (ptr)
483         {
484           *ptr = 0;
485           ptr++;
486           size.y = safe_atoi (ptr);
487         }
488       size.x = safe_atoi (buf);
489
490       if ((size.x > VT102_MAX_COLS) || (size.x < 1))
491         fatal_moan ("-w requires a width between 1 and %d\n", VT102_MAX_COLS);
492
493       if ((size.y > VT102_MAX_ROWS) || (size.y < 1))
494         fatal_moan ("-w requires a height between 1 and %d\n",
495                     VT102_MAX_ROWS);
496
497     }
498
499   if (oflags['s'] && !oflags['F'])
500     {
501       /*nochdir incase socket is relative path, unlink then will fail */
502       daemon (1, 0);
503
504     }
505
506
507   if (oflags['s'] )
508     {
509       char *path;
510       path = mome ("/.sympathy");
511       mkdir (path, 0700);
512       free (path);
513
514
515       if (!oflags['k']) {
516       pid = getpid ();
517         
518       oargs['k'] = mome ("/.sympathy/%s%d", hostname, pid);
519       oflags['k']++;
520         }
521
522       server_socket = socket_listen (oargs['k']);
523
524       /*Tell our parent's parent what our pid is */
525       if (csnok)
526         {
527           pid = getpid ();
528
529           write (csnok_pipe[1], &pid, sizeof (pid));
530           close (csnok_pipe[1]);
531         }
532
533       if (!server_socket)
534         fatal_moan ("failed to create socket %s for listening", oargs['k']);
535
536     }
537
538   if (oflags['s'] || oflags['t'])
539     {
540
541       if (oflags['L'])
542         {
543           log = file_log_new (oargs['L']);
544           if (!log)
545             fatal_moan ("unable to access log file %s", oargs['L']);
546         }
547
548       if (oflags['p'])
549         {
550           tty = ptty_open (NULL, NULL, &size);
551           if (!tty)
552             fatal_moan ("unable to open a ptty");
553         }
554       else
555         {
556           /*HACK-- check that console=device does not occur in */
557           /*/proc/cmdline */
558           if (!oargs['d'])
559             fatal_moan ("no argument to -d");
560
561           {
562             char kernel_cmdline[4096] = { 0 };
563             char search_string[1024] = "console=";
564             char *ptr = oargs['d'];
565             int fd;
566
567             if (!strncmp ("/dev/", ptr, 5))
568               ptr += 5;
569
570             strcat (search_string, ptr);
571
572             fd = open ("/proc/cmdline", O_RDONLY);
573             read (fd, kernel_cmdline, sizeof (kernel_cmdline));
574             close (fd);
575
576             kernel_cmdline[sizeof (kernel_cmdline) - 1] = 0;
577
578             if (strstr (kernel_cmdline, search_string))
579               fatal_moan ("/proc/cmdline contains %s", search_string);
580           }
581
582           tty =
583             serial_open (oargs['d'],
584                          oflags['K'] ? SERIAL_LOCK_ACTIVE :
585                          SERIAL_LOCK_PASSIVE);
586           if (!tty)
587             fatal_moan ("unable to open serial port %s", oargs['d']);
588         }
589
590       if (oflags['b'])
591         {
592           int baud = safe_atoi (oargs['b']);
593
594           if (baud < 0)
595             fatal_moan ("Unable to parse baudrate %s", oargs['b']);
596
597           tty_set_baud (tty, baud);
598
599         }
600
601       tty_set_flow (tty, oflags['f'] ? 1 : 0);
602
603     }
604
605   if (oflags['c'])
606     {
607
608       client_socket = socket_connect (oargs['k']);
609       if (!client_socket)
610         fatal_moan ("failed to connect to socket %s", oargs['k']);
611
612
613     }
614
615
616   if (oflags['c'] || oflags['t'])
617     {
618
619       if (oflags['H'])
620         {
621           ansi = ansi_new_html (stdout);
622         }
623       else
624         {
625           terminal_register_handlers ();
626           ansi =
627             ansi_new_from_terminal (terminal_open (0, 1),
628                                     oflags['u'] ? 0 : 1);
629           ansi->reset (ansi, NULL);
630         }
631     }
632
633   mainloop (tty, server_socket, client_socket, ansi, log, history, &size);
634
635   if (ansi)
636     {
637       ansi->close (ansi);
638       terminal_atexit ();
639     }
640
641   if (tty)
642     tty->close (tty);
643
644   if (log)
645     log->close (log);
646   if (server_socket)
647     socket_free (server_socket);
648   if (client_socket)
649     socket_free (client_socket);
650
651   if (!oflags['H'])
652     printf ("you have now exited sympathy\n");
653   return 0;
654 }