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