Print this page
5375 utmpd(1M) core dumps when WTMPX_UPDATE_FREQ is zero

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/utmpd/utmpd.c
          +++ new/usr/src/cmd/utmpd/utmpd.c
↓ open down ↓ 11 lines elided ↑ open up ↑
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22      - * Copyright 2014 Shruti V Sampat <shrutisampat@gmail.com>
       22 + * Copyright 2014, 2015 Shruti V Sampat <shrutisampat@gmail.com>
  23   23   */
  24   24  
  25   25  /*
  26   26   * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  27   27   * Use is subject to license terms.
  28   28   */
  29   29  
  30   30  /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
  31   31  /*        All Rights Reserved   */
  32   32  
↓ open down ↓ 38 lines elided ↑ open up ↑
  71   71  #include        <time.h>
  72   72  #include        <sys/stropts.h>
  73   73  #include        <wait.h>
  74   74  #include        <syslog.h>
  75   75  #include        <stdlib.h>
  76   76  #include        <string.h>
  77   77  #include        <poll.h>
  78   78  #include        <deflt.h>
  79   79  #include        <procfs.h>
  80   80  #include        <sys/resource.h>
       81 +#include        <limits.h>
  81   82  
  82   83  #define dprintf(x)      if (Debug) (void) printf x
  83   84  
  84   85  /*
  85   86   * Memory allocation keyed off MAX_FDS
  86   87   */
  87   88  #define MAX_FDS         4064    /* Maximum # file descriptors */
  88   89  #define EXTRA_MARGIN    32      /* Allocate this many more FDS over Max_Fds */
  89   90  /*
  90   91   * MAX_POLLNV & RESETS - paranoia to cover an error case that might not exist
↓ open down ↓ 10 lines elided ↑ open up ↑
 101  102  
 102  103  /*
 103  104   * The pidrec structure describes the data shipped down the pipe to
 104  105   * us from the pututxline() library in
 105  106   * lib/libc/port/gen/getutx.c
 106  107   */
 107  108  
 108  109  /*
 109  110   * pd_type's
 110  111   */
 111      -#define ADDPID  1
 112      -#define REMPID  2
      112 +#define ADDPID  1
      113 +#define REMPID  2
 113  114  
 114      -struct  pidrec {
      115 +struct  pidrec {
 115  116          int     pd_type;                /* Command type */
 116  117          pid_t   pd_pid;                 /* pid to add or remove */
 117  118  };
 118  119  
 119  120  
 120  121  /*
 121  122   * Since this program uses poll(2) and poll takes an array of file descriptors
 122  123   * as an argument we maintain our data in tables.
 123  124   * One table is the file descriptor array for poll, another parallel
 124  125   * array is a table which contains the process ID of the corresponding
 125  126   * open fd.  These tables are kept sorted by process ID for quick lookups.
 126  127   */
 127  128  
 128      -struct  pidentry {
      129 +struct  pidentry {
 129  130          pid_t   pl_pid;                 /* pid to watch for */
 130      -        int     pl_status;              /* Exit status of proc */
      131 +        int     pl_status;              /* Exit status of proc */
 131  132  };
 132  133  
 133  134  static struct pidentry *pidtable = NULL;
 134  135  
 135  136  static pollfd_t *fdtable = NULL;
 136  137  
 137  138  static int      pidcnt = 0;             /* Number of procs being watched */
 138  139  static char     *prog_name;             /* To save the invocation name away */
 139  140  static char     *UTMPPIPE_DIR = "/var/run";
 140  141  static char     *UTMPPIPE = "/var/run/utmppipe";
 141  142  static int      Pfd = -1;               /* File descriptor of named pipe */
 142      -static int      Poll_timeout = POLL_TIMEOUT;
      143 +static int      Poll_timeout = POLL_TIMEOUT;
 143  144  static int      WTMPXfd = -1;           /* File descriptor of WTMPX_FILE */
 144  145  static int      WTMPX_ufreq = WTMPX_UFREQ;
 145  146  static int      Debug = 0;              /* Set by command line argument */
 146  147  static int      Max_fds         = MAX_FDS;
 147  148  
 148  149  /*
 149  150   * This program has three main components plus utilities and debug routines
 150  151   *      Receiver - receives the process ID or process for us to watch.
 151  152   *                 (Uses a named pipe to get messages)
 152  153   *      Watcher  - Use poll(2) to watch for processes to die so they
 153  154   *                 can be cleaned up (get marked as DEAD_PROCESS)
 154      - *      Scanner  - periodically scans the utmpx file for stale entries
      155 + *      Scanner  - periodically scans the utmpx file for stale entries
 155  156   *                 or live entries that we don't know about.
 156  157   */
 157  158  
 158  159  static int wait_for_pids();     /* Watcher - uses poll */
 159  160  static void scan_utmps();       /* Scanner, reads utmpx file */
 160  161  static void drain_pipe();       /* Receiver - reads mesgs over UTMPPIPE */
 161  162  static void setup_pipe();       /* For setting up receiver */
 162  163  
 163  164  static void add_pid();          /* Adds a process to the table */
 164  165  static void rem_pid();          /* Removes a process from the table */
↓ open down ↓ 4 lines elided ↑ open up ↑
 169  170  
 170  171  static void clean_entry();      /* Removes entry from our table and calls ... */
 171  172  static void clean_utmpx_ent();  /* Cleans a utmpx entry */
 172  173  
 173  174  static void fatal() __NORETURN; /* Prints error message and calls exit */
 174  175  static void nonfatal();         /* Prints error message */
 175  176  static void print_tables();     /* Prints out internal tables for Debug */
 176  177  static int proc_is_alive(pid_t pid);    /* Check if a process is alive */
 177  178  static void warn_utmp(void);
 178  179  
      180 +/* Validate defaults from file and assign */
      181 +static int validate_default(char *defp, int *flag);
      182 +
 179  183  /*
 180  184   * main()  - Main does basic setup and calls wait_for_pids() to do the work
 181  185   */
 182  186  
 183  187  int
 184  188  main(int argc, char *argv[])
 185  189  {
 186  190          char *defp;
 187  191          struct rlimit rlim;
 188  192          int i;
 189  193          time_t curtime, now;
      194 +        char msg[256];
 190  195  
 191  196          prog_name = argv[0];                    /* Save invocation name */
 192  197  
 193  198          if (getuid() != 0)  {
 194  199                  (void) fprintf(stderr,
 195  200                      "You must be root to run this program\n");
 196  201                  fatal("You must be root to run this program");
 197  202          }
 198  203  
 199  204          if (argc > 1) {
↓ open down ↓ 3 lines elided ↑ open up ↑
 203  208                  } else {
 204  209                          (void) fprintf(stderr,
 205  210                              "%s: Wrong number of arguments\n", prog_name);
 206  211                          (void) fprintf(stderr,
 207  212                              "Usage: %s [-debug]\n", prog_name);
 208  213                          exit(2);
 209  214                  }
 210  215          }
 211  216  
 212  217          /*
 213      -         * Read defaults file for poll timeout
      218 +         * Read defaults file for poll timeout, WTMPX update frequency
      219 +         * and maximum number of processes to monitor.
 214  220           */
 215  221          if (defopen(UTMP_DEFAULT) == 0) {
 216      -                if ((defp = defread("SCAN_PERIOD=")) != NULL) {
 217      -                        Poll_timeout = atol(defp);
 218      -                        dprintf(("Poll timeout set to %d\n", Poll_timeout));
 219      -                }
      222 +                if ((defp = defread("SCAN_PERIOD=")) != NULL)
      223 +                        if (validate_default(defp, &Poll_timeout) == -1) {
      224 +                                (void) snprintf(msg, sizeof (msg), "SCAN_PERIOD"
      225 +                                    " should be a positive integer, found %s",
      226 +                                    defp);
      227 +                                nonfatal(msg);
      228 +                        }
      229 +                dprintf(("Poll timeout set to %d\n", Poll_timeout));
 220  230  
 221      -                if ((defp = defread("WTMPX_UPDATE_FREQ=")) != NULL) {
 222      -                        WTMPX_ufreq = atol(defp);
 223      -                        dprintf(("WTMPX update frequency set to %d\n",
 224      -                            WTMPX_ufreq));
 225      -                }
      231 +                if ((defp = defread("WTMPX_UPDATE_FREQ=")) != NULL)
      232 +                        if (validate_default(defp, &WTMPX_ufreq) == -1) {
      233 +                                (void) snprintf(msg, sizeof (msg),
      234 +                                    "WTMPX_UPDATE_FREQ should be a positive "
      235 +                                    "integer, found %s", defp);
      236 +                                nonfatal(msg);
      237 +                        }
      238 +                dprintf(("WTMPX update frequency set to %d\n", WTMPX_ufreq));
 226  239  
 227  240                  /*
 228  241                   * Paranoia - if polling on large number of FDs is expensive /
 229  242                   * buggy the number can be set lower in the field.
 230  243                   */
 231      -                if ((defp = defread("MAX_FDS=")) != NULL) {
 232      -                        Max_fds = atol(defp);
 233      -                        dprintf(("Max_fds set to %d\n", Max_fds));
 234      -                }
      244 +                if ((defp = defread("MAX_FDS=")) != NULL)
      245 +                        if (validate_default(defp, &Max_fds) == -1) {
      246 +                                (void) snprintf(msg, sizeof (msg), "MAX_FDS "
      247 +                                    "should be a positive integer, found %s",
      248 +                                    defp);
      249 +                                nonfatal(msg);
      250 +                        }
      251 +                dprintf(("Max fds set to %d\n", Max_fds));
 235  252                  (void) defopen((char *)NULL);
 236  253          }
 237  254  
 238  255          if (Debug == 0) {
 239  256                  /*
 240  257                   * Daemonize ourselves
 241  258                   */
 242  259                  if (fork()) {
 243  260                          exit(0);
 244  261                  }
↓ open down ↓ 29 lines elided ↑ open up ↑
 274  291                  rlim.rlim_max = Max_fds + EXTRA_MARGIN + 1;
 275  292                  if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
 276  293                          fatal("Out of File Descriptors");
 277  294                  }
 278  295          } else
 279  296                  fatal("getrlimit returned failure");
 280  297  
 281  298          (void) enable_extended_FILE_stdio(-1, -1);
 282  299  
 283  300          if ((WTMPXfd = open(WTMPX_FILE, O_RDONLY)) < 0)
 284      -                nonfatal("WARNING: unable to open " WTMPX_FILE "for update.");
      301 +                nonfatal("WARNING: unable to open " WTMPX_FILE " for update.");
 285  302  
 286  303          /*
 287  304           * Loop here scanning the utmpx file and waiting for processes
 288  305           * to terminate.  Most of the activity is directed out of wait_for_pids.
 289  306           * If wait_for_pids fails we reload the table and try again.
 290  307           */
 291  308  
 292  309          curtime = time(NULL);
 293  310          dprintf(("utmp warning timer set to %d seconds\n", WARN_TIME));
 294  311  
↓ open down ↓ 380 lines elided ↑ open up ↑
 675  692  /*
 676  693   *              *** Utilities for add and removing entries in the tables ***
 677  694   */
 678  695  
 679  696  /*
 680  697   * add_pid      - add a pid to the fd table and the pidtable.
 681  698   *                these tables are sorted tables for quick lookups.
 682  699   *
 683  700   */
 684  701  static void
 685      -add_pid(pid)
 686      -        pid_t pid;
      702 +add_pid(pid_t pid)
 687  703  {
 688  704          int fd = 0;
 689  705          int i = 0, move_amt;
 690  706          int j;
 691  707          static int first_time = 1;
 692  708  
 693  709          /*
 694  710           * Check to see if the pid is already in our table, or being passed
 695  711           * pid zero.
 696  712           */
↓ open down ↓ 36 lines elided ↑ open up ↑
 733  749                   * the end, or overwriting an existing entry.
 734  750                   */
 735  751                  if (i != pidcnt && pid != pidtable[i].pl_pid) {
 736  752  
 737  753                          move_amt = pidcnt - i;
 738  754                          /*
 739  755                           * Move table down
 740  756                           */
 741  757                          if (move_amt != 0) {
 742  758                                  (void) memmove(&pidtable[i+1], &pidtable[i],
 743      -                                        move_amt * sizeof (struct pidentry));
      759 +                                    move_amt * sizeof (struct pidentry));
 744  760                                  (void) memmove(&fdtable[i+1], &fdtable[i],
 745      -                                        move_amt * sizeof (pollfd_t));
      761 +                                    move_amt * sizeof (pollfd_t));
 746  762                          }
 747  763                  }
 748  764          }
 749  765  
 750  766          /*
 751  767           * Fill in the events field for poll and copy the entry into the array
 752  768           */
 753  769          fdtable[i].events = 0;
 754  770          fdtable[i].revents = 0;
 755  771          fdtable[i].fd = fd;
 756  772  
 757  773          /*
 758  774           * Likewise, setup pid field and pointer (index) to the fdtable entry
 759  775           */
 760  776          pidtable[i].pl_pid = pid;
 761  777  
 762  778          pidcnt++;                       /* Bump the pid count */
 763  779          dprintf(("  add_pid: pid = %d fd = %d index = %d pidcnt = %d\n",
 764      -                (int)pid, fd, i, pidcnt));
      780 +            (int)pid, fd, i, pidcnt));
 765  781  }
 766  782  
 767  783  
 768  784  /*
 769  785   * rem_pid      - Remove an entry from the table and check to see if its
 770  786   *                not in the utmpx file.
 771  787   *                If i != -1 don't look up the pid, use i as index
      788 + *
      789 + * pid          - Pid of process to clean or 0 if we don't know it
      790 + *
      791 + * i            - Index into table or -1 if we need to look it up
      792 + *
      793 + * clean_it     - Clean the entry, or just remove from table?
 772  794   */
 773  795  
 774  796  static void
 775      -rem_pid(pid, i, clean_it)
 776      -        pid_t pid;      /* Pid of process to clean or 0 if we don't know it */
 777      -        int i;          /* Index into table or -1 if we need to look it up */
 778      -        int clean_it;   /* Clean the entry, or just remove from table? */
      797 +rem_pid(pid_t pid, int i, int clean_it)
 779  798  {
 780  799          int move_amt;
 781  800  
 782  801          dprintf(("  rem_pid: pid = %d i = %d", (int)pid, i));
 783  802  
 784  803          /*
 785  804           * Don't allow slot 0 in the table to be removed - utmppipe fd
 786  805           */
 787  806          if ((i == -1 && pid == 0) || (i == 0))  {
 788  807                  dprintf((" - attempted to remove proc 0\n"));
↓ open down ↓ 6 lines elided ↑ open up ↑
 795  814                  dprintf((" fd = %d\n", fdtable[i].fd));
 796  815  
 797  816                  if (clean_it == CLEANIT)
 798  817                          clean_entry(i);
 799  818  
 800  819                  move_amt = (pidcnt - i) - 1;
 801  820                  /*
 802  821                   * Remove entries from the tables.
 803  822                   */
 804  823                  (void) memmove(&pidtable[i], &pidtable[i+1],
 805      -                        move_amt * sizeof (struct pidentry));
      824 +                    move_amt * sizeof (struct pidentry));
 806  825  
 807  826                  (void) memmove(&fdtable[i], &fdtable[i+1],
 808      -                        move_amt * sizeof (pollfd_t));
      827 +                    move_amt * sizeof (pollfd_t));
 809  828  
 810  829                  /*
 811  830                   * decrement the pid count - one less pid to worry about
 812  831                   */
 813  832                  pidcnt--;
 814  833          }
 815  834          if (i == -1)
 816  835                  dprintf((" - entry not found \n"));
 817  836  }
 818  837  
 819  838  
 820  839  /*
 821  840   * find_pid     - Returns an index into the pidtable of the specifed pid,
 822  841   *                else -1 if not found
 823  842   */
 824  843  
 825  844  static int
 826      -find_pid(pid, i)
 827      -        pid_t pid;
 828      -        int *i;
      845 +find_pid(pid_t pid, int *i)
 829  846  {
 830  847          struct pidentry pe;
 831  848          struct pidentry *p;
 832  849  
 833  850          pe.pl_pid = pid;
 834  851          p = bsearch(&pe, pidtable, pidcnt, sizeof (struct pidentry), pidcmp);
 835  852  
 836  853          if (p == NULL)
 837  854                  return (0);
 838  855          else {
↓ open down ↓ 1 lines elided ↑ open up ↑
 840  857                  return (1);
 841  858          }
 842  859  }
 843  860  
 844  861  
 845  862  /*
 846  863   * Pidcmp - Used by besearch for sorting and finding  process IDs.
 847  864   */
 848  865  
 849  866  static int
 850      -pidcmp(a, b)
 851      -        struct pidentry *a, *b;
      867 +pidcmp(struct pidentry *a, struct pidentry *b)
 852  868  {
 853  869          if (b == NULL || a == NULL)
 854  870                  return (0);
 855  871          return (a->pl_pid - b->pl_pid);
 856  872  }
 857  873  
 858  874  
 859  875  /*
 860  876   * proc_to_fd   - Take a process ID and return an open file descriptor to the
 861  877   *                /proc file for the specified process.
 862  878   */
 863  879  static int
 864      -proc_to_fd(pid)
 865      -        pid_t pid;
      880 +proc_to_fd(pid_t pid)
 866  881  {
 867  882          char procname[64];
 868  883          int fd, dfd;
 869  884  
 870  885          (void) sprintf(procname, "/proc/%d/psinfo", (int)pid);
 871  886  
 872  887          if ((fd = open(procname, O_RDONLY)) >= 0) {
 873  888                  /*
 874  889                   * dup the fd above the low order values to assure
 875  890                   * stdio works for other fds - paranoia.
↓ open down ↓ 28 lines elided ↑ open up ↑
 904  919  
 905  920  /*
 906  921   *              *** Utmpx Cleaning Utilities ***
 907  922   */
 908  923  
 909  924  /*
 910  925   * Clean_entry  - Cleans the specified entry - where i is an index
 911  926   *                into the pid_table.
 912  927   */
 913  928  static void
 914      -clean_entry(i)
 915      -        int i;
      929 +clean_entry(int i)
 916  930  {
 917  931          struct utmpx *u;
 918  932  
 919  933          if (pidcnt == 0)
 920  934                  return;
 921  935  
 922  936          dprintf(("    Cleaning %d\n", (int)pidtable[i].pl_pid));
 923  937  
 924  938          /*
 925  939           * Double check if the process is dead.
 926  940           */
 927  941          if (proc_is_alive(pidtable[i].pl_pid)) {
 928      -                dprintf(("      Bad attempt to clean %d\n", \
 929      -                        (int)pidtable[i].pl_pid));
      942 +                dprintf(("      Bad attempt to clean %d\n",
      943 +                    (int)pidtable[i].pl_pid));
 930  944                  return;
 931  945          }
 932  946  
 933  947          /*
 934  948           * Find the entry that corresponds to this pid.
 935  949           * Do nothing if entry not found in utmpx file.
 936  950           */
 937  951          setutxent();
 938  952          while ((u = getutxent()) != NULL) {
 939  953                  if (u->ut_pid == pidtable[i].pl_pid) {
↓ open down ↓ 4 lines elided ↑ open up ↑
 944  958          }
 945  959          endutxent();
 946  960  }
 947  961  
 948  962  
 949  963  /*
 950  964   * clean_utmpx_ent      - Clean a utmpx entry
 951  965   */
 952  966  
 953  967  static void
 954      -clean_utmpx_ent(u)
 955      -        struct utmpx *u;
      968 +clean_utmpx_ent(struct utmpx *u)
 956  969  {
 957  970          dprintf(("      clean_utmpx_ent: %d\n", (int)u->ut_pid));
 958  971          u->ut_type = DEAD_PROCESS;
 959  972          (void) time(&u->ut_xtime);
 960  973          (void) pututxline(u);
 961  974          updwtmpx(WTMPX_FILE, u);
 962  975          /*
 963  976           * XXX update wtmp for ! nonuserx entries?
 964  977           */
 965  978  }
↓ open down ↓ 60 lines elided ↑ open up ↑
1026 1039          dprintf(("\n"));
1027 1040  }
1028 1041  
1029 1042  /*
1030 1043   * proc_is_alive        - Check to see if a process is alive AND its
1031 1044   *                        not a zombie.  Returns 1 if process is alive
1032 1045   *                        and zero if it is dead or a zombie.
1033 1046   */
1034 1047  
1035 1048  static int
1036      -proc_is_alive(pid)
1037      -        pid_t pid;
     1049 +proc_is_alive(pid_t pid)
1038 1050  {
1039 1051          char psinfoname[64];
1040 1052          int fd;
1041 1053          psinfo_t psinfo;
1042 1054  
1043 1055          if (kill(pid, 0) != 0)
1044 1056                  return (0);             /* Kill failed - no process */
1045 1057  
1046 1058          /*
1047 1059           * The process exists, so check if it's a zombie.
↓ open down ↓ 27 lines elided ↑ open up ↑
1075 1087  static void
1076 1088  warn_utmp()
1077 1089  {
1078 1090          struct stat s;
1079 1091  
1080 1092          if (lstat(UTMP_FILE, &s) == 0 &&
1081 1093              s.st_size % sizeof (struct utmp) == 0) {
1082 1094                  nonfatal("WARNING: /var/adm/utmp exists!\nSee "
1083 1095                      "utmp(4) for more information");
1084 1096          }
     1097 +}
     1098 +
     1099 +/*
     1100 + * validate_default - validate and assign defaults.
     1101 + */
     1102 +
     1103 +static int
     1104 +validate_default(char *defp, int *flag)
     1105 +{
     1106 +        long lval;
     1107 +        char *endptr;
     1108 +
     1109 +        errno = 0;
     1110 +        lval = strtol(defp, &endptr, 10);
     1111 +
     1112 +        if (errno != 0 || lval > INT_MAX || lval <= 0)
     1113 +                return (-1);
     1114 +
     1115 +        while (isspace(*endptr) != 0)
     1116 +                endptr++;
     1117 +
     1118 +        if (*endptr != '\0')
     1119 +                return (-1);
     1120 +
     1121 +        *flag = lval;
     1122 +        return (0);
1085 1123  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX