home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.lbl.gov / 2014.05.ftp.ee.lbl.gov.tar / ftp.ee.lbl.gov / acld-1.11.tar.gz / acld-1.11.tar / acld-1.11 / acld.c < prev    next >
C/C++ Source or Header  |  2012-02-07  |  23KB  |  1,056 lines

  1. /*
  2.  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that: (1) source code distributions
  7.  * retain the above copyright notice and this paragraph in its entirety, (2)
  8.  * distributions including binary code include the above copyright notice and
  9.  * this paragraph in its entirety in the documentation or other materials
  10.  * provided with the distribution, and (3) all advertising materials mentioning
  11.  * features or use of this software display the following acknowledgement:
  12.  * ``This product includes software developed by the University of California,
  13.  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  14.  * the University nor the names of its contributors may be used to endorse
  15.  * or promote products derived from this software without specific prior
  16.  * written permission.
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  18.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  19.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21.  
  22. #ifndef lint
  23. static const char copyright[] =
  24.     "@(#) Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012\n\
  25. The Regents of the University of California.  All rights reserved.\n";
  26. static const char rcsid[] =
  27.     "@(#) $Id: acld.c 806 2012-02-08 03:40:06Z leres $ (LBL)";
  28. #endif
  29.  
  30. /*
  31.  * acld - manage ACLs on a router
  32.  */
  33.  
  34. #include <sys/types.h>
  35. #include <sys/time.h>
  36. #include <sys/socket.h>
  37. #include <sys/stat.h>
  38. #include <sys/wait.h>
  39.  
  40. #include <netinet/in.h>
  41.  
  42. #include <arpa/inet.h>
  43.  
  44. #include <errno.h>
  45. #include <fcntl.h>
  46. #include <signal.h>
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <sysexits.h>
  51. #include <syslog.h>
  52. #include <time.h>
  53. #include <unistd.h>
  54.  
  55. #include "gnuc.h"
  56. #ifdef HAVE_OS_PROTO_H
  57. #include "os-proto.h"
  58. #endif
  59.  
  60. #include "acld.h"
  61. #include "cf.h"
  62. #ifdef HAVE_CFORCE
  63. #include "cforcep.h"
  64. #endif
  65. #include "child.h"
  66. #include "server.h"
  67. #include "setsignal.h"
  68.  
  69. #define MAX(x, y) ((x) > (y) ? (x) : (y))
  70.  
  71. /* Globals */
  72. int debug;            /* turn on debugging and don't fork */
  73. int verbose;            /* not really used for anything! */
  74. int reload;            /* kill child and reload config */
  75. int foreground;
  76. const char *prog;
  77. const char *configfn = "/usr/local/etc/acld.conf";
  78. const char *logfile;
  79. FILE *lf;
  80.  
  81. struct s2v astate2str[] = {
  82.     { "connected",        ASTATE_CONNECTED },
  83.     { "loggedin",        ASTATE_LOGGEDIN },
  84.     { "notconnected",    ASTATE_NOTCONNECTED },
  85.     { "readacl",        ASTATE_READACL },
  86.     { "readerror",        ASTATE_READERROR },
  87.     { "readresponse",    ASTATE_READRESPONSE },
  88.     { "readroute",        ASTATE_READROUTE },
  89.     { "sentattr",        ASTATE_SENTATTR },
  90.     { "sentlistacl",    ASTATE_SENTLISTACL },
  91.     { "sentlistroute",    ASTATE_SENTLISTROUTE },
  92.     { "sentlogin",        ASTATE_SENTLOGIN },
  93.     { NULL,            0 }
  94. };
  95.  
  96. struct s2v rstate2str[] = {
  97.     { "child",        RSTATE_CHILD },
  98.     { "done",        RSTATE_DONE },
  99.     { "pending",        RSTATE_PENDING },
  100.     { "readcomment",    RSTATE_READCOMMENT },
  101.     { "readresponse",    RSTATE_READRESPONSE },
  102.     { NULL,            0 }
  103. };
  104.  
  105. /* Locals */
  106. static struct state state;
  107. static int backlog = 16;        /* listen() backlog */
  108. static int sawterm;
  109.  
  110. static const char *pidfile = "/var/run/acld/acld.pid";
  111.  
  112. /* Externs */
  113. extern int optind;
  114. extern int opterr;
  115. extern char *optarg;
  116.  
  117. /* Forwards */
  118. int expect(struct state *);
  119. int main(int, char **);
  120. void handleclientreqs(struct state *);
  121. void initiateclient(struct state *, int, enum clienttype, int);
  122. int opensocket(struct state *, struct addr *, u_short);
  123. int reapdeadclients(struct state *);
  124. void settimeout(struct state *);
  125. RETSIGTYPE sigchild(int);
  126. RETSIGTYPE sighup(int);
  127. #ifdef notdef
  128. RETSIGTYPE sigpipe(int);
  129. #endif
  130. RETSIGTYPE sigterm(int);
  131. int update_fd_set(struct state *, fd_set *, fd_set *);
  132. __dead void usage(void) __attribute__((noreturn));
  133.  
  134. int
  135. main(int argc, char **argv)
  136. {
  137.     int n, nfds, op, done;
  138. #ifdef HAVE_BRO_INIT
  139.     int usessl;
  140. #endif
  141.     char *cp;
  142.     time_t t, idlet;
  143.     FILE *fp;
  144.     ssize_t cc;
  145.     struct req *rp;
  146.     struct state *sp;
  147.     struct cf *cf;
  148.     struct client *cl, *nextcl;
  149.     fd_set readfds, writefds;
  150.     struct stat sbuf;
  151.  
  152.     if (argv[0] == NULL)
  153.         prog = "acld";
  154.     else if ((cp = strrchr(argv[0], '/')) != NULL)
  155.         prog = cp + 1;
  156.     else
  157.         prog = argv[0];
  158.  
  159.     openlog(prog, 0, LOG_DAEMON);
  160.  
  161.     opterr = 0;
  162.     while ((op = getopt(argc, argv, "c:dDfo:P:v")) != EOF)
  163.         switch (op) {
  164.  
  165.         case 'c':
  166.             configfn = optarg;
  167.             break;
  168.  
  169.         case 'd':
  170.             ++debug;
  171.             break;
  172.  
  173. #ifdef HAVE_BROCCOLI
  174.         case 'D':
  175.             /* Undocumented */
  176.             ++bro_debug_messages;
  177.             break;
  178. #endif
  179.  
  180.         case 'f':
  181.             ++foreground;
  182.             break;
  183.  
  184.         case 'o':
  185.             logfile = optarg;
  186.             break;
  187.  
  188.         case 'P':
  189.             pidfile = optarg;
  190.             break;
  191.  
  192.         case 'v':
  193.             ++verbose;
  194.             break;
  195.  
  196.         default:
  197.             usage();
  198.             /* NOTREACHED */
  199.         }
  200.  
  201.     if (argc != optind)
  202.         usage();
  203.  
  204.     if (logfile != NULL)
  205.         checklf();
  206.     else
  207.         lf = stderr;
  208.  
  209.     if (!foreground && daemon(0, 1) < 0) {
  210.         lg(LOG_ERR, "daemon: %s", strerror(errno));
  211.         exit(EX_OSERR);
  212.     }
  213.  
  214.     /* Update pidfile */
  215.     fp = fopen(pidfile, "w");
  216.     if (fp == NULL)
  217.         lg(LOG_ERR, "daemon: %s: %s", pidfile, strerror(errno));
  218.     else {
  219.         fprintf(fp, "%d\n", (int)getpid());
  220.         fclose(fp);
  221.     }
  222.  
  223.     lg(LOG_INFO, "version %s starting", version);
  224.  
  225.     (void)setsignal(SIGTERM, sigterm);
  226.     (void)setsignal(SIGINT, sigterm);
  227.     (void)setsignal(SIGHUP, sighup);
  228.     (void)setsignal(SIGCHLD, sigchild);
  229.     (void)setsignal(SIGPIPE, SIG_IGN);
  230.  
  231.     sp = &state;
  232.     sp->state = ASTATE_NOTCONNECTED;
  233.     sp->rfd = -1;
  234.     sp->wfd = -1;
  235.     sp->s = -1;
  236.     sp->ro = -1;
  237.     sp->web = -1;
  238. #ifdef HAVE_BROCCOLI
  239.     sp->b = -1;
  240.     // sp->b_ro = -1;
  241.     // sp->b_web = -1;
  242. #endif
  243.     reload = 1;
  244.     idlet = time(NULL);
  245.     sp->f_ayt = serverayt;
  246.     sp->f_compact = servercompact;
  247.     sp->f_droprestore = childdroprestore;
  248.     sp->f_kill = childkill;
  249.     sp->f_listacl = childlistacl;
  250.     sp->f_listroute = childlistroute;
  251.     sp->f_login = childlogin;
  252.     sp->f_nullzero = childnullzero;
  253.     sp->f_send = childsend;
  254.     sp->f_sendattr = childsendattr;
  255.     sp->f_sync = serversync;
  256.  
  257.     /* Initial config file parsing */
  258.     sp->cf = parsecf(configfn);
  259.  
  260. #ifdef HAVE_BRO_INIT
  261.     /* Broccoli */
  262.     if (sp->cf->c_portbro > 0) {
  263.         if (!bro_init(NULL)) {
  264.             lg(LOG_ERR, "Failed to initialize broccoli; exiting");
  265.             exit(EX_CONFIG);
  266.         }
  267.         usessl = 0;
  268.         if (!bro_conf_get_int("/broccoli/use_ssl", &usessl))
  269.             usessl = 0;
  270.         lg(LOG_DEBUG, "broinit: %susing SSL", usessl ? "" : "not ");
  271.     }
  272. #endif
  273.  
  274. #ifdef HAVE_CFORCE
  275.     /* cForce */
  276.     if (sp->cf->c_cforceaddr != NULL)
  277.         cforceinit(sp);
  278. #endif
  279.  
  280.     /* nullzero routes */
  281.     routeinit(sp);
  282.  
  283.     for (;;) {
  284.         if (sawterm) {
  285.             lg(LOG_NOTICE, "exiting");
  286.             /* Explicitly terminate the child */
  287.             if (sp->pid > 0 &&
  288.                 kill(sp->pid, SIGTERM) < 0 && errno != ESRCH)
  289.                 lg(LOG_ERR, "kill %d: %s",
  290.                     (int)sp->pid, strerror(errno));
  291.             /* Simulate /bin/sh exit status */
  292.             exit(128 + SIGTERM);
  293.         }
  294.         if (reload) {
  295.             /* Terminate the child */
  296.             if (sp->wfd >= 0 && sp->state == ASTATE_LOGGEDIN)
  297.                 (sp->f_kill)(sp);
  298.  
  299.             /* Reload config file if it's changed */
  300.             if (sp->cf != NULL) {
  301.                 /* Get the config file mod time */
  302.                 if (stat(configfn, &sbuf) < 0)
  303.                     lg(LOG_ERR, "stat %s: %s",
  304.                         configfn, strerror(errno));
  305.                 else {
  306.                     t = (sbuf.st_mtime > sbuf.st_ctime) ?
  307.                         sbuf.st_mtime : sbuf.st_ctime;
  308.                     if (t > sp->cf->c_time) {
  309.                         lg(LOG_INFO, "reloading %s",
  310.                             configfn);
  311.                         freecf(sp->cf);
  312.                         routefree(sp);
  313.                         sp->cf = NULL;
  314.                     }
  315.                 }
  316.             }
  317.             reload = 0;
  318.             timerset(&sp->t_login, 1);
  319.         }
  320.  
  321.         /* Read the config file */
  322.         if (sp->cf == NULL && (sp->cf = parsecf(configfn)) == NULL)
  323.             exit(EX_CONFIG);
  324.  
  325.         /* Create new expect process */
  326.         if (sp->cf->c_script != NULL && sp->wfd < 0 && expect(sp) < 0) {
  327.             lg(LOG_ERR, "expect child: %s",
  328.                 strerror(errno));
  329.             exit(EX_OSERR);
  330.         }
  331.  
  332.         /* Internal processing */
  333.         if (
  334. #ifdef HAVE_CFORCE
  335.             sp->cf->c_cforceaddr != NULL ||
  336. #endif
  337.             sp->wfd >= 0) {
  338.             switch (sp->state) {
  339.  
  340.             case ASTATE_CONNECTED:
  341. #ifdef HAVE_CFORCE
  342.             case ASTATE_NOTCONNECTED:
  343. #endif
  344.                 if (timerdue(&sp->t_login))
  345.                     (sp->f_login)(sp);
  346.                 break;
  347.  
  348.             case ASTATE_LOGGEDIN:
  349.                 if (!sp->sentattrs)
  350.                     (sp->f_sendattr)(sp);
  351.                 else if (!sp->listedallacls)
  352.                     (sp->f_listacl)(sp);
  353.                 else if (!sp->listedroutes)
  354.                     (sp->f_listroute)(sp);
  355.                 break;
  356.  
  357.             default:
  358.                 break;
  359.             }
  360.         }
  361.  
  362.         /* Open the listen socket */
  363.         cf = sp->cf;
  364.         if (sp->s < 0)
  365.             sp->s = opensocket(sp, &cf->c_bindaddr, cf->c_port);
  366.  
  367.         /* Optional read/only request socket */
  368.         if (sp->ro < 0 && cf->c_portro > 0)
  369.             sp->ro = opensocket(sp, &cf->c_bindaddr, cf->c_portro);
  370.  
  371.         /* Optional web registration request socket */
  372.         if (sp->web < 0 && cf->c_portweb > 0)
  373.             sp->web = opensocket(sp, &cf->c_bindaddr,
  374.                 cf->c_portweb);
  375.  
  376. #ifdef HAVE_BROCCOLI
  377.         /* Open the broccoli socket */
  378.         if (sp->b < 0 && cf->c_portbro > 0)
  379.             sp->b = opensocket(sp, &cf->c_broaddr, cf->c_portbro);
  380. #endif
  381.  
  382.         /* Update fd_sets */
  383.         nfds = update_fd_set(sp, &readfds, &writefds);
  384.  
  385.         /* Wait for stuff to do (or timeout) */
  386.         settimeout(sp);
  387.         n = select(nfds, &readfds, &writefds, NULL, &sp->timeout);
  388.         if (n < 0) {
  389.             /* Don't choke if we get ptraced */
  390.             if (errno == EINTR)
  391.                 continue;
  392.             lg(LOG_ERR, "select: %s", strerror(errno));
  393.             /*
  394.              * There's a race between the client and
  395.              * our call to select(). It's possible for
  396.              * the client to close his connection just
  397.              * before we call select(). When this happens
  398.              * we get EBADF. If we can find the client
  399.              * and clean him up, we can continue on.
  400.              * Otherwise, exit.
  401.              */
  402.             if (errno == EBADF && reapdeadclients(sp) > 0)
  403.                 continue;
  404.             exit(EX_OSERR);
  405.         }
  406.  
  407.         /* Time for maintenance */
  408.         // XXX this is broken
  409.         // t = time(NULL);
  410.         if (n == 0) {
  411.             /* Did the child fall asleep? */
  412.             if (((cf->c_expect != NULL && sp->wfd < 0) ||
  413.                 sp->state != ASTATE_LOGGEDIN) &&
  414.                 timerdue(&sp->t_ayt)) {
  415.                 (sp->f_kill)(sp);
  416.                 continue;
  417.             }
  418.  
  419.             /* Only do maintenance if there's nothing else to do */
  420.             if (sp->req == NULL && sp->reqclient == NULL) {
  421.                 idlet = t;
  422.                 if (timerdue(&sp->t_compact))
  423.                     (sp->f_compact)(sp);
  424.                 else if (timerdue(&sp->t_sync))
  425.                     (sp->f_sync)(sp);
  426.                 else if (timerdue(&sp->t_ayt))
  427.                     (sp->f_ayt)(sp);
  428.             }
  429.         }
  430.  
  431. #ifdef notdef
  432.         /* Make sure we're occasionally handling idle tasks */
  433.         if ((t - idlet) > SELECT_SECS) {
  434.             lg(LOG_ERR, "idle failure; aborting");
  435.             abort();
  436.         }
  437. #endif
  438.  
  439.         /* Did any clients say anything? */
  440.         /* XXX limit about of client data to throttle them? */
  441.         for (cl = sp->clients; cl != NULL; cl = nextcl) {
  442.             /* Ok to free clients inside this loop */
  443.             nextcl = cl->next;
  444.             if (cl->c >= 0 && FD_ISSET(cl->c, &readfds)) {
  445. #ifdef HAVE_BROCCOLI
  446.                 if (cl->broccoli && cl->bc != NULL) {
  447.                     if (!broinput(cl)) {
  448.                         lg(LOG_ERR,
  449.                             "client #%d broccoli: exit",
  450.                             cl->n);
  451.                         freeclient(sp, cl);
  452.                     }
  453.                     continue;
  454.                 }
  455. #endif
  456.                 cc = ioread(cl->c, &cl->rbuf);
  457.                 if (cc < 0) {
  458.                     lg(LOG_ERR, "client #%d read: %s",
  459.                         cl->n, strerror(errno));
  460.                     freeclient(sp, cl);
  461.                     continue;
  462.                 }
  463.                 if (cc == 0) {
  464.                     lg(LOG_INFO,
  465.                         "client #%d disconnected", cl->n);
  466.                     freeclient(sp, cl);
  467.                     continue;
  468.                 }
  469.             }
  470.         }
  471.  
  472.         /* Check for new clients */
  473.         if (sp->s >= 0 && FD_ISSET(sp->s, &readfds))
  474.             initiateclient(sp, sp->s, CTYPE_PRIV, 0);
  475.         if (sp->ro >= 0 && FD_ISSET(sp->ro, &readfds))
  476.             initiateclient(sp, sp->ro, CTYPE_RO, 0);
  477.         if (sp->web >= 0 && FD_ISSET(sp->web, &readfds))
  478.             initiateclient(sp, sp->web, CTYPE_WEB, 0);
  479. #ifdef HAVE_BROCCOLI
  480.         if (sp->b >= 0 && FD_ISSET(sp->b, &readfds))
  481.             initiateclient(sp, sp->b, CTYPE_PRIV, 1);
  482. #endif
  483.  
  484.         /* Did the child say something? */
  485.         if (sp->rfd >= 0 && FD_ISSET(sp->rfd, &readfds)) {
  486.             cc = ioread(sp->rfd, &sp->rbuf);
  487.             if (cc < 0) {
  488.                 lg(LOG_ERR, "child read: %s", strerror(errno));
  489.                 continue;
  490.             }
  491.             if (cc == 0) {
  492.                 lg(LOG_DEBUG, "child disconnect");
  493.                 sp->state = ASTATE_NOTCONNECTED;
  494.                 sp->cmd = NULL;
  495.                 (sp->f_kill)(sp);
  496.                 continue;
  497.             }
  498.         }
  499.  
  500.         /* Process child input */
  501.         done = 0;
  502.         while (sp->rbuf.len > 0 && !done) {
  503.             cl = sp->reqclient;
  504.             done = childinput(sp, cl);
  505.  
  506.             /* Remove server request from list if completed */
  507.             if (sp->req != NULL && sp->req->state == RSTATE_DONE) {
  508.                 /* Remove from request list */
  509.                 rp = sp->req;
  510.                 sp->req = rp->next;
  511.                 rp->next = NULL;
  512.                 freereq(rp);
  513.                 continue;
  514.             }
  515.  
  516.             /* Remove client request from list if completed */
  517.             if (cl != NULL && cl->req != NULL &&
  518.                 cl->req->state == RSTATE_DONE) {
  519.                 /* Remove from request list */
  520.                 rp = cl->req;
  521.                 cl->req = rp->next;
  522.                 rp->next = NULL;
  523.                 freereq(rp);
  524.                 sp->reqclient = NULL;
  525.                 continue;
  526.             }
  527.         }
  528.  
  529.         /*
  530.          * Process input from each client input, possibly
  531.          * appending a request to the end of the client's
  532.          * queue.
  533.          */
  534.         for (cl = sp->clients; cl != NULL; cl = cl->next)
  535.             while (iohaveline(&cl->rbuf))
  536.                 clientinput(sp, cl);
  537.  
  538.         /* Reset keepalive if necessary */
  539.         if (sp->state == ASTATE_LOGGEDIN &&
  540.             cf->c_ayt_secs > 0 && timercheck(&sp->t_ayt) < 0)
  541.             timerset(&sp->t_ayt, cf->c_ayt_secs);
  542.  
  543.         /* Handle server requests */
  544.         while (sp->state == ASTATE_LOGGEDIN &&
  545.             (rp = sp->req) != NULL && rp->state == RSTATE_PENDING) {
  546.             serverprocess(sp, rp);
  547.             if (rp->state == RSTATE_DONE) {
  548.                 /* Remove from request list */
  549.                 rp = sp->req;
  550.                 sp->req = rp->next;
  551.                 rp->next = NULL;
  552.                 freereq(rp);
  553.             }
  554.         }
  555.  
  556.         /* Handle client requests */
  557.         if (sp->req == NULL && sp->reqclient == NULL)
  558.             handleclientreqs(sp);
  559.  
  560.         /* Drain client output */
  561.         for (cl = sp->clients; cl != NULL; cl = nextcl) {
  562.             /* Ok to free clients inside this loop */
  563.             nextcl = cl->next;
  564.             if (cl->c >= 0 && FD_ISSET(cl->c, &writefds) &&
  565.                 cl->wbuf.len > 0) {
  566. #ifdef HAVE_BROCCOLI
  567.                 if (cl->broccoli) {
  568.                     lg(LOG_ERR, "broclient output");
  569.                     abort();
  570.                 }
  571. #endif
  572.                 if (iowrite(cl->c, &cl->wbuf) < 0) {
  573.                     lg(LOG_ERR, "client #%d write: %s",
  574.                         cl->n, strerror(errno));
  575.                     freeclient(sp, cl);
  576.                     continue;
  577.                 }
  578.                 /* If this client is done free him */
  579.                 if (cl->close && cl->wbuf.len <= 0) {
  580.                     freeclient(sp, cl);
  581.                     continue;
  582.                 }
  583.             }
  584.         }
  585.     }
  586. }
  587.  
  588. struct state *
  589. getstate(void)
  590. {
  591.     return (&state);
  592. }
  593.  
  594. /*
  595.  * Round robin to give each client a fair chance. Once a request
  596.  * blocks on the client, move that client to the end of the client
  597.  * list.
  598.  */
  599. void
  600. handleclientreqs(struct state *sp)
  601. {
  602.     struct client *cl, *cl2, *lastcl;
  603.     struct req *rp;
  604.  
  605.     lastcl = NULL;
  606.     for (cl = sp->clients; cl != NULL; cl = cl->next) {
  607.         while (cl->req != NULL && cl->req->state == RSTATE_PENDING) {
  608.             sp->reqclient = cl;
  609.             clientprocess(sp, cl, cl->req);
  610.  
  611.             /* If we blocked, move client to the end and bail */
  612.             if (cl->req->state != RSTATE_DONE) {
  613.                 if (cl->next != NULL) {
  614.                     if (lastcl != NULL)
  615.                         lastcl->next = cl->next;
  616.                     else
  617.                         sp->clients = cl->next;
  618.                     cl2 = cl->next;
  619.                     while (cl2->next != NULL)
  620.                         cl2 = cl2->next;
  621.                     cl2->next = cl;
  622.                     cl->next = NULL;
  623.                 }
  624.                 return;
  625.             }
  626.  
  627.             /* Remove this request from the list */
  628.             rp = cl->req;
  629.             cl->req = rp->next;
  630.             rp->next = NULL;
  631.             freereq(rp);
  632.             sp->reqclient = NULL;
  633.             /* Look at next request for this client */
  634.         }
  635.         lastcl = cl;
  636.     }
  637. }
  638.  
  639. struct req hello = {
  640.     NULL,
  641.     REQ_UNKNOWN,
  642.     RSTATE_PENDING,
  643.     RFLAG_CONTINUE,            /* request flags */
  644.     "acld",                /* client command name */
  645.     NULL,                /* acllist pointer */
  646.     NULL,                /* ACL name */
  647.     0,                /* cookie */
  648.     { 0, 0 },            /* arrival timestamp */
  649.     { 0, 0 },            /* completion timestamp */
  650.     { },                /* ACL */
  651.     { },                /* route */
  652.     { 0 },                /* whitelist */
  653.     0,                /* argument count */
  654.     NULL,                /* argument vector */
  655.     NULL,                /* comment */
  656.     { "ready for action\r\n", 0, sizeof(hello.payload.buf) - 1 },
  657. };
  658.  
  659. /* XXX limit number of clients? */
  660. void
  661. initiateclient(struct state *sp, int s, enum clienttype ctype, int broccoli)
  662. {
  663.     int c;
  664.     struct client *cl;
  665.     socklen_t salen;
  666.     u_int16_t port;
  667.     struct sockaddr sa;
  668.     struct addr addr;
  669.  
  670.     memset(&sa, 0, sizeof(sa));
  671.     salen = sizeof(sa);
  672.     c = accept(s, &sa, &salen);
  673.     if (c < 0) {
  674.         lg(LOG_ERR, "accept: %s", strerror(errno));
  675.         if (errno == EBADF)
  676.             exit(EX_OSERR);
  677.         return;
  678.     }
  679.     sa2addr(&sa, &addr, &port);
  680.     nbio(c);
  681.     cl = newclient(sp, c);
  682.     cl->type = ctype;
  683.  
  684.     lg(LOG_DEBUG, "client #%d connect from %s.%hu (%s)",
  685.         cl->n, addr2str(&addr), port, val2str(ctype2str, cl->type));
  686.  
  687. #ifdef HAVE_BROCCOLI
  688.     if (broccoli) {
  689.         ++cl->broccoli;
  690.         if (!broinit(sp, cl))
  691.             freeclient(sp, cl);
  692.         return;
  693.     }
  694. #endif
  695.  
  696.     /* Update completion timestamp */
  697.     getts(&hello.cts);
  698.     clientsend(cl, &hello);
  699. }
  700.  
  701. /* Create the expect process */
  702. int
  703. expect(struct state *sp)
  704. {
  705.     pid_t pid;
  706.     int xerrno;
  707.     int wfds[2], rfds[2];
  708.     char *argv[3], *envp[1];
  709.  
  710.     if (access(sp->cf->c_script, R_OK) < 0) {
  711.         lg(LOG_ERR, "expect: %s: %s",
  712.             sp->cf->c_script, strerror(errno));
  713.         exit(EX_OSFILE);
  714.     }
  715.  
  716.     if (access(sp->cf->c_expect, X_OK) < 0) {
  717.         lg(LOG_ERR, "expect: %s: %s",
  718.             sp->cf->c_expect, strerror(errno));
  719.         exit(EX_OSFILE);
  720.     }
  721.  
  722.     if (pipe(rfds) < 0)
  723.         return (-1);
  724.     if (pipe(wfds) < 0) {
  725.         xerrno = errno;
  726.         (void)close(rfds[0]);
  727.         (void)close(rfds[1]);
  728.         errno = xerrno;
  729.         return (-1);
  730.     }
  731.  
  732.     pid = fork();
  733.     if (pid < 0) {
  734.         xerrno = errno;
  735.         (void)close(rfds[0]);
  736.         (void)close(rfds[1]);
  737.         (void)close(wfds[0]);
  738.         (void)close(wfds[1]);
  739.         errno = xerrno;
  740.         return (-1);
  741.     }
  742.     if (pid == 0) {
  743.         /* Child */
  744.         /* XXX close all descriptors?? */
  745.         /* Setup stdin, stdout and stderr */
  746.         if (rfds[0] != STDIN_FILENO) {
  747.             (void)dup2(rfds[0], STDIN_FILENO);
  748.             (void)close(rfds[0]);
  749.         }
  750.         (void)close(rfds[1]);
  751.  
  752.         if (wfds[1] != STDOUT_FILENO) {
  753.             (void)dup2(wfds[1], STDOUT_FILENO);
  754.             (void)close(wfds[1]);
  755.         }
  756.         (void)dup2(STDOUT_FILENO, STDERR_FILENO);
  757.         (void)close(wfds[0]);
  758.  
  759.         argv[0] = sp->cf->c_expect;
  760.         argv[1] = sp->cf->c_script;
  761.         argv[2] = NULL;
  762.         envp[0] = NULL;
  763.         (void)execve(sp->cf->c_expect, argv, envp);
  764.         lg(LOG_ERR, "execve: %s", strerror(errno));
  765.         exit(EX_OSERR);
  766.     }
  767.  
  768.     /* Parent */
  769.     (void)close(rfds[0]);
  770.     (void)close(wfds[1]);
  771.  
  772.     /* The child's write is our read, etc. */
  773.     sp->rfd = wfds[0];
  774.     sp->wfd = rfds[1];
  775.     sp->pid = pid;
  776.     lg(LOG_DEBUG, "expect: spawed child %d", (int)pid);
  777.     return (0);
  778. }
  779.  
  780. /* Append the new request to end of list */
  781. void
  782. appendreq(struct req **reqp, struct req *rp)
  783. {
  784.     struct req *rp2;
  785.  
  786.     if (*reqp == NULL)
  787.         *reqp = rp;
  788.     else {
  789.         rp2 = *reqp;
  790.         while (rp2->next != NULL)
  791.             rp2 = rp2->next;
  792.         rp2->next = rp;
  793.     }
  794. }
  795.  
  796. void
  797. freereq(struct req *rp)
  798. {
  799.     int i;
  800.     struct state *sp;
  801.     struct acllist *ap;
  802.  
  803.     if (rp == NULL)
  804.         return;
  805.  
  806.     if (rp->next != NULL) {
  807.         lg(LOG_ERR, "freereq: rp->next not NULL");
  808.         exit(EX_SOFTWARE);
  809.     }
  810.     if (rp->aclname != NULL)
  811.         free(rp->aclname);
  812.     if (rp->comment != NULL)
  813.         free(rp->comment);
  814.     if (rp->payload.size > 0 )
  815.         iofree(&rp->payload);
  816.     if (rp->av != NULL)
  817.         freeargv(rp->av);
  818.     if (rp->acl.port1 != NULL)
  819.         free(rp->acl.port1);
  820.     if (rp->acl.port2 != NULL)
  821.         free(rp->acl.port2);
  822.     if (rp->acl.raw != NULL)
  823.         free(rp->acl.raw);
  824.     if (rp->nullzero.raw != NULL)
  825.         free(rp->nullzero.raw);
  826.     free(rp);
  827.  
  828.     /* Clean up compaction state */
  829.     sp = &state;        /* XXX */
  830.     for (i = 0, ap = sp->cf->c_acllist; i < sp->cf->c_acllistlen; ++i, ++ap)
  831.         if (ap->compactreq == rp) {
  832.             ap->compactclient = NULL;
  833.             ap->compactreq = NULL;
  834.             break;
  835.         }
  836. }
  837.  
  838. void
  839. nbio(int s)
  840. {
  841.     int flags;
  842.  
  843.     if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
  844.         lg(LOG_ERR, "nbio: F_GETFL: %s", strerror(errno));
  845.         return;
  846.     }
  847.     flags |= O_NONBLOCK;
  848.     if ((flags = fcntl(s, F_SETFL, flags)) < 0) {
  849.         lg(LOG_ERR, "nbio: F_SETFL: %s", strerror(errno));
  850.         return;
  851.     }
  852. }
  853.  
  854. int
  855. opensocket(struct state *sp, struct addr *addr, u_short port)
  856. {
  857.     int s;
  858.     struct sockaddr sa;
  859.     static int on = 1;
  860.     char buf[64];
  861.  
  862.     addr2sa(addr, port, &sa);
  863.     (void)snprintf(buf, sizeof(buf), "%s.%hu", addr2str(addr), port);
  864.  
  865.     s = socket(PF_INET, SOCK_STREAM, 0);
  866.     if (s < 0) {
  867.         lg(LOG_ERR, "socket: %s: %s", buf, strerror(errno));
  868.         exit(EX_OSERR);
  869.     }
  870.     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
  871.         (char *)&on, sizeof (on)) < 0) {
  872.         lg(LOG_ERR, "SO_REUSEADDR: %s: %s", buf, strerror(errno));
  873.         exit(EX_OSERR);
  874.     }
  875.     if (bind(s, &sa, sizeof(sa)) < 0) {
  876.         lg(LOG_ERR, "bind: %s: %s", buf, strerror(errno));
  877.         exit(EX_OSERR);
  878.     }
  879.     if (listen(s, backlog) < 0) {
  880.         lg(LOG_ERR, "listen: %s: %s", buf, strerror(errno));
  881.         exit(EX_OSERR);
  882.     }
  883.     return (s);
  884. }
  885.  
  886. int
  887. reapdeadclients(struct state *sp)
  888. {
  889.     int numbad;
  890.     struct client *cl, *nextcl;
  891.  
  892.     numbad = 0;
  893.     for (cl = sp->clients; cl != NULL; cl = nextcl) {
  894.         /* Ok to free clients inside this loop */
  895.         nextcl = cl->next;
  896.         if (cl->c < 0)
  897.             continue;
  898.         if (fcntl(cl->c, F_GETFL, 0) >= 0 || errno != EBADF)
  899.             continue;
  900.         lg(LOG_ERR, "reapdeadclients: reaping client #%d", cl->n);
  901.         freeclient(sp, cl);
  902.         ++numbad;
  903.     }
  904.     return (numbad);
  905. }
  906.  
  907. /* Set our select timeout */
  908. void
  909. settimeout(struct state *sp)
  910. {
  911.     int t, secs;
  912.  
  913.     secs = sp->cf->c_select_secs;
  914.     t = timercheck(&sp->t_login);
  915.     if (t >= 0 && secs > t)
  916.         secs = t;
  917.     t = timercheck(&sp->t_sync);
  918.     if (t >= 0 && secs > t)
  919.         secs = t;
  920.     t = timercheck(&sp->t_compact);
  921.     if (t >= 0 && secs > t)
  922.         secs = t;
  923.     t = timercheck(&sp->t_ayt);
  924.     if (t >= 0 && secs > t)
  925.         secs = t;
  926.  
  927.     /* Don't screw around if we have things to do */
  928.     if (sp->state == ASTATE_LOGGEDIN &&
  929.         (!sp->sentattrs || !sp->listedallacls || !sp->listedroutes))
  930.         secs = 0;
  931.  
  932.     /* Always sleep for at least 10ms */
  933.     if (secs > 0) {
  934.         sp->timeout.tv_sec = secs;
  935.         sp->timeout.tv_usec = 0;
  936.     } else {
  937.         sp->timeout.tv_sec = 0;
  938.         sp->timeout.tv_usec = 10 * 1000;
  939.     }
  940. }
  941.  
  942. /* Reap exiting child */
  943. RETSIGTYPE
  944. sigchild(int signo)
  945. {
  946.     pid_t pid;
  947.     DECLWAITSTATUS status;
  948.  
  949.     pid = waitpid(0, &status, WNOHANG);
  950.     return RETSIGVAL;
  951. }
  952.  
  953. RETSIGTYPE
  954. sighup(int signo)
  955. {
  956.  
  957.     ++reload;
  958.     return RETSIGVAL;
  959. }
  960.  
  961. #ifdef notdef
  962. RETSIGTYPE
  963. sigpipe(int signo)
  964. {
  965.     struct state *sp;
  966.  
  967.     sp = &state;
  968.     if (sp->c >= 0) {
  969.         close(sp->c);
  970.         sp->c = -1;
  971.     }
  972.  
  973.     return RETSIGVAL;
  974. }
  975. #endif
  976.  
  977. RETSIGTYPE
  978. sigterm(int signo)
  979. {
  980.     struct state *sp;
  981.     struct client *cl;
  982.  
  983.     sp = &state;
  984.     for (cl = sp->clients; cl != NULL; cl = cl->next) {
  985.         close(cl->c);
  986.         cl->c = -1;
  987.     }
  988.  
  989.     ++sawterm;
  990.     return RETSIGVAL;
  991. }
  992.  
  993. int
  994. update_fd_set(struct state *sp, fd_set *rp, fd_set *wp)
  995. {
  996.     int nfds;
  997.     struct client *cl;
  998.  
  999.     FD_ZERO(rp);
  1000.     FD_ZERO(wp);
  1001.  
  1002.     /* Normal socket */
  1003.     nfds = sp->s;
  1004.     FD_SET(sp->s, rp);
  1005.     if (sp->rfd >= 0) {
  1006.         FD_SET(sp->rfd, rp);
  1007.         nfds = MAX(nfds, sp->rfd);
  1008.     }
  1009.  
  1010.     /* R/O socket */
  1011.     if (sp->ro >= 0) {
  1012.         FD_SET(sp->ro, rp);
  1013.         nfds = MAX(nfds, sp->ro);
  1014.     }
  1015.  
  1016.     /* Web registration socket */
  1017.     if (sp->web >= 0) {
  1018.         FD_SET(sp->web, rp);
  1019.         nfds = MAX(nfds, sp->web);
  1020.     }
  1021.  
  1022. #ifdef HAVE_BROCCOLI
  1023.     /* broccoli socket */
  1024.     if (sp->b >= 0) {
  1025.         FD_SET(sp->b, rp);
  1026.         nfds = MAX(nfds, sp->b);
  1027.     }
  1028. #endif
  1029.  
  1030.     /* Client sockets */
  1031.     for (cl = sp->clients; cl != NULL; cl = cl->next) {
  1032.         if (cl->c >= 0) {
  1033.             FD_SET(cl->c, rp);
  1034.             nfds = MAX(nfds, cl->c);
  1035.  
  1036.             if (cl->wbuf.len > 0)
  1037.                 FD_SET(cl->c, wp);
  1038.         }
  1039.     }
  1040.     ++nfds;
  1041.     return (nfds);
  1042. }
  1043.  
  1044. __dead void
  1045. usage(void)
  1046. {
  1047.  
  1048.     fprintf(stderr, "%s version %s\n", prog, version);
  1049. #ifdef HAVE_BROCCOLI
  1050.     fprintf(stderr, "Broccoli version %s\n", broccoli_version);
  1051. #endif
  1052.     fprintf(stderr,
  1053.         "Usage: %s [-dfv] [-c config] [-o logfile] [-P pidfile]\n", prog);
  1054.     exit(EX_USAGE);
  1055. }
  1056.