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