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