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