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