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