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