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