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