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