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