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