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