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