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