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