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