]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/main.c
new upstream version
[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: main.c 2328 2009-03-22 17:34:39Z kcr $
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: main.c 2328 2009-03-22 17:34:39Z kcr $";
21 #endif
22
23 #include <netdb.h>
24 #include <arpa/nameser.h>
25 #include <sys/socket.h>
26 #include <sys/resource.h>
27 #include <zephyr/mit-copyright.h>
28 #include <zephyr/zephyr.h>
29
30 #include "new_memory.h"
31 #include "zwgc.h"
32 #include "parser.h"
33 #include "node.h"
34 #include "exec.h"
35 #include "zephyr.h"
36 #include "notice.h"
37 #include "subscriptions.h"
38 #include "file.h"
39 #include "mux.h"
40 #include "port.h"
41 #include "variables.h"
42 #include "main.h"
43 #ifdef CMU_ZCTL_PUNT
44 #include "int_dictionary.h"
45 #endif
46 #ifdef CMU_ZWGCPLUS
47 #include "plus.h"
48 int zwgcplus = 0;
49 #endif
50
51 void notice_handler(ZNotice_t *);
52 static void process_notice(ZNotice_t *, char *);
53 static void setup_signals(int);
54 static void detach(void);
55 static void signal_exit(int);
56 #ifdef HAVE_ARES
57 static void notice_callback(void *, int, int, char *, char *);
58 #endif
59
60 /*
61  * Global zwgc-wide variables:
62  */
63
64 #ifdef DEBUG
65 int zwgc_debug = 0;
66 #endif
67
68 static char *zwgc_version_string = "1.0";
69
70 /*
71  * description_filename_override - <<<>>>
72  */
73
74 static char *description_filename_override = NULL;
75
76 /*
77  * progname - <<<>>> export!
78  */
79
80 char *progname = NULL;
81
82 /*
83  * subscriptions_filename_override - <<<>>> export!
84  */
85
86 char *subscriptions_filename_override = NULL;
87
88 /*
89  * location_override - <<<>>> export!
90  */
91
92 char *location_override = NULL;
93
94 #ifdef HAVE_ARES
95 /*
96  * achannel - <<<>>> export!
97  */
98
99 ares_channel achannel;
100 #endif
101
102 /****************************************************************************/
103 /*                                                                          */
104 /*             Code to deal with reading in the description file:           */
105 /*                                                                          */
106 /****************************************************************************/
107
108 /*
109  * program - this holds a pointer to the node representation of the
110  *           description file once it has been read in.
111  */
112
113 static struct _Node *program = NULL;
114
115 /*
116  * <<<>>>
117  */
118
119 static void
120 fake_startup_packet(void)
121 {
122     ZNotice_t *notice = (ZNotice_t *)malloc(sizeof(ZNotice_t));
123     struct timezone tz;
124     char msgbuf[BUFSIZ];
125     extern void Z_gettimeofday(struct _ZTimeval *, struct timezone *);
126
127     var_set_variable("version", zwgc_version_string);
128
129     (void) memset(notice, 0, sizeof(ZNotice_t));
130
131     notice->z_version = "";
132     notice->z_class = "WG_CTL_CLASS";
133     notice->z_class_inst = "WG_CTL_USER<<<>>>";
134     notice->z_opcode = "WG_STARTUP";
135     notice->z_default_format = "Zwgc mark II version $version now running...\n";
136     notice->z_recipient = "";
137     notice->z_sender = "ZWGC";
138     Z_gettimeofday(&notice->z_time, &tz);
139     notice->z_port = 0;
140     notice->z_kind = ACKED;
141     notice->z_auth = ZAUTH_YES;
142     notice->z_charset = ZCHARSET_UNKNOWN;
143     sprintf(msgbuf,"Zwgc mark II version %s now running...",
144             zwgc_version_string);
145     notice->z_message = msgbuf;
146     notice->z_message_len = strlen(notice->z_message)+1;
147     
148 #ifdef CMU_ZWGCPLUS
149     list_add_notice(notice);
150     set_notice_fake(notice, 1);
151 #endif
152     process_notice(notice, NULL);
153 #ifdef CMU_ZWGCPLUS
154     list_del_notice(notice);
155 #else
156     free(notice);
157 #endif
158 }
159
160 static void
161 read_in_description_file(void)
162 {
163     FILE *input_file;
164     char defdesc[128];
165
166 /*    var_clear_all_variables(); <<<>>> */
167
168     sprintf(defdesc, "%s/zephyr/%s", DATADIR, DEFDESC);
169     input_file = locate_file(description_filename_override, USRDESC, defdesc);
170     if (input_file)
171       program = parse_file(input_file);
172     else
173       program = NULL;
174     
175     fake_startup_packet();
176 }
177
178 /****************************************************************************/
179 /*                                                                          */
180 /*            Code to deal with argument parsing & overall control:         */
181 /*                                                                          */
182 /****************************************************************************/
183
184 /*
185  *    void usage()
186  *        Effects: Prints out an usage message on stderr then exits the
187  *                 program with error code 1.
188  */
189
190 void
191 usage(void)
192 {
193 #ifdef DEBUG
194     fprintf(stderr, "\
195 zwgc: usage: zwgc [-debug] [-f <filename>] [-subfile <filename>]\n\
196                   [-ttymode] [-nofork] [-reenter] [-loc text]\n\
197                   [-default <driver>] {-disable <driver>}*\n\
198                   [output driver options]\n");
199 #else
200     fprintf(stderr, "\
201 zwgc: usage: zwgc [-f <filename>] [-subfile <filename>]\n\
202                   [-ttymode] [-nofork] [-reenter] [-loc text]\n\
203                   [-default <driver>] {-disable <driver>}*\n\
204                   [output driver options]\n");
205 #endif
206     exit(1);
207 }
208
209 /*
210  * <<<>>>
211  */
212
213 static void
214 run_initprogs(void)
215 {
216     /*
217      * This code stolen from old zwgc: yuck.  Clean up & fix.  <<<>>>
218      * Should this fork instead of just systeming?
219      */
220
221     int status;
222     char *progname = ZGetVariable("initprogs");
223     
224     if (!progname)
225       return;
226     
227     status = system(progname);
228     if (status == 127) {
229         perror("zwgc initprog exec");
230         fprintf(stderr,"zwgc initprog of <%s> failed: no shell.\n",
231                 progname);
232     } else if (status!=-1 && status>>8) {
233         perror("zwgc initprog exec");
234         fprintf(stderr,"zwgc initprog of <%s> failed with status [%d].\n",
235                 progname, status>>8);
236     }
237 }
238
239 /*
240  * main -- the program entry point.  Does parsing & top level control.
241  */
242
243 int
244 main(int argc,
245      char **argv)
246 {
247     char **new;
248     register char **current;
249     int dofork = 1;
250 #ifdef HAVE_ARES
251     int status;
252 #endif
253
254     progname = argv[0];
255
256     /*
257      * Process "-f <filename>", "-subfile <filename>", "-nofork",
258      * "-reenter" (which is ignored) and (if DEBUG) "-debug"
259      * arguments, removing then from argc, argv:
260      */
261     for (new=current=argv+1; *current; current++) {
262         if (string_Eq(*current, "-debug")) {
263             argc--;
264 #ifdef DEBUG
265             zwgc_debug = 1;
266 #endif
267         } else if (string_Eq(*current, "-f")) {
268             argc -= 2; current++;
269             if (!*current)
270               usage();
271             description_filename_override = *current;
272         } else if (string_Eq(*current, "-subfile")) {
273             argc -= 2; current++;
274             if (!*current)
275               usage();
276             subscriptions_filename_override = *current;
277         } else if (string_Eq(*current, "-nofork")) {
278             argc--;
279             dofork = 0;
280         } else if (string_Eq(*current, "-reenter")) {
281             argc--;                     /* just throw it away */
282         } else if (string_Eq(*current, "-loc")) {
283             argc -= 2; current++;
284             if (!*current)
285               usage();
286             location_override = *current;
287         } else
288           *(new)++ = *current;
289     }
290     *new = *current;
291
292 #ifdef HAVE_ARES
293     /*
294      * Initialize resolver library
295      */
296     status = ares_init(&achannel);
297     if (status != ARES_SUCCESS) {
298         fprintf(stderr, "Couldn't initialize resolver: %s\n",
299                 ares_strerror(status));
300         return(1);
301     }
302 #endif
303
304     /*
305      * Initialize various subsystems in proper order:
306      */
307     dprintf("Initializing subsystems...\n"); /*<<<>>>*/
308 #ifdef CMU_ZWGCPLUS
309     init_noticelist();
310 #endif
311     mux_init();
312     var_clear_all_variables(); /* <<<>>> */
313     init_ports();       /* <<<>>> */
314     dprintf("Initializing standard ports...\n");
315     init_standard_ports(&argc, argv);
316     if (argc>1)
317       usage();
318     dprintf("Initializing zephyr...\n");
319     setup_signals(dofork);
320     zephyr_init(notice_handler);
321
322     if (dofork)
323         detach();
324     /*
325      * Run the initprogs program(s) now that we are all set to deal:
326      */
327     dprintf("Running initprogs program...\n");
328     run_initprogs();
329
330     dprintf("Test Zwgc parser.\n\n");
331     read_in_description_file();
332
333 #ifdef CMU_ZWGCPLUS
334     if (strcmp(progname, "zwgcplus") == 0)
335       zwgcplus = 1;
336 #endif
337
338     dprintf("Entering main loop\n");
339     mux_loop();
340
341     dprintf("Returning from main loop\n");
342     finalize_zephyr();
343
344     return(0);
345 }
346
347 /****************************************************************************/
348 /*                                                                          */
349 /*               :               */
350 /*                                                                          */
351 /****************************************************************************/
352
353 #define  USER_SUPPRESS     "SUPPRESS"
354 #define  USER_UNSUPPRESS   "UNSUPPRESS"
355
356 #ifdef CMU_ZCTL_PUNT
357 #define  USER_LIST_SUPPRESSED "LIST-SUPPRESSED"
358
359 #define PUNT_INC 1024
360 extern int_dictionary puntable_addresses_dict;
361 ZNotice_t punt_reply;
362
363 void
364 create_punt_reply(int_dictionary_binding *punt)
365 {
366     string binding;
367     int key_len = strlen(punt->key);
368     char *tmp;
369     
370     if (!punt_reply.z_message) {
371         punt_reply.z_message = (char *)malloc(PUNT_INC);
372         punt_reply.z_message[0] = 0;
373     }
374     
375     if ((punt_reply.z_message_len + key_len + 1) / PUNT_INC > 
376         (punt_reply.z_message_len + PUNT_INC - 1) / PUNT_INC) {
377         char *new_message = (char *)malloc((punt_reply.z_message_len
378                                             / PUNT_INC + 1) * PUNT_INC);
379         
380         strcpy(new_message, punt_reply.z_message);
381         
382         free(punt_reply.z_message);
383         punt_reply.z_message = new_message;
384     }
385     tmp = punt_reply.z_message + strlen(punt_reply.z_message);
386     strcat (punt_reply.z_message, punt->key);
387     strcat (punt_reply.z_message, "\n");
388     punt_reply.z_message_len += key_len + 1;
389     
390     while (*tmp != '\001') tmp++;
391     *tmp = ',';
392     while (*tmp != '\001') tmp++;
393     *tmp = ',';
394 }
395 #endif /* CMU_ZCTL_PUNT */
396
397 void
398 notice_handler(ZNotice_t *notice)
399 {
400     char node[MAXDNAME];
401
402 #if defined(CMU_ZWGCPLUS)
403     list_add_notice(notice);
404 #endif
405
406 #ifdef HAVE_ARES
407     ares_getnameinfo(achannel,
408                      (const struct sockaddr *)&(notice->z_sender_sockaddr),
409                      notice->z_sender_sockaddr.sa.sa_family == AF_INET ?
410                      sizeof(struct sockaddr_in) :
411                      notice->z_sender_sockaddr.sa.sa_family == AF_INET6 ?
412                      sizeof(struct sockaddr_in6) :
413                      sizeof(notice->z_sender_sockaddr), ARES_NI_LOOKUPHOST,
414                      notice_callback, notice);
415     
416 #else
417     getnameinfo((const struct sockaddr *)&(notice->z_sender_sockaddr),
418                 sizeof(notice->z_sender_sockaddr),
419                 node, sizeof(node), NULL, 0, 0);
420     
421     process_notice(notice, node);
422 #ifdef CMU_ZWGCPLUS
423     /* Let list_del_notice clean up for us. */
424 #else
425     ZFreeNotice(notice);
426     free(notice);
427 #endif
428 #endif
429 }
430
431 #ifdef HAVE_ARES
432 /*
433 static void
434 notice_callback(void *arg,
435                 int status,
436                 int timeouts,
437                 struct hostent *fromhost)
438 */
439 static void
440 notice_callback(void *arg,
441                 int status,
442                 int timeouts,
443                 char *node,
444                 char *service)
445 {
446     ZNotice_t *notice = (ZNotice_t *) arg;
447
448 #ifdef CMU_ZWGCPLUS
449     plus_set_hname(notice, node);
450 #endif
451
452     process_notice(notice, node);
453 #ifdef CMU_ZWGCPLUS
454     list_del_notice(notice);
455 #else
456     ZFreeNotice(notice);
457     free(notice);
458 #endif
459 }
460 #endif
461
462 static void
463 process_notice(ZNotice_t *notice,
464                char *hostname)
465 {
466     char *control_opcode;
467
468     dprintf("Got a message\n");
469
470     control_opcode = decode_notice(notice, hostname);
471     if (control_opcode) {
472 #ifdef DEBUG
473         printf("got control opcode <%s>.\n", control_opcode);
474 #endif
475         if (!strcasecmp(control_opcode, USER_REREAD)) {
476             read_in_description_file();
477         } else if (!strcasecmp(control_opcode, USER_SHUTDOWN))
478           zwgc_shutdown();
479         else if (!strcasecmp(control_opcode, USER_STARTUP)) {
480 #ifdef DEBUG_MEMORY
481             report_memory_usage(); /* <<<>>> */
482 #endif
483             zwgc_startup();
484         } else if (!strcasecmp(control_opcode, USER_SUPPRESS)) {
485             string class = get_field(notice->z_message,
486                                      notice->z_message_len, 1);
487             string instance = get_field(notice->z_message,
488                                         notice->z_message_len, 2);
489             string recipient = get_field(notice->z_message,
490                                          notice->z_message_len, 3);
491             punt(class, instance, recipient);
492             free(class);
493             free(instance);
494             free(recipient);
495         } else if (!strcasecmp(control_opcode, USER_UNSUPPRESS)) {
496             string class = get_field(notice->z_message,
497                                      notice->z_message_len, 1);
498             string instance = get_field(notice->z_message,
499                                         notice->z_message_len, 2);
500             string recipient = get_field(notice->z_message,
501                                          notice->z_message_len, 3);
502             unpunt(class, instance, recipient);
503             free(class);
504             free(instance);
505             free(recipient);
506 #ifdef CMU_ZCTL_PUNT
507         } else if (!strcasecmp(control_opcode, USER_LIST_SUPPRESSED)) {
508             struct sockaddr_in old, to;
509             int retval;
510             
511             if (!notice->z_port) {
512                 printf("zwgc: can't reply to LIST-SUPPRESSED request\n");
513                 return;
514             }
515             memset((char *) &punt_reply, 0, sizeof(ZNotice_t));
516             punt_reply.z_kind = CLIENTACK;
517             punt_reply.z_class = WG_CTL_CLASS;
518             punt_reply.z_class_inst = "WG_REPLY";
519             punt_reply.z_recipient = "zctl?";
520             punt_reply.z_sender = "Zwgc";
521             punt_reply.z_default_format = "";
522             punt_reply.z_opcode = USER_LIST_SUPPRESSED;
523             punt_reply.z_port = notice->z_port;
524             punt_reply.z_message = NULL;
525             punt_reply.z_message_len = 0;
526             
527             if (puntable_addresses_dict) {
528                 int_dictionary_Enumerate(puntable_addresses_dict,
529                                          create_punt_reply);
530             }
531             
532             old = ZGetDestAddr();
533             to = old;
534             
535             to.sin_port = notice->z_port;
536             if ((retval = ZSetDestAddr(&to)) != ZERR_NONE) {
537                 com_err("zwgc",retval,"while setting destination address");
538                 exit(1);
539             }
540             
541             ZSendNotice(&punt_reply, ZNOAUTH);
542             
543             if ((retval = ZSetDestAddr(&old)) != ZERR_NONE) {
544                 com_err("zwgc",retval,"while resetting destination address");
545                 exit(1);
546             }
547             
548             if (punt_reply.z_message) {
549                 free(punt_reply.z_message);
550                 punt_reply.z_message = NULL;
551             }
552 #endif
553         } else if (!strcasecmp(control_opcode, USER_EXIT)) {
554             signal_exit(0);
555         } else
556           printf("zwgc: unknown control opcode %s.\n", control_opcode);
557
558         goto cleanup;
559     }
560
561     if (!zwgc_active) {
562 #ifdef DEBUG
563         if (zwgc_debug)
564           printf("NON-ACTIVE: PUNTED <%s>!!!!\n", notice->z_class_inst);
565 #endif
566         goto cleanup;   
567     }
568     
569     if (puntable_address_p(notice->z_class,
570                            notice->z_class_inst,
571                            notice->z_recipient)) {
572 #ifdef DEBUG
573         if (zwgc_debug)
574           printf("PUNTED <%s>!!!!\n", notice->z_class_inst);
575 #endif
576         goto cleanup;
577     }
578
579     exec_process_packet(program, notice);
580   cleanup:
581     return;
582 }
583
584 #ifdef CMU_ZWGCPLUS
585 void
586 reprocess_notice(ZNotice_t *notice, char *hostname)
587 {
588   list_add_notice(notice);
589   process_notice(notice, hostname);
590   list_del_notice(notice);
591 }
592 #endif
593
594 /***************************************************************************/
595
596 /*
597  *
598  */
599
600 static void
601 signal_exit(int ignored)
602 {
603     mux_end_loop_p = 1;
604 }
605
606 /* clean up ALL the waiting children, in case we get hit with
607    multiple SIGCHLD's at once, and don't process in time. */
608 static RETSIGTYPE
609 signal_child(int ignored)
610 {
611 #ifdef HAVE_WAITPID
612   int status;
613 #else
614   union wait status;
615 #endif
616   int pid, old_errno = errno;
617
618   do {
619 #ifdef HAVE_WAITPID
620       pid = waitpid(-1, &status, WNOHANG);
621 #else
622       pid = wait3(&status, WNOHANG, (struct rusage *)0);
623 #endif
624   } while (pid != 0 && pid != -1);
625   errno = old_errno;
626 }
627
628 /* rewrite the wgfile in case it has gone away */
629 static RETSIGTYPE
630 signal_usr1(int ignored)
631 {
632     write_wgfile();
633 }
634
635 static void
636 setup_signals(int dofork)
637 {
638 #ifdef _POSIX_VERSION
639     struct sigaction sa;
640
641     sigemptyset(&sa.sa_mask);
642     sa.sa_flags = 0;
643     
644     if (dofork) {
645         sa.sa_handler = SIG_IGN;
646         sigaction(SIGINT, &sa, (struct sigaction *)0);
647         sigaction(SIGTSTP, &sa, (struct sigaction *)0);
648         sigaction(SIGQUIT, &sa, (struct sigaction *)0);
649         sigaction(SIGTTOU, &sa, (struct sigaction *)0);
650     } else {
651         /* clean up on SIGINT; exiting on logout is the user's problem, now. */
652         sa.sa_handler = signal_exit;
653         sigaction(SIGINT, &sa, (struct sigaction *)0);
654     }
655
656     /* behavior never changes */
657     sa.sa_handler = signal_exit;
658     sigaction(SIGTERM, &sa, (struct sigaction *)0);
659     sigaction(SIGHUP, &sa, (struct sigaction *)0);
660
661     sa.sa_handler = SIG_IGN;
662     sigaction(SIGPIPE, &sa, (struct sigaction *)0);
663
664     sa.sa_handler = signal_child;
665     sigaction(SIGCHLD, &sa, (struct sigaction *)0);
666
667     sa.sa_handler = signal_usr1;
668     sigaction(SIGUSR1, &sa, (struct sigaction *)0);
669
670 #else /* !POSIX */
671     if (dofork) {
672         /* Ignore keyboard signals if forking.  Bad things will happen. */
673         signal(SIGINT, SIG_IGN);
674         signal(SIGTSTP, SIG_IGN);
675         signal(SIGTTOU, SIG_IGN);
676         signal(SIGQUIT, SIG_IGN);
677     } else {
678         /* clean up on SIGINT; exiting on logout is the user's problem, now. */
679         signal(SIGINT, signal_exit);
680     }
681     
682     /* behavior never changes */
683     signal(SIGTERM, signal_exit);
684     signal(SIGHUP, signal_exit);
685     signal(SIGCHLD, signal_child);
686     signal(SIGPIPE, SIG_IGN);           /* so that Xlib gets an error */
687     signal(SIGUSR1, signal_usr1);
688 #endif
689 }
690
691 /* detach() taken from old zwgc, with lots of stuff ripped out */
692
693 static void
694 detach(void)
695 {
696   /* detach from terminal and fork. */
697   register int i;
698
699   /* Attempt to join the process group of the session leader.  This
700    * will get us a HUP if the session leader is in the foreground at
701    * logout time (which is often the case) or if the shell is running
702    * under telnetd or xterm (both of which HUP the process group of
703    * their child process).  If we have getsid(), that's the best way
704    * of finding the session leader; otherwise use the process group of
705    * the parent process, which is a good guess. */
706 #if defined(HAVE_GETSID)
707
708   setpgid(0, getsid(0));
709 #elif defined(HAVE_GETPGID)
710   setpgid(0, getpgid(getppid()));
711 #elif !defined(GETPGRP_VOID)
712   setpgid(0, getpgrp(getppid()));
713 #endif
714
715   /* fork off and let parent exit... */
716   i = fork();
717   if (i) {
718       if (i < 0) {
719           perror("zwgc: cannot fork, aborting:");
720           exit(1);
721       }
722       exit(0);
723   }
724 }