home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / interfaces / zephyr / tzc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  22.3 KB  |  932 lines

  1. /*
  2.    tzc.c  trivial zephyr client (once upon a time)
  3.    Copyright (C) 1992, 1993 Scott Draves (spot@cs.cmu.edu)
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2 of the License, or
  8.    (at your option) any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.    Now at version 2.0.  Somewhat (he made me say that) incompatible with
  20.    previous versions.
  21.  
  22.    this program is a replacement for zwgc and zwrite that talks to emacs
  23.    in printed sexps.  you can get the whole thing from
  24.    hopeless.mess.cs.cmu.edu:/usr/spot/pub/zephyr.tar.Z
  25.  
  26.  
  27.    CHANGES
  28.  
  29.    Mon Jan 18  received -a option from hsw@cs.cmu.edu, it restarts the
  30.                process if no messages have been received after arg seconds.
  31.    Wed Jan  6  added p: to options string, i forgot it in the merge
  32.    Fri Jan  1  replace ((tzcspew . sent) (to "spot"))
  33.                with    ((tzcspew . sent) (to "PERSONAL" . "spot"))
  34.    Sun Dec 20  removed silly old code that was forcing recip to be "" for
  35.                non-personal instances.
  36.    sometime    recognizes (auth . nil) to disable kerberos authentication
  37.    Wed Dec 16  added some more error checking to send_zgram so that it
  38.                handles garbled input without dying.
  39.    Sun Dec 13: merged in lisp reading stuff.  no more calls to zwrite.
  40.    Sun Oct 25: if we get a USR1 signal, then restart ourselves. added -p
  41.                flag to write our pid into a file. this is so that kauthd
  42.            users can restart tzc automatically when they get new
  43.            tickets.
  44.    Tue Jul 28: added wait() to subscribe_with_zctl() to prevent zombies.
  45.  
  46.    TODO
  47.  
  48.    if you get a USR1 in the middle of writing out a msg, then the output
  49.    will not be properly formatted.  so should either disable the sig for
  50.    this time, or set a flag in the handler and check at the main loop.  the
  51.    same probably applies to the alarm.
  52.  
  53.    write a man page
  54.    
  55.   */
  56.  
  57. #include <strings.h>
  58. #include <stdio.h>
  59. #include <sys/types.h>
  60. #include <ctype.h>
  61. #include <zephyr/zephyr.h>
  62. #include <netdb.h>
  63. #include <sys/socket.h>
  64. #include <netinet/in.h>
  65. #include <arpa/inet.h>
  66. #include <fcntl.h>
  67. #include <signal.h>
  68. #include "lread.h"
  69.  
  70. #define ZCTL_BINARY "/usr/misc/.zephyr/bin/zctl"
  71.  
  72. typedef struct PendingReply PendingReply;
  73. struct PendingReply {
  74.    char *instance;
  75.    char *recipient;
  76.    ZUnique_Id_t    uid;
  77.    PendingReply *next;
  78. };
  79.  
  80. struct Globals {
  81.    char        *program;
  82.    int          argc;
  83.    char         **argv;
  84.  
  85.    u_short    port;
  86.    int        stdin_flags;
  87.    int        zfd;
  88.  
  89.    int        ebufsiz;
  90.    char *    ebuf;
  91.    char *    ebufptr;
  92.  
  93.    /* linked list of messages sent which are waiting for replies */
  94.    PendingReply *pending_replies;
  95.  
  96.    struct {
  97.       Value *sym_class;
  98.       Value *sym_instance;
  99.       Value *sym_recipients;
  100.       Value *sym_message;
  101.       Value *sym_send;
  102.       Value *sym_tzcfodder;
  103.       Value *sym_auth;
  104.    } constants;
  105. };
  106.  
  107. struct Globals global_storage, *globals;
  108.  
  109. void usage() {
  110.    fprintf(stderr, "usage: %s [-s] [-e exposure] [-p filename] [-a nseconds]\n",
  111.        globals->program);
  112. }
  113.  
  114. /* I'm paranoid about leaving stdin non-blocking, though I doubt it's a */
  115. /* problem in this case. - nix */
  116. void bail(int code) {
  117.    if (-1 == fcntl(0, F_SETFL, globals->stdin_flags)) {
  118.       perror("unable to reset stdin");
  119.       code = 1;
  120.    }
  121.    exit(code);
  122. }
  123.  
  124. Code_t check(Code_t e, char *s) {
  125.    if (e) {
  126.       com_err(__FILE__, e, s);
  127.       bail(1);
  128.    }
  129.    return e;
  130. }
  131.  
  132. Code_t warn(Code_t e, char *s) {
  133.    if (e)
  134.       com_err(__FILE__, e, s);
  135.    return e;
  136. }
  137.  
  138. #if 0
  139. /* useful for debugging */
  140. void list_subs() {
  141.    int i, n, one = 1;
  142.    ZSubscription_t sub;
  143.    ZRetrieveSubscriptions(0, &n);
  144.    for (i = 0; i < n; i++) {
  145.       ZGetSubscriptions(&sub, &one);
  146.       printf("recip(%s) class(%s) inst(%s)\n",
  147.          sub.recipient, sub.class, sub.classinst);
  148.    }
  149. }
  150. #endif
  151.  
  152. void subscribe() {
  153.    static ZSubscription_t sub[] =
  154.    {{"%me%", "MESSAGE", "*"},
  155.     {"*", "MESSAGE", "*"},
  156.     {"*", "*", "*"}};
  157.    int n = sizeof(sub) / sizeof(ZSubscription_t);
  158.    sub[0].recipient = ZGetSender();
  159.    check(ZSubscribeTo(sub, n, 0), "ZSubscribeTo");
  160. }
  161.  
  162.  
  163. void subscribe_with_zctl() {
  164.   int pid;
  165.   FILE *fd;
  166.   char st[1024];
  167.   char *portfile;
  168.   extern char *getenv();
  169.  
  170.   portfile = getenv("WGFILE");
  171.  
  172.   if(!portfile) {
  173.     sprintf(st,"/tmp/wg.%d",getuid());
  174.     portfile = st;
  175.   }
  176.  
  177.   fd = fopen(portfile,"w");
  178.   if(!fd) {
  179.     perror(portfile);
  180.     bail(1);
  181.   }
  182.   fprintf(fd, "%d\n", globals->port);
  183.   fclose(fd);
  184.  
  185.   if(!(pid = vfork())) {
  186.       char fn[1024];
  187.       sprintf(fn,"%s/.zephyr.subs.tzc",getenv("HOME"));
  188.       execl(ZCTL_BINARY,"zctl","load",fn,0);
  189.       perror("zwgc exec");
  190.       fprintf(stderr,"Unable to load subscriptions.\n");
  191.       bail(0);
  192.     }
  193.   if(pid == -1) {
  194.       perror("zwgc exec");
  195.       fprintf(stderr,"Unable to fork and load subscriptions.\n");
  196.     }
  197.   while (pid != wait(0));
  198. }
  199.  
  200.  
  201. /*
  202.   like fputs, but quotes the string as necc for 
  203.   reading as a string by gnu-emacs
  204.   */
  205. void fputqs(char *s, FILE *o) {
  206.    char c;
  207.    putc('\"', o);
  208.    while (c = *s++) {
  209.       if (c == '\1') {
  210.      putc('\\', o);
  211.      putc('1', o);
  212.       } else {
  213.      if (c == '\"' || c == '\\')
  214.         putc('\\', o);
  215.      putc(c, o);
  216.       }
  217.    }
  218.    putc('\"', o);
  219. }
  220.  
  221. /* like fputs, but quotes for reading as a symbol by gnu-emacs */
  222. void fputqqs(char *s, FILE *o) {
  223.    char c;
  224.    if ('\0' == *s)
  225.       fputs("nil", o);
  226.    else
  227.       while (c = *s++) {
  228.      if (!(isalnum(c) || strchr("-+*/_~!@$%^&=:<>{}", c)))
  229.         putc('\\', o);
  230.      putc(c, o);
  231.       }
  232. }
  233.  
  234. void
  235. emacs_put_open()
  236. {
  237.    fputs("\001(", stdout);
  238. }
  239.  
  240. void
  241. emacs_put_close()
  242. {
  243.    putc(')', stdout);
  244.    putc('\0', stdout);
  245.    putc('\n', stdout);
  246.    fflush(stdout);
  247. }
  248.  
  249. void
  250. emacs_put_pair_open(char *tag)
  251. {
  252.    putc('(', stdout);
  253.    fputqqs(tag, stdout);
  254.    fputs(" . ", stdout);
  255. }
  256.  
  257. void
  258. emacs_put_pair_close()
  259. {
  260.    fputs(") ", stdout);
  261. }
  262.  
  263. void
  264. emacs_put_sym(char *tag, char *sym)
  265. {
  266.    putc('(', stdout);
  267.    fputqqs(tag, stdout);
  268.    fputs(" . ", stdout);
  269.    if (sym[0])
  270.       fputqqs(sym, stdout);
  271.    else
  272.       fputs("nil", stdout);
  273.    fputs(") ", stdout);
  274. }
  275.  
  276. void
  277. emacs_put_str(char *tag, char *str)
  278. {
  279.    putc('(', stdout);
  280.    fputqqs(tag, stdout);
  281.    fputs(" . ", stdout);
  282.    fputqs(str, stdout);
  283.    fputs(") ", stdout);
  284. }
  285.  
  286. void
  287. emacs_put_int(char *tag, int i)
  288. {
  289.    putc('(', stdout);
  290.    fputqqs(tag, stdout);
  291.    fputs(" . ", stdout);
  292.    printf("%d", i);
  293.    fputs(") ", stdout);
  294. }
  295.  
  296. void
  297. emacs_put_str_str(char *tag, char *a, char *b)
  298. {
  299.    putc('(', stdout);
  300.    fputqqs(tag, stdout);
  301.    putc(' ', stdout);
  302.    fputqs(a, stdout);
  303.    fputs(" . ", stdout);
  304.    fputqs(b, stdout);
  305.    fputs(") ", stdout);
  306. }   
  307.  
  308. void
  309. emacs_error(char *err)
  310. {
  311.    static int reentry = 0;
  312.    if (reentry) {
  313.       /* die a horrible death */
  314.       printf("Looping emacs_error.  Aieee!  I perish in flames!\n");
  315.       exit(5);
  316.    }
  317.    reentry = 1;
  318.    emacs_put_open();
  319.    emacs_put_sym("tzcspew", "error");
  320.    emacs_put_str("message", err);
  321.    emacs_put_close();
  322.    reentry = 0;
  323. }
  324.  
  325. char *auth_string(int n) {
  326.    switch (n) {
  327.     case ZAUTH_YES    : return "yes";
  328.     case ZAUTH_FAILED : return "failed";
  329.     case ZAUTH_NO     : return "no";
  330.     default           : return "bad-auth-value";
  331.    }
  332. }
  333.  
  334. char *kind_string(int n) {
  335.    switch (n) {
  336.     case UNSAFE:    return "unsafe";
  337.     case UNACKED:   return "unacked";
  338.     case ACKED:     return "acked";
  339.     case HMACK:     return "hmack";
  340.     case HMCTL:     return "hmctl";
  341.     case SERVACK:   return "servack";
  342.     case SERVNAK:   return "servnak";
  343.     case CLIENTACK: return "clientack";
  344.     case STAT:      return "stat";
  345.     default:        return "bad-kind-value";
  346.    }
  347. }
  348.  
  349. void
  350. send_zgram(Value *spec)
  351. {
  352.     ZNotice_t notice;
  353.     int retval;
  354.     char bfr[BUFSIZ];
  355.     int (*auth)();
  356.     Value *v;
  357.     Value *recip_list;
  358.     char *class;
  359.     Value *messageval;
  360.     Value *message_list;
  361.  
  362.     bzero((char *) ¬ice, sizeof(notice));
  363.  
  364.     /* emacs sends something of the form:
  365.      * ((class . class) (recipients . recip-list))
  366.      */
  367.     /* pull class and inst, and recip_list out of spec */
  368.     v = assqv(globals->constants.sym_class, spec);
  369.     if (VTAG(v) != cons) {
  370.        emacs_error("class not defined");
  371.        goto fail;
  372.     }
  373.     class = vextract_string_c(VCDR(v));
  374.     v = assqv(globals->constants.sym_recipients, spec);
  375.     if (VTAG(v) != cons) {
  376.        emacs_error("recipients not defined");
  377.        goto fail;
  378.     }
  379.     recip_list = VCDR(v);
  380.     v = assqv(globals->constants.sym_message, spec);
  381.     if (VTAG(v) != cons) {
  382.        emacs_error("message not defined");
  383.        goto fail;
  384.     }
  385.     message_list = VCDR(v);
  386.  
  387.     auth = ZAUTH;
  388.     v = assqv(globals->constants.sym_auth, spec);
  389.     if (VTAG(v) == cons &&
  390.     VTAG(VCDR(v)) == nil)
  391.        auth = ZNOAUTH;
  392.  
  393.     notice.z_kind = ACKED;
  394.     notice.z_port = 0;
  395.     notice.z_class = class;
  396.     notice.z_opcode = "";
  397.     notice.z_sender = 0;
  398.     notice.z_message = NULL;
  399.  
  400.     if (auth == ZAUTH) {
  401.        notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient)\n$message";
  402.     } else {
  403.        notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance:\n$message";
  404.     }
  405.  
  406.     if (1) {
  407.        /* cdr through the message list, determining total length */
  408.        Value *chase_message_list = message_list;
  409.        char *buffer_mark;
  410.        notice.z_message_len = 0;
  411.        while (VTAG(chase_message_list) == cons) {
  412.       Value *one_field = VCAR(chase_message_list);
  413.       notice.z_message_len += VSLENGTH(one_field) + 1;
  414.       chase_message_list = VCDR(chase_message_list);
  415.        }
  416.  
  417.        buffer_mark = notice.z_message =
  418.       (char *) malloc(notice.z_message_len);
  419.        
  420.        /* cdr through again, this time copying strings into the buffer */
  421.        chase_message_list = message_list;
  422.        while (VTAG(chase_message_list) == cons) {
  423.       Value *one_field = VCAR(chase_message_list);
  424.       bcopy(VSDATA(one_field), buffer_mark, VSLENGTH(one_field));
  425.       buffer_mark += VSLENGTH(one_field);
  426.       *buffer_mark++ = 0;
  427.       chase_message_list = VCDR(chase_message_list);
  428.        }
  429.     }
  430.     
  431.     /* cdr through the recipient list */
  432.     while (VTAG(recip_list) == cons) {
  433.        Value *recip_pair = VCAR(recip_list);
  434.  
  435.        if (VTAG(recip_pair) != cons) {
  436.       emacs_error("bad recipient, not a pair");
  437.       goto bailout;
  438.        }
  439.  
  440.        /* these strings are freed when the reply comes back in */
  441.        notice.z_class_inst = vextract_string_c(VCAR(recip_pair));
  442.        notice.z_recipient = vextract_string_c(VCDR(recip_pair));
  443.  
  444.        if ((retval = ZSendNotice(¬ice, auth)) != ZERR_NONE) {
  445.       (void) sprintf(bfr, "while sending notice to %s", 
  446.              notice.z_recipient);
  447.       com_err("foo", retval, bfr);
  448.       break;
  449.        }
  450.        else {
  451.       /* prepare for reply */
  452.       PendingReply *reply = (PendingReply *) malloc(sizeof(PendingReply));
  453.       reply->instance = notice.z_class_inst;
  454.       reply->recipient = notice.z_recipient;
  455.       reply->uid = notice.z_uid;
  456.       /* any other info to save? */
  457.       reply->next = globals->pending_replies;
  458.       globals->pending_replies = reply;
  459.        }
  460.  
  461.        recip_list = VCDR(recip_list);
  462.     }
  463.  
  464.   bailout:
  465.     free(notice.z_message);
  466.   fail:
  467. }
  468.  
  469. void
  470. process_sexp(Value *v)
  471. {
  472.    Value *pair;
  473.    Value *key;
  474.  
  475. #if 0
  476.    printf("read: ");
  477.    prin(stdout, v);
  478.    fputc('\n', stdout);
  479. #endif
  480.  
  481.    if (VTAG(v) == cons &&
  482.        NULL != (pair = assqv(globals->constants.sym_tzcfodder, v)) &&
  483.        NULL != (key = VCDR(pair))) {
  484.       if (eqv(key, globals->constants.sym_send))
  485.      send_zgram(v);
  486.       else
  487.      emacs_error("bad tzcfodder key");
  488.    }
  489.    else
  490.       emacs_error("no tzcfodder key");
  491.  
  492. /*
  493.      printf("parsed: ");
  494.      prin(stdout, v);
  495.      fputc('\n', stdout);
  496.  */
  497. #if 0
  498.      if (destructure(pattern, v)) {
  499.         printf("match_data = ");
  500.         prin(stdout, match_data);
  501.         fputc('\n', stdout);
  502.      }
  503.      else {
  504.         printf("destructure failed\n");
  505.      }
  506. #endif
  507. }
  508.  
  509. void
  510. read_emacs()
  511. {
  512.    int ret;
  513.    char *c;
  514.    Value *v;
  515.    
  516.    /* if the buffer is full, expand it*/
  517.    if (globals->ebufptr - globals->ebuf >= globals->ebufsiz) {
  518.       char *new_buf;
  519.       globals->ebufsiz *= 2;
  520.       new_buf = (char *) malloc(globals->ebufsiz);
  521.       bcopy(globals->ebuf, new_buf, globals->ebufptr - globals->ebuf);
  522.       globals->ebufptr = new_buf + (globals->ebufptr - globals->ebuf);
  523.       free(globals->ebuf);
  524.       globals->ebuf = new_buf;
  525.    }
  526.  
  527.    /* read up to size of buffer */
  528.    ret = read(0, globals->ebufptr,
  529.           globals->ebufsiz - (globals->ebufptr - globals->ebuf));
  530.    if (ret < 0) {
  531.       if (errno != EWOULDBLOCK) {
  532.      perror("reading from emacs");
  533.      bail(1);
  534.       }
  535.    }
  536.    else
  537.       globals->ebufptr += ret;
  538.  
  539.    /* parse & process all available input from emacs */
  540.    do {
  541.       ret = parse(globals->ebufptr - globals->ebuf,
  542.           globals->ebuf, &v);
  543.       if (ret > 0) {
  544.      bcopy(globals->ebuf + ret, globals->ebuf,
  545.            (globals->ebufptr - globals->ebuf) - ret);
  546.      globals->ebufptr -= ret;
  547.  
  548.      process_sexp(v);
  549.  
  550.      free_value(v);
  551.       }
  552.    } while (ret > 0);
  553.    
  554. }
  555.  
  556. char *time_str()
  557. {
  558.     char *now_name;
  559.     time_t now;
  560.     now = time(0);
  561.     now_name = ctime(&now);
  562.     now_name[24] = '\0';    /* dump newline at end */
  563.     return(now_name);
  564. }
  565.  
  566. void say_hi() {
  567.    printf("; tzc is free software and you are welcome to distribute copies\n");
  568.    printf("; of it under certain conditions; see the source code for details.\n");
  569.    printf("; Copyright (C) 1992, 1993 Scott Draves\n");
  570.    printf("; Started: %s\n\n", time_str());
  571.    fflush(stdout);
  572. }
  573.  
  574. void
  575. setup(int use_zctl)
  576. {
  577.    /* set stdin up for nonblocking reads, saving previous flags so we can */
  578.    /* restore them later */
  579.    if (-1 == (globals->stdin_flags = fcntl(0, F_GETFL, 0))) {
  580.       perror("fcntl(0, F_GETFL)");
  581.       exit(1);
  582.    }
  583.    if (-1 == (fcntl(0, F_SETFL, globals->stdin_flags | FNDELAY))) {
  584.       perror("fcntl(0, F_SETFL)");
  585.       bail(1);
  586.    }
  587.  
  588.    /* if there were an easy way to block-buffer stdout
  589.       i'd do it here */
  590.  
  591.    check(ZInitialize(), "ZInitialize");
  592.    globals->port = 0;
  593.    check(ZOpenPort(&globals->port), "ZOpenPort");
  594.  
  595.    if ((globals->zfd= ZGetFD()) < 0) {
  596.       perror("ZGetFD");
  597.       bail(1);
  598.    }
  599.  
  600.    if (use_zctl)
  601.       subscribe_with_zctl();
  602.    else
  603.       subscribe();
  604.  
  605.    globals->ebufsiz = 256;
  606.    globals->ebuf = (char *) malloc(globals->ebufsiz);
  607.    globals->ebufptr = globals->ebuf;
  608.  
  609.    globals->pending_replies = NULL;
  610.  
  611.    globals->constants.sym_class = vmake_symbol_c("class");
  612.    globals->constants.sym_instance = vmake_symbol_c("instance");
  613.    globals->constants.sym_recipients = vmake_symbol_c("recipients");
  614.    globals->constants.sym_message = vmake_symbol_c("message");
  615.    globals->constants.sym_send = vmake_symbol_c("send");
  616.    globals->constants.sym_tzcfodder = vmake_symbol_c("tzcfodder");
  617.    globals->constants.sym_auth = vmake_symbol_c("auth");
  618. }
  619.  
  620. void
  621. await_input(int *emacs_in, int *zephyr_in)
  622. {
  623.    *emacs_in = *zephyr_in = 0;
  624.  
  625.    if (ZPending() > 0)
  626.       *zephyr_in = 1;
  627.  
  628.     while (!(*emacs_in || *zephyr_in)) {
  629.       fd_set fdset;
  630.  
  631.       FD_ZERO(&fdset);
  632.       FD_SET(0, &fdset);        /* stdin from emacs */
  633.       FD_SET(globals->zfd, &fdset);    /* Zephyr incoming */
  634.  
  635.       if (select(globals->zfd + 1, &fdset, NULL, NULL, NULL) < 0) {
  636.      perror("select");
  637.      break;
  638.       }
  639.       *emacs_in = FD_ISSET(0, &fdset);
  640.       *zephyr_in = FD_ISSET(globals->zfd, &fdset);
  641.       /* this didn't work - we'll just assume that a whole packet
  642.        * will arrive soon after anything shows up on the input
  643.        if (*zephyr_in)
  644.        if (warn(ZPending(), "ZPending") < 1)
  645.        *zephyr_in = 0;
  646.        */
  647.    }
  648. }
  649.  
  650. void
  651. report_zgram(ZNotice_t *notice, int auth)
  652. {
  653.       int i, len, forced_termination;
  654.       char *from_host, *p;
  655.       struct hostent *hst;
  656.       char *now_name;
  657.  
  658.       now_name = time_str();
  659.  
  660.       /* convert IP# of sender into ascii name */
  661.       hst = gethostbyaddr((char *)¬ice->z_sender_addr,
  662.               sizeof(notice->z_sender_addr), AF_INET);
  663.       from_host = hst ? hst->h_name : inet_ntoa(notice->z_sender_addr);
  664.  
  665.       /* abbreviate sender by throwing away realm if it's local */
  666.       p = strchr(notice->z_sender, '@');
  667.       if (p && !strcmp(p+1, ZGetRealm()))
  668.      *p = '\0';
  669.  
  670.       /* ditto for recipient */
  671.       p = strchr(notice->z_recipient, '@');
  672.       if (p && !strcmp(p+1, ZGetRealm()))
  673.      *p = '\0';
  674.  
  675.       emacs_put_open();
  676.       emacs_put_sym("tzcspew", "message");
  677.       emacs_put_sym("kind", kind_string(notice->z_kind));
  678.       emacs_put_sym("class", notice->z_class);
  679.       emacs_put_str("instance", notice->z_class_inst);
  680.       emacs_put_sym("opcode", notice->z_opcode);
  681.       emacs_put_str("sender", notice->z_sender);
  682.       emacs_put_str("recipient", notice->z_recipient);
  683.       emacs_put_int("port", notice->z_port);
  684.       emacs_put_sym("auth", auth_string(auth));
  685.       emacs_put_str("time", now_name);
  686.       emacs_put_str("fromhost", from_host);
  687.  
  688.       emacs_put_pair_open("message");
  689.       putc('(', stdout);
  690.       p = notice->z_message;
  691.       len = notice->z_message_len;
  692.       forced_termination = 0;
  693.       if (p[len-1]) {
  694.      /* force null termination */
  695.      /* this code has not been tested */
  696.      p = (char *) malloc(len+1);
  697.      bcopy(notice->z_message, p, len);
  698.      p[len] = '\0';
  699.      len += 1;
  700.      forced_termination = 1;
  701.       }
  702.       for (i = 0; i < len; i++) {
  703.      fputqs(p+i, stdout);
  704.      putc(' ', stdout);
  705.      i += strlen(p+i);
  706.       }
  707.       putc(')', stdout);
  708.       emacs_put_pair_close();
  709.  
  710.       if (forced_termination) {
  711.      emacs_put_sym("forced-termination", "t");
  712.      free(p);
  713.       }
  714.  
  715.       emacs_put_close();
  716.  
  717. #if 0
  718.       fputs("(kind . ", stdout);
  719.       fputs(kind_string(notice->z_kind), stdout);
  720.       fputs(") (class . ", stdout);
  721.       fputqqs(notice->z_class, stdout);
  722.       fputs(") (instance . ", stdout);
  723.       fputqs(notice->z_class_inst, stdout);
  724.       fputs(") (opcode . ", stdout);
  725.       fputqqs(notice->z_opcode, stdout);
  726.       fputs(") (sender . ", stdout);
  727.       fputqs(notice->z_sender, stdout);
  728.       fputs(") (recipient . ", stdout);
  729.       if (notice->z_recipient[0])
  730.      fputqs(notice->z_recipient, stdout);
  731.       else
  732.      fputs("nil", stdout);
  733.       fprintf(stdout,
  734.           ") (port . %d) (auth . %s) (time . \"%s\") (fromhost . ",
  735.           notice->z_port, auth_string(auth), now_name);
  736.       fputqs(from_host, stdout);
  737.       fputs(") (message . (", stdout);
  738. #endif
  739. }
  740.  
  741. void
  742. process_reply(ZNotice_t *notice, int auth)
  743. {
  744.    PendingReply **link;
  745.    PendingReply *reply = NULL;
  746.  
  747.    /* remove from the pending reply list */
  748.    link = &globals->pending_replies;
  749.    while (*link != NULL) {
  750.       if (ZCompareUID(¬ice->z_uid, &(*link)->uid)) {
  751.      reply = *link;
  752.      *link = (*link)->next;
  753.      break;
  754.       }
  755.       link = &(*link)->next;
  756.    }
  757.  
  758.    if (NULL == reply)
  759.       /* who called us, anyway ? */
  760.       return;
  761.  
  762.    /* genuine reply, deal with it */
  763.    switch (notice->z_kind) {
  764.     case SERVACK:
  765.       if (!strcmp(notice->z_message, ZSRVACK_SENT)) {
  766.      /* tell emacs message sent successfully */
  767.      emacs_put_open();
  768.      emacs_put_sym("tzcspew", "sent");
  769.      emacs_put_str_str("to", reply->instance, reply->recipient);
  770.      emacs_put_close();
  771.       }
  772.       else if (!strcmp(notice->z_message, ZSRVACK_NOTSENT)) {
  773.      emacs_put_open();
  774.      emacs_put_sym("tzcspew", "not-sent");
  775.      emacs_put_str_str("to", reply->instance, reply->recipient);
  776.      emacs_put_close();
  777.       }
  778.       else {
  779.      /* oh nooooo! */
  780.      emacs_put_open();
  781.      emacs_put_sym("tzcspew", "error");
  782.      emacs_put_str("message", "bad reply code");
  783.      emacs_put_int("code", notice->z_kind);
  784.      emacs_put_close();
  785.       }
  786.       break;
  787.  
  788.     case SERVNAK:
  789.       /* tell emacs authorization failure */
  790.       emacs_error("authorization failure");
  791.       break;
  792.  
  793.     default:
  794.       /* server failure when receiving acknowledgement */
  795.       emacs_error("server failure when processing acknowledgement");
  796.       break;
  797.    }
  798.  
  799.    free(reply->instance);
  800.    free(reply->recipient);
  801.    free(reply);
  802. }
  803.  
  804. void
  805. read_zephyr()
  806. {
  807.    ZNotice_t notice;
  808.    struct sockaddr_in from;
  809.    int auth;
  810.    int expected_reply;
  811.    PendingReply *reply;
  812.  
  813.    while (ZPending() > 0) {
  814.       expected_reply = 0;
  815.  
  816.       warn(ZReceiveNotice(¬ice, &from), "ZReceiveNotice");
  817.       auth = ZCheckAuthentication(¬ice, &from);
  818.  
  819.       if (notice.z_kind != ACKED) {
  820.      /* check to see whether this is an ack/nak or whether it is new
  821.       * incoming info */
  822.      reply = globals->pending_replies;
  823.      while (reply != NULL) {
  824.         if (ZCompareUID(¬ice.z_uid, &reply->uid)) {
  825.            expected_reply = 1;
  826.            break;
  827.         }
  828.         reply = reply->next;
  829.      }
  830.       }
  831.  
  832.       if (expected_reply)
  833.      process_reply(¬ice, auth);
  834.       else
  835.      report_zgram(¬ice, auth);
  836.  
  837.       (void) ZFreeNotice(¬ice);
  838.    }
  839. }
  840.  
  841. void restart_tzc(int sig) {
  842.    /* unblock sigusr1 */
  843.    int mask = sigsetmask(0);
  844.    (void) sigsetmask(mask & ~sigmask(SIGUSR1) & ~sigmask(SIGALRM));
  845.    execvp(globals->argv[0], globals->argv);
  846. }
  847.  
  848. void reset_alarm(long alarm_rate) {
  849.    if (alarm_rate > 0) {
  850.       struct itimerval value;
  851.       value.it_interval.tv_usec = 0;
  852.       value.it_interval.tv_sec = alarm_rate;
  853.       value.it_value.tv_usec = 0;
  854.       value.it_value.tv_sec = alarm_rate;
  855.       setitimer(ITIMER_REAL, &value, (struct itimerval *)0);
  856.    }
  857. }
  858.  
  859. int main(int argc, char **argv) {
  860.    char *program;
  861.    int use_zctl = 0, sw;
  862.    extern char *optarg;
  863.    char *location = NULL;
  864.    long restart_rate = 0;    /* In seconds; zero disables auto restart */
  865.  
  866.    globals = &global_storage;
  867.    program = strrchr(argv[0], '/');
  868.    if (program == NULL)
  869.       program = argv[0];
  870.    else
  871.       program++;
  872.    globals->program = program;
  873.    globals->argc = argc;
  874.    globals->argv = argv;
  875.  
  876.    say_hi();
  877.  
  878.    signal(SIGUSR1, restart_tzc);
  879.    signal(SIGALRM, restart_tzc);
  880.  
  881.    while ((sw = getopt(argc, argv, "sa:e:p:")) != EOF)
  882.       switch (sw) {
  883.        case 's':
  884.      use_zctl = 1;
  885.      break;
  886.        case 'e':
  887.      location = optarg;
  888.      break;
  889.        case 'a':
  890.      restart_rate = atoi(optarg);
  891.      break;
  892.        case 'p':
  893.      if (1) {
  894.         FILE *f = fopen(optarg, "w");
  895.         if (f) {
  896.            fprintf(f, "%d\n", getpid());
  897.            fclose(f);
  898.         } else {
  899.            perror(optarg);
  900.            exit(1);
  901.         }
  902.      }
  903.      break;
  904.        case '?':
  905.        default:
  906.      usage();
  907.      bail(1);
  908.       }
  909.  
  910.    setup(use_zctl);
  911.    if (location != NULL) {
  912.       check(ZSetLocation(location), "ZSetLocation");
  913.    }
  914.  
  915.    reset_alarm(restart_rate);
  916.  
  917.    while (1) {
  918.       int emacs_in, zephyr_in;
  919.  
  920.       await_input(&emacs_in, &zephyr_in);
  921.  
  922.       /* if emacs input ready, add it to a buffer */
  923.       if (emacs_in)
  924.      read_emacs();
  925.  
  926.       if (zephyr_in) {
  927.      reset_alarm(restart_rate);
  928.      read_zephyr();
  929.       }
  930.    }
  931. }
  932.