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