chiark / gitweb /
Remove build products
[sympathy.git] / apps / mainloop.c
1 /* 
2  * mainloop.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: mainloop.c,v 1.33 2008/05/09 12:35:57 james Exp $";
11
12 /* 
13  * $Log: mainloop.c,v $
14  * Revision 1.33  2008/05/09 12:35:57  james
15  * *** empty log message ***
16  *
17  * Revision 1.32  2008/03/11 17:56:50  james
18  * *** empty log message ***
19  *
20  * Revision 1.31  2008/03/11 17:56:04  james
21  * *** empty log message ***
22  *
23  * Revision 1.30  2008/03/11 17:47:24  james
24  * *** empty log message ***
25  *
26  * Revision 1.29  2008/03/10 11:49:32  james
27  * *** empty log message ***
28  *
29  * Revision 1.28  2008/03/07 13:56:39  james
30  * *** empty log message ***
31  *
32  * Revision 1.27  2008/03/07 13:16:02  james
33  * *** empty log message ***
34  *
35  * Revision 1.26  2008/03/06 21:34:09  james
36  * *** empty log message ***
37  *
38  * Revision 1.25  2008/03/06 21:33:02  james
39  * *** empty log message ***
40  *
41  * Revision 1.24  2008/03/06 16:49:39  james
42  * *** empty log message ***
43  *
44  * Revision 1.23  2008/03/06 16:49:05  james
45  * *** empty log message ***
46  *
47  * Revision 1.22  2008/03/03 06:04:42  james
48  * *** empty log message ***
49  *
50  * Revision 1.21  2008/03/02 10:27:24  james
51  * *** empty log message ***
52  *
53  * Revision 1.20  2008/02/28 16:57:51  james
54  * *** empty log message ***
55  *
56  * Revision 1.19  2008/02/28 16:37:16  james
57  * *** empty log message ***
58  *
59  * Revision 1.18  2008/02/28 11:27:48  james
60  * *** empty log message ***
61  *
62  * Revision 1.17  2008/02/27 09:42:53  james
63  * *** empty log message ***
64  *
65  * Revision 1.16  2008/02/27 09:42:21  james
66  * *** empty log message ***
67  *
68  * Revision 1.15  2008/02/27 01:31:38  james
69  * *** empty log message ***
70  *
71  * Revision 1.14  2008/02/27 01:31:14  james
72  * *** empty log message ***
73  *
74  * Revision 1.13  2008/02/26 23:56:12  james
75  * *** empty log message ***
76  *
77  * Revision 1.12  2008/02/26 23:23:16  james
78  * *** empty log message ***
79  *
80  * Revision 1.11  2008/02/24 00:43:55  james
81  * *** empty log message ***
82  *
83  * Revision 1.10  2008/02/23 13:05:57  staffcvs
84  * *** empty log message ***
85  *
86  * Revision 1.9  2008/02/23 11:48:51  james
87  * *** empty log message ***
88  *
89  * Revision 1.8  2008/02/22 23:39:30  james
90  * *** empty log message ***
91  *
92  * Revision 1.7  2008/02/20 20:16:07  james
93  * *** empty log message ***
94  *
95  * Revision 1.6  2008/02/20 19:44:37  james
96  * @@
97  *
98  * Revision 1.5  2008/02/20 18:31:44  james
99  * *** empty log message ***
100  *
101  * Revision 1.4  2008/02/20 17:18:33  james
102  * *** empty log message ***
103  *
104  * Revision 1.3  2008/02/20 02:11:35  james
105  * *** empty log message ***
106  *
107  * Revision 1.2  2008/02/16 10:58:52  james
108  * *** empty log message ***
109  *
110  * Revision 1.13  2008/02/15 23:52:12  james
111  * *** empty log message ***
112  *
113  * Revision 1.12  2008/02/15 03:32:07  james
114  * *** empty log message ***
115  *
116  * Revision 1.11  2008/02/14 16:21:17  james
117  * *** empty log message ***
118  *
119  * Revision 1.10  2008/02/14 10:39:14  james
120  * *** empty log message ***
121  *
122  * Revision 1.9  2008/02/14 10:34:47  james
123  * *** empty log message ***
124  *
125  * Revision 1.8  2008/02/14 10:34:30  james
126  * *** empty log message ***
127  *
128  * Revision 1.7  2008/02/14 02:46:44  james
129  * *** empty log message ***
130  *
131  * Revision 1.6  2008/02/14 00:57:58  james
132  * *** empty log message ***
133  *
134  * Revision 1.5  2008/02/13 18:05:06  james
135  * *** empty log message ***
136  *
137  * Revision 1.4  2008/02/13 17:21:55  james
138  * *** empty log message ***
139  *
140  * Revision 1.3  2008/02/08 15:06:52  james
141  * *** empty log message ***
142  *
143  * Revision 1.2  2008/02/07 15:42:49  james
144  * *** empty log message ***
145  *
146  * Revision 1.1  2008/02/05 14:25:49  james
147  * *** empty log message ***
148  *
149  */
150
151 #include <sys/time.h>
152 #include <sympathy.h>
153 #include "mainloop.h"
154
155 #include "clients.h"
156
157
158 typedef struct
159 {
160   int nclients;
161   int lines;
162   int baud;
163   int crtscts;
164   int cd_edge_sec;
165   int blocked;
166   int bootstrap;
167 } Status;
168
169 static Status
170 get_status (TTY * t, Clients * cs)
171 {
172   static struct timeval last_cd_edge = { 0 };
173   static int last_cd_state = -1;
174   int cd;
175   struct timeval now, dif;
176
177   TTY_Status tty_status = { 0 };
178   Status status;
179
180   tty_get_status (t, &tty_status);
181
182   status.bootstrap = 1;
183
184   if (cs)
185     status.nclients = cs->n;
186   else
187     status.nclients = 0;
188
189   status.lines = tty_status.lines;
190   status.baud = tty_status.baud;
191   status.crtscts = (tty_status.termios.c_cflag & CRTSCTS) ? 1 : 0;
192   status.blocked = tty_status.blocked;
193
194   cd = (tty_status.lines & TIOCM_CD) ? 1 : 0;
195
196   if (cd != last_cd_state)
197     {
198       gettimeofday (&last_cd_edge, NULL);
199       last_cd_state = cd;
200     }
201
202   gettimeofday (&now, NULL);
203   timersub (&now, &last_cd_edge, &dif);
204   status.cd_edge_sec = dif.tv_sec;
205
206   return status;
207 }
208
209 static char *
210 line_to_name (int l)
211 {
212
213   switch (l)
214     {
215 #ifdef TIOCM_LE
216     case TIOCM_LE:
217       return "LE";
218 #endif
219 #ifdef TIOCM_DTR
220     case TIOCM_DTR:
221       return "DTR";
222 #endif
223 #ifdef TIOCM_RTS
224     case TIOCM_RTS:
225       return "RTS";
226 #endif
227 #ifdef TIOCM_ST
228     case TIOCM_ST:
229       return "ST";
230 #endif
231 #ifdef TIOCM_SR
232     case TIOCM_SR:
233       return "SR";
234 #endif
235 #ifdef TIOCM_CTS
236     case TIOCM_CTS:
237       return "CTS";
238 #endif
239 #ifdef TIOCM_CD
240     case TIOCM_CD:
241       return "CD";
242 #endif
243 #ifdef TIOCM_RI
244     case TIOCM_RI:
245       return "RI";
246 #endif
247 #ifdef TIOCM_DSR
248     case TIOCM_DSR:
249       return "DSR";
250 #endif
251     }
252   return "??";
253 }
254
255 static void
256 log_line_changes (Context * ctx, int old, int new)
257 {
258   int dif = old ^ new;
259   int c = 1;
260   char buf[1024], *ptr = buf;
261   char *n;
262
263   if (!dif)
264     return;
265   if (!ctx->l)
266     return;
267
268   n = "<Modem lines changed:";
269
270   while (*n)
271     *(ptr++) = *(n++);
272
273   while (dif >= c)
274     {
275
276       if (dif & c)
277         {
278           *(ptr++) = ' ';
279           *(ptr++) = (new & c) ? '+' : '-';
280           n = line_to_name (c);
281           while (*n)
282             *(ptr++) = *(n++);
283         }
284
285       c <<= 1;
286     }
287   *(ptr++) = '>';
288   *ptr = 0;
289
290
291   ctx->l->log (ctx->l, buf);
292
293 }
294
295 static char *
296 do_line (char *ptr, int lines, int line)
297 {
298   char *lname;
299
300   if (!(lines & line))
301     return ptr;
302   lname = line_to_name (line);
303
304   *(ptr++) = ' ';
305   while (*lname)
306     *(ptr++) = *(lname++);
307
308   return ptr;
309 }
310
311
312
313 static void
314 check_status (Context * c, Clients * cs)
315 {
316   static Status old_status = { 0 };
317   Status status;
318   char buf[1024];
319   char *ptr = buf;
320   char *t;
321
322   status = get_status (c->t, cs);
323   if (!memcmp (&status, &old_status, sizeof (status)))
324     return;
325
326   log_line_changes (c, old_status.lines, status.lines);
327
328   old_status = status;
329
330   ptr += sprintf (ptr, "CTRL-B ");
331
332   t = c->t->name;
333   if (!strncmp (t, "/dev/", 5))
334     t += 5;
335   while (*t)
336     *(ptr++) = *(t++);
337
338   ptr += sprintf (ptr, " %db", status.baud);
339
340   ptr = do_line (ptr, status.lines, TIOCM_RTS);
341   ptr = do_line (ptr, status.lines, TIOCM_CTS);
342   ptr = do_line (ptr, status.lines, TIOCM_DTR);
343   ptr = do_line (ptr, status.lines, TIOCM_DSR);
344   ptr = do_line (ptr, status.lines, TIOCM_RI);
345   ptr = do_line (ptr, status.lines, TIOCM_CD);
346
347   if (status.blocked)
348     {
349       t = ", Locked";
350       while (*t)
351         *(ptr++) = *(t++);
352     }
353
354   if (status.crtscts)
355     {
356       t = ", Flow";
357       while (*t)
358         *(ptr++) = *(t++);
359     }
360 #if 0
361   if (status.lines & TIOCM_CD)
362     {
363       ptr +=
364         sprintf (ptr, ", On %d.%d", status.cd_edge_sec / 60,
365                  status.cd_edge_sec % 60);
366     }
367   else
368     {
369       ptr +=
370         sprintf (ptr, ", Off %d.%d", status.cd_edge_sec / 60,
371                  status.cd_edge_sec % 60);
372     }
373 #endif
374
375   ptr +=
376     sprintf (ptr, ", %d client%s", status.nclients,
377              (status.nclients == 1) ? "" : "s");
378
379   if (c->tp->biterrs)
380     {
381
382       ptr +=
383         sprintf (ptr, ", %d err%s", c->tp->biterrs,
384                  (c->tp->biterrs == 1) ? "" : "s");
385
386       if (c->tp->guessed_baud == -1)
387         {
388           ptr += sprintf (ptr, " try higher");
389         }
390       else if (c->tp->guessed_baud > 0)
391         {
392           ptr += sprintf (ptr, " try %db", c->tp->guessed_baud);
393         }
394     }
395
396   *ptr = 0;
397
398 #if 0
399   log_f (c->l, "%s:%d %s", __FILE__, __LINE__, buf);
400 #endif
401
402   if (cs)
403     send_status (cs, buf);
404   else
405     cmd_new_status (c->d, c, buf);
406
407 }
408
409
410 static int
411 msg_from_server (ANSI * a, IPC_Msg * m, Context * c)
412 {
413   int err = 0;
414   switch (m->hdr.type)
415     {
416
417     case IPC_MSG_TYPE_NOOP:
418       break;
419     case IPC_MSG_TYPE_DEBUG:
420       // fprintf (stderr,"%p [%d] %s\n", m, m->hdr.size , m->debug.msg );
421       break;
422     case IPC_MSG_TYPE_HISTORY:
423       history_add (c->h, m->history.history.line);
424       break;
425     case IPC_MSG_TYPE_VT102:
426       if (sizeof (VT102) != m->vt102.len)
427         crash_out ("sizeof(VT102) differs in client and server");
428
429       *(c->v) = m->vt102.vt102;
430
431       if (a->one_shot)
432         {
433           a->one_shot (a, &c->v->crt);
434           err++;                /* Simulate a fatal write error enclosing tty 
435                                  */
436         }
437       break;
438     case IPC_MSG_TYPE_TERM:
439       err += tty_parse (c, m->term.term, m->term.len);
440       break;
441     case IPC_MSG_TYPE_STATUS:
442       cmd_new_status (c->d, c, m->status.status);
443       break;
444     default:
445       fprintf (stderr, "Unhandeled message type %d\n", m->hdr.type);
446     }
447   return err;
448 }
449
450
451 void
452 mainloop (Context * c, ANSI * ansi, Socket * server_socket,
453           Socket * client_socket)
454 {
455   fd_set rfds, wfds;
456   Clients *clients;
457
458   c->tp = tty_parser_new ();
459   c->u = utf8_new ();
460
461   /* are we being fed by a tty or a socket */
462   if (client_socket)
463     {
464       if (server_socket)
465         crash_out ("mainloop cannot both be a server and a client");
466       c->k = keydis_ipc_new (client_socket);
467     }
468   else
469     {
470       if (!c->t)
471         crash_out ("mainloop must have either a client_socket or a terminal");
472       c->k = keydis_vt102_new ();
473     }
474
475   /* do we have an upstream terminal to talk to */
476   /* if so start a command parser */
477   if (ansi)
478     {
479       c->d = cmd_new ();
480     }
481   else
482     {
483       c->d = NULL;
484     }
485
486
487   vt102_reset (c);
488
489
490   if (server_socket)
491     {
492       if (client_socket)
493         crash_out ("mainloop cannot both be a server and a client");
494       clients = clients_new ();
495     }
496   else
497     {
498       clients = NULL;
499     }
500
501   for (;;)
502     {
503       struct timeval tv = { 0, 250000 };
504
505       if ((c->d) && (c->d->disconnect))
506         break;
507
508       /* update the status lines, locally or remotely */
509       if (c->t)
510         check_status (c, clients);
511
512       FD_ZERO (&rfds);
513       FD_ZERO (&wfds);
514
515       if (c->t)
516         tty_pre_select (c->t, &rfds, &wfds);
517
518       if (server_socket)
519         {
520           FD_SET (server_socket->fd, &rfds);
521           clients_pre_select (clients, &rfds, &wfds);
522         }
523
524       if (client_socket)
525         socket_pre_select (client_socket, &rfds, &wfds);
526
527       if (ansi && ansi->terminal)
528         tty_pre_select (ansi->terminal, &rfds, &wfds);
529
530       select (FD_SETSIZE, &rfds, &wfds, NULL, &tv);
531
532       /* any message from clients, or new connexions */
533       if (server_socket)
534         {
535           Socket *new_client_socket;
536           if (FD_ISSET (server_socket->fd, &rfds)
537               && ((new_client_socket = socket_accept (server_socket))))
538             {
539               {
540                 Client *new_client;
541                 /* New client connexion */
542                 new_client =
543                   clients_new_client (clients, new_client_socket, c);
544
545
546               }
547             }
548
549           clients_post_select (clients, c, &rfds, &wfds);
550         }
551
552       /* any data from the port */
553       if (c->t && FD_ISSET (c->t->rfd, &rfds))
554         {
555           char buf[IPC_MAX_BUF];
556           int red;
557
558           red = c->t->recv (c->t, buf, sizeof (buf));
559
560           if (red < 0)
561             break;
562
563           if (red)
564             {
565               if (clients)
566                 send_output (clients, buf, red);
567               if (tty_parse (c, buf, red))
568                 break;
569             }
570         }
571
572
573
574       /* any data from the server */
575       if (client_socket)
576         {
577           int err = 0;
578
579           if (socket_post_select (client_socket, &rfds, &wfds))
580             break;
581
582           while (client_socket->msg && !err)
583             {
584               err += msg_from_server (ansi, client_socket->msg, c);
585               socket_consume_msg (client_socket);
586             }
587
588           if (err)
589             break;
590         }
591
592
593       /* update our local screen */
594       if (ansi)
595         {
596           if (ansi->dispatch)
597             if (ansi->dispatch (ansi, c))
598               break;
599
600           if (ansi->update)
601             if (ansi->update (ansi, c))
602               break;
603         }
604     }
605
606   if (clients)
607     clients_shutdown (clients, c);
608   log_f (c->l, "<sympathy is now terminating>");
609 }