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