home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / rwhod / rwhod.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-19  |  13.7 KB  |  533 lines

  1. /*
  2.  * Copyright (c) 1983 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. char copyright[] =
  36. "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
  37.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)rwhod.c    5.20 (Berkeley) 3/2/91";
  42. #endif /* not lint */
  43.  
  44. #include <sys/param.h>
  45. #include <sys/socket.h>
  46. #include <sys/stat.h>
  47. #include <sys/signal.h>
  48. #include <sys/ioctl.h>
  49. #include <sys/file.h>
  50.  
  51. #include <net/if.h>
  52. #include <netinet/in.h>
  53.  
  54. #include <nlist.h>
  55. #include <errno.h>
  56. #include <utmp.h>
  57. #include <ctype.h>
  58. #include <netdb.h>
  59. #include <syslog.h>
  60. #include <protocols/rwhod.h>
  61. #include <stdio.h>
  62. #include <paths.h>
  63.  
  64. /*
  65.  * Alarm interval. Don't forget to change the down time check in ruptime
  66.  * if this is changed.
  67.  */
  68. #define AL_INTERVAL (3 * 60)
  69.  
  70. struct    sockaddr_in sin;
  71.  
  72. char    myname[MAXHOSTNAMELEN];
  73.  
  74. struct    nlist nl[] = {
  75. #define    NL_BOOTTIME    0
  76.     { "_boottime" },
  77.     0
  78. };
  79.  
  80. /*
  81.  * We communicate with each neighbor in
  82.  * a list constructed at the time we're
  83.  * started up.  Neighbors are currently
  84.  * directly connected via a hardware interface.
  85.  */
  86. struct    neighbor {
  87.     struct    neighbor *n_next;
  88.     char    *n_name;        /* interface name */
  89.     char    *n_addr;        /* who to send to */
  90.     int    n_addrlen;        /* size of address */
  91.     int    n_flags;        /* should forward?, interface flags */
  92. };
  93.  
  94. struct    neighbor *neighbors;
  95. struct    whod mywd;
  96. struct    servent *sp;
  97. int    s, utmpf, kmemf = -1;
  98.  
  99. #define    WHDRSIZE    (sizeof (mywd) - sizeof (mywd.wd_we))
  100.  
  101. extern int errno;
  102. char    *strcpy(), *malloc();
  103. long    lseek();
  104. void    getkmem(), onalrm();
  105. struct    in_addr inet_makeaddr();
  106.  
  107. main()
  108. {
  109.     struct sockaddr_in from;
  110.     struct stat st;
  111.     char path[64];
  112.     int on = 1;
  113.     char *cp, *index(), *strerror();
  114.  
  115.     if (getuid()) {
  116.         fprintf(stderr, "rwhod: not super user\n");
  117.         exit(1);
  118.     }
  119.     sp = getservbyname("who", "udp");
  120.     if (sp == 0) {
  121.         fprintf(stderr, "rwhod: udp/who: unknown service\n");
  122.         exit(1);
  123.     }
  124. #ifndef DEBUG
  125.     daemon(1, 0);
  126. #endif
  127.     if (chdir(_PATH_RWHODIR) < 0) {
  128.         (void)fprintf(stderr, "rwhod: %s: %s\n",
  129.             _PATH_RWHODIR, strerror(errno));
  130.         exit(1);
  131.     }
  132.     (void) signal(SIGHUP, getkmem);
  133.     openlog("rwhod", LOG_PID, LOG_DAEMON);
  134.     /*
  135.      * Establish host name as returned by system.
  136.      */
  137.     if (gethostname(myname, sizeof (myname) - 1) < 0) {
  138.         syslog(LOG_ERR, "gethostname: %m");
  139.         exit(1);
  140.     }
  141.     if ((cp = index(myname, '.')) != NULL)
  142.         *cp = '\0';
  143.     strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1);
  144.     utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
  145.     if (utmpf < 0) {
  146.         syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
  147.         exit(1);
  148.     }
  149.     getkmem();
  150.     if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  151.         syslog(LOG_ERR, "socket: %m");
  152.         exit(1);
  153.     }
  154.     if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
  155.         syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
  156.         exit(1);
  157.     }
  158.     sin.sin_family = AF_INET;
  159.     sin.sin_port = sp->s_port;
  160.     if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
  161.         syslog(LOG_ERR, "bind: %m");
  162.         exit(1);
  163.     }
  164.     if (!configure(s))
  165.         exit(1);
  166.     signal(SIGALRM, onalrm);
  167.     onalrm();
  168.     for (;;) {
  169.         struct whod wd;
  170.         int cc, whod, len = sizeof (from);
  171.  
  172.         cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
  173.             (struct sockaddr *)&from, &len);
  174.         if (cc <= 0) {
  175.             if (cc < 0 && errno != EINTR)
  176.                 syslog(LOG_WARNING, "recv: %m");
  177.             continue;
  178.         }
  179.         if (from.sin_port != sp->s_port) {
  180.             syslog(LOG_WARNING, "%d: bad from port",
  181.                 ntohs(from.sin_port));
  182.             continue;
  183.         }
  184.         if (wd.wd_vers != WHODVERSION)
  185.             continue;
  186.         if (wd.wd_type != WHODTYPE_STATUS)
  187.             continue;
  188.         if (!verify(wd.wd_hostname)) {
  189.             syslog(LOG_WARNING, "malformed host name from %x",
  190.                 from.sin_addr);
  191.             continue;
  192.         }
  193.         (void) sprintf(path, "whod.%s", wd.wd_hostname);
  194.         /*
  195.          * Rather than truncating and growing the file each time,
  196.          * use ftruncate if size is less than previous size.
  197.          */
  198.         whod = open(path, O_WRONLY | O_CREAT, 0644);
  199.         if (whod < 0) {
  200.             syslog(LOG_WARNING, "%s: %m", path);
  201.             continue;
  202.         }
  203. #if ENDIAN != BIG_ENDIAN
  204.         {
  205.             int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
  206.             struct whoent *we;
  207.  
  208.             /* undo header byte swapping before writing to file */
  209.             wd.wd_sendtime = ntohl(wd.wd_sendtime);
  210.             for (i = 0; i < 3; i++)
  211.                 wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
  212.             wd.wd_boottime = ntohl(wd.wd_boottime);
  213.             we = wd.wd_we;
  214.             for (i = 0; i < n; i++) {
  215.                 we->we_idle = ntohl(we->we_idle);
  216.                 we->we_utmp.out_time =
  217.                     ntohl(we->we_utmp.out_time);
  218.                 we++;
  219.             }
  220.         }
  221. #endif
  222.         (void) time((time_t *)&wd.wd_recvtime);
  223.         (void) write(whod, (char *)&wd, cc);
  224.         if (fstat(whod, &st) < 0 || st.st_size > cc)
  225.             ftruncate(whod, cc);
  226.         (void) close(whod);
  227.     }
  228. }
  229.  
  230. /*
  231.  * Check out host name for unprintables
  232.  * and other funnies before allowing a file
  233.  * to be created.  Sorry, but blanks aren't allowed.
  234.  */
  235. verify(name)
  236.     register char *name;
  237. {
  238.     register int size = 0;
  239.  
  240.     while (*name) {
  241.         if (!isascii(*name) || !(isalnum(*name) || ispunct(*name)))
  242.             return (0);
  243.         name++, size++;
  244.     }
  245.     return (size > 0);
  246. }
  247.  
  248. int    utmptime;
  249. int    utmpent;
  250. int    utmpsize = 0;
  251. struct    utmp *utmp;
  252. int    alarmcount;
  253.  
  254. void
  255. onalrm()
  256. {
  257.     register struct neighbor *np;
  258.     register struct whoent *we = mywd.wd_we, *wlast;
  259.     register int i;
  260.     struct stat stb;
  261.     int cc;
  262.     double avenrun[3];
  263.     time_t now = time((time_t *)NULL);
  264.     char *strerror();
  265.  
  266.     if (alarmcount % 10 == 0)
  267.         getkmem();
  268.     alarmcount++;
  269.     (void) fstat(utmpf, &stb);
  270.     if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
  271.         utmptime = stb.st_mtime;
  272.         if (stb.st_size > utmpsize) {
  273.             utmpsize = stb.st_size + 10 * sizeof(struct utmp);
  274.             if (utmp)
  275.                 utmp = (struct utmp *)realloc(utmp, utmpsize);
  276.             else
  277.                 utmp = (struct utmp *)malloc(utmpsize);
  278.             if (! utmp) {
  279.                 fprintf(stderr, "rwhod: malloc failed\n");
  280.                 utmpsize = 0;
  281.                 goto done;
  282.             }
  283.         }
  284.         (void) lseek(utmpf, (long)0, L_SET);
  285.         cc = read(utmpf, (char *)utmp, stb.st_size);
  286.         if (cc < 0) {
  287.             fprintf(stderr, "rwhod: %s: %s\n",
  288.                 _PATH_UTMP, strerror(errno));
  289.             goto done;
  290.         }
  291.         wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1];
  292.         utmpent = cc / sizeof (struct utmp);
  293.         for (i = 0; i < utmpent; i++)
  294.             if (utmp[i].ut_name[0]) {
  295.                 bcopy(utmp[i].ut_line, we->we_utmp.out_line,
  296.                    sizeof (utmp[i].ut_line));
  297.                 bcopy(utmp[i].ut_name, we->we_utmp.out_name,
  298.                    sizeof (utmp[i].ut_name));
  299.                 we->we_utmp.out_time = htonl(utmp[i].ut_time);
  300.                 if (we >= wlast)
  301.                     break;
  302.                 we++;
  303.             }
  304.         utmpent = we - mywd.wd_we;
  305.     }
  306.  
  307.     /*
  308.      * The test on utmpent looks silly---after all, if no one is
  309.      * logged on, why worry about efficiency?---but is useful on
  310.      * (e.g.) compute servers.
  311.      */
  312.     if (utmpent && chdir(_PATH_DEV)) {
  313.         syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
  314.         exit(1);
  315.     }
  316.     we = mywd.wd_we;
  317.     for (i = 0; i < utmpent; i++) {
  318.         if (stat(we->we_utmp.out_line, &stb) >= 0)
  319.             we->we_idle = htonl(now - stb.st_atime);
  320.         we++;
  321.     }
  322.     (void) getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
  323.     for (i = 0; i < 3; i++)
  324.         mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
  325.     cc = (char *)we - (char *)&mywd;
  326.     mywd.wd_sendtime = htonl(time(0));
  327.     mywd.wd_vers = WHODVERSION;
  328.     mywd.wd_type = WHODTYPE_STATUS;
  329.     for (np = neighbors; np != NULL; np = np->n_next)
  330.         (void) sendto(s, (char *)&mywd, cc, 0,
  331.             (struct sockaddr *)np->n_addr, np->n_addrlen);
  332.     if (utmpent && chdir(_PATH_RWHODIR)) {
  333.         syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
  334.         exit(1);
  335.     }
  336. done:
  337.     (void) alarm(AL_INTERVAL);
  338. }
  339.  
  340. void
  341. getkmem()
  342. {
  343.     static ino_t vmunixino;
  344.     static time_t vmunixctime;
  345.     struct stat sb;
  346.  
  347.     if (stat(_PATH_UNIX, &sb) < 0) {
  348.         if (vmunixctime)
  349.             return;
  350.     } else {
  351.         if (sb.st_ctime == vmunixctime && sb.st_ino == vmunixino)
  352.             return;
  353.         vmunixctime = sb.st_ctime;
  354.         vmunixino= sb.st_ino;
  355.     }
  356.     if (kmemf >= 0)
  357.         (void) close(kmemf);
  358. loop:
  359.     if (nlist(_PATH_UNIX, nl)) {
  360.         syslog(LOG_WARNING, "%s: namelist botch", _PATH_UNIX);
  361.         sleep(300);
  362.         goto loop;
  363.     }
  364.     kmemf = open(_PATH_KMEM, O_RDONLY, 0);
  365.     if (kmemf < 0) {
  366.         syslog(LOG_ERR, "%s: %m", _PATH_KMEM);
  367.         exit(1);
  368.     }
  369.     (void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, L_SET);
  370.     (void) read(kmemf, (char *)&mywd.wd_boottime,
  371.         sizeof (mywd.wd_boottime));
  372.     mywd.wd_boottime = htonl(mywd.wd_boottime);
  373. }
  374.  
  375. /*
  376.  * Figure out device configuration and select
  377.  * networks which deserve status information.
  378.  */
  379. configure(s)
  380.     int s;
  381. {
  382.     char buf[BUFSIZ], *cp, *cplim;
  383.     struct ifconf ifc;
  384.     struct ifreq ifreq, *ifr;
  385.     struct sockaddr_in *sin;
  386.     register struct neighbor *np;
  387.  
  388.     ifc.ifc_len = sizeof (buf);
  389.     ifc.ifc_buf = buf;
  390.     if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
  391.         syslog(LOG_ERR, "ioctl (get interface configuration)");
  392.         return (0);
  393.     }
  394.     ifr = ifc.ifc_req;
  395. #ifdef AF_LINK
  396. #define max(a, b) (a > b ? a : b)
  397. #define size(p)    max((p).sa_len, sizeof(p))
  398. #else
  399. #define size(p) (sizeof (p))
  400. #endif
  401.     cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
  402.     for (cp = buf; cp < cplim;
  403.             cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
  404.         ifr = (struct ifreq *)cp;
  405.         for (np = neighbors; np != NULL; np = np->n_next)
  406.             if (np->n_name &&
  407.                 strcmp(ifr->ifr_name, np->n_name) == 0)
  408.                 break;
  409.         if (np != NULL)
  410.             continue;
  411.         ifreq = *ifr;
  412.         np = (struct neighbor *)malloc(sizeof (*np));
  413.         if (np == NULL)
  414.             continue;
  415.         np->n_name = malloc(strlen(ifr->ifr_name) + 1);
  416.         if (np->n_name == NULL) {
  417.             free((char *)np);
  418.             continue;
  419.         }
  420.         strcpy(np->n_name, ifr->ifr_name);
  421.         np->n_addrlen = sizeof (ifr->ifr_addr);
  422.         np->n_addr = malloc(np->n_addrlen);
  423.         if (np->n_addr == NULL) {
  424.             free(np->n_name);
  425.             free((char *)np);
  426.             continue;
  427.         }
  428.         bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen);
  429.         if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
  430.             syslog(LOG_ERR, "ioctl (get interface flags)");
  431.             free((char *)np);
  432.             continue;
  433.         }
  434.         if ((ifreq.ifr_flags & IFF_UP) == 0 ||
  435.             (ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) {
  436.             free((char *)np);
  437.             continue;
  438.         }
  439.         np->n_flags = ifreq.ifr_flags;
  440.         if (np->n_flags & IFF_POINTOPOINT) {
  441.             if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
  442.                 syslog(LOG_ERR, "ioctl (get dstaddr)");
  443.                 free((char *)np);
  444.                 continue;
  445.             }
  446.             /* we assume addresses are all the same size */
  447.             bcopy((char *)&ifreq.ifr_dstaddr,
  448.               np->n_addr, np->n_addrlen);
  449.         }
  450.         if (np->n_flags & IFF_BROADCAST) {
  451.             if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
  452.                 syslog(LOG_ERR, "ioctl (get broadaddr)");
  453.                 free((char *)np);
  454.                 continue;
  455.             }
  456.             /* we assume addresses are all the same size */
  457.             bcopy((char *)&ifreq.ifr_broadaddr,
  458.               np->n_addr, np->n_addrlen);
  459.         }
  460.         /* gag, wish we could get rid of Internet dependencies */
  461.         sin = (struct sockaddr_in *)np->n_addr;
  462.         sin->sin_port = sp->s_port;
  463.         np->n_next = neighbors;
  464.         neighbors = np;
  465.     }
  466.     return (1);
  467. }
  468.  
  469. #ifdef DEBUG
  470. sendto(s, buf, cc, flags, to, tolen)
  471.     int s;
  472.     char *buf;
  473.     int cc, flags;
  474.     char *to;
  475.     int tolen;
  476. {
  477.     register struct whod *w = (struct whod *)buf;
  478.     register struct whoent *we;
  479.     struct sockaddr_in *sin = (struct sockaddr_in *)to;
  480.     char *interval();
  481.  
  482.     printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port));
  483.     printf("hostname %s %s\n", w->wd_hostname,
  484.        interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), "  up"));
  485.     printf("load %4.2f, %4.2f, %4.2f\n",
  486.         ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
  487.         ntohl(w->wd_loadav[2]) / 100.0);
  488.     cc -= WHDRSIZE;
  489.     for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) {
  490.         time_t t = ntohl(we->we_utmp.out_time);
  491.         printf("%-8.8s %s:%s %.12s",
  492.             we->we_utmp.out_name,
  493.             w->wd_hostname, we->we_utmp.out_line,
  494.             ctime(&t)+4);
  495.         we->we_idle = ntohl(we->we_idle) / 60;
  496.         if (we->we_idle) {
  497.             if (we->we_idle >= 100*60)
  498.                 we->we_idle = 100*60 - 1;
  499.             if (we->we_idle >= 60)
  500.                 printf(" %2d", we->we_idle / 60);
  501.             else
  502.                 printf("   ");
  503.             printf(":%02d", we->we_idle % 60);
  504.         }
  505.         printf("\n");
  506.     }
  507. }
  508.  
  509. char *
  510. interval(time, updown)
  511.     int time;
  512.     char *updown;
  513. {
  514.     static char resbuf[32];
  515.     int days, hours, minutes;
  516.  
  517.     if (time < 0 || time > 3*30*24*60*60) {
  518.         (void) sprintf(resbuf, "   %s ??:??", updown);
  519.         return (resbuf);
  520.     }
  521.     minutes = (time + 59) / 60;        /* round to minutes */
  522.     hours = minutes / 60; minutes %= 60;
  523.     days = hours / 24; hours %= 24;
  524.     if (days)
  525.         (void) sprintf(resbuf, "%s %2d+%02d:%02d",
  526.             updown, days, hours, minutes);
  527.     else
  528.         (void) sprintf(resbuf, "%s    %2d:%02d",
  529.             updown, hours, minutes);
  530.     return (resbuf);
  531. }
  532. #endif
  533.