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