]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/main.c
52b9e7a3741bc22df86cf25feceadd3bef0738f6
[1ts-debian.git] / zephyr / zwgc / main.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It is one of the source files comprising zwgc, the Zephyr WindowGram
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id$
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15 #ifdef HAVE_ARES
16 #include <ares.h>
17 #endif
18
19 #if (!defined(lint) && !defined(SABER))
20 static const char rcsid_main_c[] = "$Id$";
21 #endif
22
23 #include <netdb.h>
24 #include <sys/socket.h>
25 #include <sys/resource.h>
26 #include <zephyr/mit-copyright.h>
27 #include <zephyr/zephyr.h>
28
29 #include "new_memory.h"
30 #include "zwgc.h"
31 #include "parser.h"
32 #include "node.h"
33 #include "exec.h"
34 #include "zephyr.h"
35 #include "notice.h"
36 #include "subscriptions.h"
37 #include "file.h"
38 #include "mux.h"
39 #include "port.h"
40 #include "variables.h"
41 #include "main.h"
42
43 extern void notice_handler();
44 static void process_notice(), setup_signals(), detach(), signal_exit();
45 #ifdef HAVE_ARES
46 static void notice_callback();
47 #endif
48
49 /*
50  * Global zwgc-wide variables:
51  */
52
53 #ifdef DEBUG
54 int zwgc_debug = 0;
55 #endif
56
57 static char *zwgc_version_string = "1.0";
58
59 /*
60  * description_filename_override - <<<>>>
61  */
62
63 static char *description_filename_override = NULL;
64
65 /*
66  * progname - <<<>>> export!
67  */
68
69 char *progname = NULL;
70
71 /*
72  * subscriptions_filename_override - <<<>>> export!
73  */
74
75 char *subscriptions_filename_override = NULL;
76
77 /*
78  * location_override - <<<>>> export!
79  */
80
81 char *location_override = NULL;
82
83 #ifdef HAVE_ARES
84 /*
85  * achannel - <<<>>> export!
86  */
87
88 ares_channel achannel;
89 #endif
90
91 /****************************************************************************/
92 /*                                                                          */
93 /*             Code to deal with reading in the description file:           */
94 /*                                                                          */
95 /****************************************************************************/
96
97 /*
98  * program - this holds a pointer to the node representation of the
99  *           description file once it has been read in.
100  */
101
102 static struct _Node *program = NULL;
103
104 /*
105  * <<<>>>
106  */
107
108 static void fake_startup_packet()
109 {
110     ZNotice_t notice;
111     struct timezone tz;
112     char msgbuf[BUFSIZ];
113
114     var_set_variable("version", zwgc_version_string);
115
116     (void) memset(&notice, 0, sizeof(notice));
117
118     notice.z_version = "";
119     notice.z_class = "WG_CTL_CLASS";
120     notice.z_class_inst = "WG_CTL_USER<<<>>>";
121     notice.z_opcode = "WG_STARTUP";
122     notice.z_default_format = "Zwgc mark II version $version now running...\n";
123     notice.z_recipient = "";
124     notice.z_sender = "ZWGC";
125     gettimeofday(&notice.z_time,&tz);
126     notice.z_port = 0;
127     notice.z_kind = ACKED;
128     notice.z_auth = ZAUTH_YES;
129     sprintf(msgbuf,"Zwgc mark II version %s now running...",
130             zwgc_version_string);
131     notice.z_message = msgbuf;
132     notice.z_message_len = strlen(notice.z_message)+1;
133     
134     process_notice(&notice, NULL);
135 }
136
137 static void read_in_description_file()
138 {
139     FILE *input_file;
140     char defdesc[128];
141
142 /*    var_clear_all_variables(); <<<>>> */
143
144     sprintf(defdesc, "%s/zephyr/%s", DATADIR, DEFDESC);
145     input_file = locate_file(description_filename_override, USRDESC, defdesc);
146     if (input_file)
147       program = parse_file(input_file);
148     else
149       program = NULL;
150     
151     fake_startup_packet();
152 }
153
154 /****************************************************************************/
155 /*                                                                          */
156 /*            Code to deal with argument parsing & overall control:         */
157 /*                                                                          */
158 /****************************************************************************/
159
160 /*
161  *    void usage()
162  *        Effects: Prints out an usage message on stderr then exits the
163  *                 program with error code 1.
164  */
165
166 void usage()
167 {
168 #ifdef DEBUG
169     fprintf(stderr, "\
170 zwgc: usage: zwgc [-debug] [-f <filename>] [-subfile <filename>]\n\
171                   [-ttymode] [-nofork] [-reenter] [-loc text]\n\
172                   [-default <driver>] {-disable <driver>}*\n\
173                   [output driver options]\n");
174 #else
175     fprintf(stderr, "\
176 zwgc: usage: zwgc [-f <filename>] [-subfile <filename>]\n\
177                   [-ttymode] [-nofork] [-reenter] [-loc text]\n\
178                   [-default <driver>] {-disable <driver>}*\n\
179                   [output driver options]\n");
180 #endif
181     exit(1);
182 }
183
184 /*
185  * <<<>>>
186  */
187
188 static void run_initprogs()
189 {
190     /*
191      * This code stolen from old zwgc: yuck.  Clean up & fix.  <<<>>>
192      * Should this fork instead of just systeming?
193      */
194
195     int status;
196     char *progname = ZGetVariable("initprogs");
197     
198     if (!progname)
199       return;
200     
201     status = system(progname);
202     if (status == 127) {
203         perror("zwgc initprog exec");
204         fprintf(stderr,"zwgc initprog of <%s> failed: no shell.\n",
205                 progname);
206     } else if (status!=-1 && status>>8) {
207         perror("zwgc initprog exec");
208         fprintf(stderr,"zwgc initprog of <%s> failed with status [%d].\n",
209                 progname, status>>8);
210     }
211 }
212
213 /*
214  * main -- the program entry point.  Does parsing & top level control.
215  */
216
217 int main(argc, argv)
218      int argc;
219      char **argv;
220 {
221     char **new;
222     register char **current;
223     int dofork = 1;
224 #ifdef HAVE_ARES
225     char *errmem;
226     int status;
227 #endif
228
229     progname = argv[0];
230
231     /*
232      * Process "-f <filename>", "-subfile <filename>", "-nofork",
233      * "-reenter" (which is ignored) and (if DEBUG) "-debug"
234      * arguments, removing then from argc, argv:
235      */
236     for (new=current=argv+1; *current; current++) {
237         if (string_Eq(*current, "-debug")) {
238             argc--;
239 #ifdef DEBUG
240             zwgc_debug = 1;
241 #endif
242         } else if (string_Eq(*current, "-f")) {
243             argc -= 2; current++;
244             if (!*current)
245               usage();
246             description_filename_override = *current;
247         } else if (string_Eq(*current, "-subfile")) {
248             argc -= 2; current++;
249             if (!*current)
250               usage();
251             subscriptions_filename_override = *current;
252         } else if (string_Eq(*current, "-nofork")) {
253             argc--;
254             dofork = 0;
255         } else if (string_Eq(*current, "-reenter")) {
256             argc--;                     /* just throw it away */
257         } else if (string_Eq(*current, "-loc")) {
258             argc -= 2; current++;
259             if (!*current)
260               usage();
261             location_override = *current;
262         } else
263           *(new)++ = *current;
264     }
265     *new = *current;
266
267 #ifdef HAVE_ARES
268     /*
269      * Initialize resolver library
270      */
271     status = ares_init(&achannel);
272     if (status != ARES_SUCCESS) {
273         fprintf(stderr, "Couldn't initialize resolver: %s\n",
274                 ares_strerror(status, &errmem));
275         ares_free_errmem(errmem);
276         return(1);
277     }
278 #endif
279
280     /*
281      * Initialize various subsystems in proper order:
282      */
283     dprintf("Initializing subsystems...\n"); /*<<<>>>*/
284     mux_init();
285     var_clear_all_variables(); /* <<<>>> */
286     init_ports();       /* <<<>>> */
287     dprintf("Initializing standard ports...\n");
288     init_standard_ports(&argc, argv);
289     if (argc>1)
290       usage();
291     dprintf("Initializing zephyr...\n");
292     setup_signals(dofork);
293     zephyr_init(notice_handler);
294
295     if (dofork)
296         detach();
297     /*
298      * Run the initprogs program(s) now that we are all set to deal:
299      */
300     dprintf("Running initprogs program...\n");
301     run_initprogs();
302
303     dprintf("Test Zwgc parser.\n\n");
304     read_in_description_file();
305
306     dprintf("Entering main loop\n");
307     mux_loop();
308
309     dprintf("Returning from main loop\n");
310     finalize_zephyr();
311
312     return(0);
313 }
314
315 /****************************************************************************/
316 /*                                                                          */
317 /*               :               */
318 /*                                                                          */
319 /****************************************************************************/
320
321 #define  USER_SUPPRESS     "SUPPRESS"
322 #define  USER_UNSUPPRESS   "UNSUPPRESS"
323
324 void notice_handler(notice)
325      ZNotice_t *notice;
326 {
327     struct hostent *fromhost = NULL;
328
329     if (notice->z_sender_addr.s_addr) {
330 #ifdef HAVE_ARES
331         ares_gethostbyaddr(achannel, &(notice->z_sender_addr),
332                            sizeof(notice->z_sender_addr), AF_INET,
333                            notice_callback, notice);
334         return;
335 #else
336         fromhost = gethostbyaddr((char *) &(notice->z_sender_addr),
337                                  sizeof(struct in_addr), AF_INET);
338 #endif
339     }
340     process_notice(notice, fromhost ? fromhost->h_name : NULL);
341     ZFreeNotice(notice);
342     free(notice);
343 }
344
345 #ifdef HAVE_ARES
346 static void notice_callback(arg, status, fromhost)
347      void *arg;
348      int status;
349      struct hostent *fromhost;
350 {
351     ZNotice_t *notice = (ZNotice_t *) arg;
352
353     process_notice(notice, fromhost ? fromhost->h_name : NULL);
354     ZFreeNotice(notice);
355     free(notice);
356 }
357 #endif
358
359 static void process_notice(notice, hostname)
360      ZNotice_t *notice;
361      char *hostname;
362 {
363     char *control_opcode;
364
365     dprintf("Got a message\n");
366
367     if (control_opcode = decode_notice(notice, hostname)) {
368 #ifdef DEBUG
369         printf("got control opcode <%s>.\n", control_opcode);
370 #endif
371         if (!strcasecmp(control_opcode, USER_REREAD)) {
372             read_in_description_file();
373         } else if (!strcasecmp(control_opcode, USER_SHUTDOWN))
374           zwgc_shutdown();
375         else if (!strcasecmp(control_opcode, USER_STARTUP)) {
376 #ifdef DEBUG_MEMORY
377             report_memory_usage(); /* <<<>>> */
378 #endif
379             zwgc_startup();
380         } else if (!strcasecmp(control_opcode, USER_SUPPRESS)) {
381             string class = get_field(notice->z_message,
382                                      notice->z_message_len, 1);
383             string instance = get_field(notice->z_message,
384                                         notice->z_message_len, 2);
385             string recipient = get_field(notice->z_message,
386                                          notice->z_message_len, 3);
387             punt(class, instance, recipient);
388             free(class);
389             free(instance);
390             free(recipient);
391         } else if (!strcasecmp(control_opcode, USER_UNSUPPRESS)) {
392             string class = get_field(notice->z_message,
393                                      notice->z_message_len, 1);
394             string instance = get_field(notice->z_message,
395                                         notice->z_message_len, 2);
396             string recipient = get_field(notice->z_message,
397                                          notice->z_message_len, 3);
398             unpunt(class, instance, recipient);
399             free(class);
400             free(instance);
401             free(recipient);
402         } else if (!strcasecmp(control_opcode, USER_EXIT)) {
403             signal_exit();
404         } else
405           printf("zwgc: unknown control opcode %s.\n", control_opcode);
406
407         return;
408     }
409
410     if (!zwgc_active) {
411 #ifdef DEBUG
412         if (zwgc_debug)
413           printf("NON-ACTIVE: PUNTED <%s>!!!!\n", notice->z_class_inst);
414 #endif
415         return;
416     }
417     
418     if (puntable_address_p(notice->z_class,
419                            notice->z_class_inst,
420                            notice->z_recipient)) {
421 #ifdef DEBUG
422         if (zwgc_debug)
423           printf("PUNTED <%s>!!!!\n", notice->z_class_inst);
424 #endif
425         return;
426     }
427
428     exec_process_packet(program, notice);
429 }
430
431 /***************************************************************************/
432
433 /*
434  *
435  */
436
437 static void signal_exit()
438 {
439     mux_end_loop_p = 1;
440 }
441
442 /* clean up ALL the waiting children, in case we get hit with
443    multiple SIGCHLD's at once, and don't process in time. */
444 static RETSIGTYPE signal_child()
445 {
446 #ifdef HAVE_WAITPID
447   int status;
448 #else
449   union wait status;
450 #endif
451   extern int errno;
452   int pid, old_errno = errno;
453
454   do {
455 #ifdef HAVE_WAITPID
456       pid = waitpid(-1, &status, WNOHANG);
457 #else
458       pid = wait3(&status, WNOHANG, (struct rusage *)0);
459 #endif
460   } while (pid != 0 && pid != -1);
461   errno = old_errno;
462 }
463
464 /* rewrite the wgfile in case it has gone away */
465 static RETSIGTYPE signal_usr1()
466 {
467     write_wgfile();
468 }
469
470 static void setup_signals(dofork)
471      int dofork;
472 {
473 #ifdef _POSIX_VERSION
474     struct sigaction sa;
475
476     sigemptyset(&sa.sa_mask);
477     sa.sa_flags = 0;
478     
479     if (dofork) {
480         sa.sa_handler = SIG_IGN;
481         sigaction(SIGINT, &sa, (struct sigaction *)0);
482         sigaction(SIGTSTP, &sa, (struct sigaction *)0);
483         sigaction(SIGQUIT, &sa, (struct sigaction *)0);
484         sigaction(SIGTTOU, &sa, (struct sigaction *)0);
485     } else {
486         /* clean up on SIGINT; exiting on logout is the user's problem, now. */
487         sa.sa_handler = signal_exit;
488         sigaction(SIGINT, &sa, (struct sigaction *)0);
489     }
490
491     /* behavior never changes */
492     sa.sa_handler = signal_exit;
493     sigaction(SIGTERM, &sa, (struct sigaction *)0);
494     sigaction(SIGHUP, &sa, (struct sigaction *)0);
495
496     sa.sa_handler = SIG_IGN;
497     sigaction(SIGPIPE, &sa, (struct sigaction *)0);
498
499     sa.sa_handler = signal_child;
500     sigaction(SIGCHLD, &sa, (struct sigaction *)0);
501
502     sa.sa_handler = signal_usr1;
503     sigaction(SIGUSR1, &sa, (struct sigaction *)0);
504
505 #else /* !POSIX */
506     if (dofork) {
507         /* Ignore keyboard signals if forking.  Bad things will happen. */
508         signal(SIGINT, SIG_IGN);
509         signal(SIGTSTP, SIG_IGN);
510         signal(SIGTTOU, SIG_IGN);
511         signal(SIGQUIT, SIG_IGN);
512     } else {
513         /* clean up on SIGINT; exiting on logout is the user's problem, now. */
514         signal(SIGINT, signal_exit);
515     }
516     
517     /* behavior never changes */
518     signal(SIGTERM, signal_exit);
519     signal(SIGHUP, signal_exit);
520     signal(SIGCHLD, signal_child);
521     signal(SIGPIPE, SIG_IGN);           /* so that Xlib gets an error */
522     signal(SIGUSR1, signal_usr1);
523 #endif
524 }
525
526 /* detach() taken from old zwgc, with lots of stuff ripped out */
527
528 static void detach()
529 {
530   /* detach from terminal and fork. */
531   register int i;
532
533   /* Attempt to join the process group of the session leader.  This
534    * will get us a HUP if the session leader is in the foreground at
535    * logout time (which is often the case) or if the shell is running
536    * under telnetd or xterm (both of which HUP the process group of
537    * their child process).  If we have getsid(), that's the best way
538    * of finding the session leader; otherwise use the process group of
539    * the parent process, which is a good guess. */
540 #if defined(HAVE_GETSID)
541   setpgid(0, getsid(0));
542 #elif defined(HAVE_GETPGID)
543   setpgid(0, getpgid(getppid()));
544 #elif !defined(GETPGRP_VOID)
545   setpgid(0, getpgrp(getppid()));
546 #endif
547
548   /* fork off and let parent exit... */
549   if (i = fork()) {
550       if (i < 0) {
551           perror("zwgc: cannot fork, aborting:");
552           exit(1);
553       }
554       exit(0);
555   }
556 }