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