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