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


   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2014 Shruti V Sampat <shrutisampat@gmail.com>
  23  */
  24 
  25 /*
  26  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  30 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  31 /*        All Rights Reserved   */
  32 
  33 /*
  34  * Portions of such source code were derived from Berkeley 4.3 BSD
  35  * under license from the Regents of the University of California.
  36  */
  37 
  38 /*
  39  * utmpd        - utmp daemon
  40  *
  41  *              This program receives requests from  pututxline(3)
  42  *              via a named pipe to watch the process to make sure it cleans up


  61 #include        <unistd.h>
  62 #include        <utmpx.h>
  63 #include        <errno.h>
  64 #include        <termio.h>
  65 #include        <sys/termios.h>
  66 #include        <sys/tty.h>
  67 #include        <ctype.h>
  68 #include        <sys/stat.h>
  69 #include        <sys/statvfs.h>
  70 #include        <fcntl.h>
  71 #include        <time.h>
  72 #include        <sys/stropts.h>
  73 #include        <wait.h>
  74 #include        <syslog.h>
  75 #include        <stdlib.h>
  76 #include        <string.h>
  77 #include        <poll.h>
  78 #include        <deflt.h>
  79 #include        <procfs.h>
  80 #include        <sys/resource.h>

  81 
  82 #define dprintf(x)      if (Debug) (void) printf x
  83 
  84 /*
  85  * Memory allocation keyed off MAX_FDS
  86  */
  87 #define MAX_FDS         4064    /* Maximum # file descriptors */
  88 #define EXTRA_MARGIN    32      /* Allocate this many more FDS over Max_Fds */
  89 /*
  90  * MAX_POLLNV & RESETS - paranoia to cover an error case that might not exist
  91  */
  92 #define MAX_POLL_ERRS   1024    /* Count of bad errors */
  93 #define MAX_RESETS      1024    /* Maximum times to reload tables */
  94 #define POLL_TIMEOUT    300     /* Default Timeout for poll() in seconds */
  95 #define CLEANIT         1       /* Used by rem_pid() */
  96 #define DONT_CLEAN      0       /* Used by rem_pid() */
  97 #define UTMP_DEFAULT    "/etc/default/utmpd"
  98 #define WARN_TIME       3600    /* seconds between utmp checks */
  99 #define WTMPX_UFREQ     60      /* seconds between updating WTMPX's atime */
 100 


 159 static void scan_utmps();       /* Scanner, reads utmpx file */
 160 static void drain_pipe();       /* Receiver - reads mesgs over UTMPPIPE */
 161 static void setup_pipe();       /* For setting up receiver */
 162 
 163 static void add_pid();          /* Adds a process to the table */
 164 static void rem_pid();          /* Removes a process from the table */
 165 static int find_pid();          /* Finds a process in the table */
 166 static int proc_to_fd();        /* Takes a pid and returns an fd for its proc */
 167 static void load_tables();      /* Loads up the tables the first time around */
 168 static int pidcmp();            /* For sorting pids */
 169 
 170 static void clean_entry();      /* Removes entry from our table and calls ... */
 171 static void clean_utmpx_ent();  /* Cleans a utmpx entry */
 172 
 173 static void fatal() __NORETURN; /* Prints error message and calls exit */
 174 static void nonfatal();         /* Prints error message */
 175 static void print_tables();     /* Prints out internal tables for Debug */
 176 static int proc_is_alive(pid_t pid);    /* Check if a process is alive */
 177 static void warn_utmp(void);
 178 



 179 /*
 180  * main()  - Main does basic setup and calls wait_for_pids() to do the work
 181  */
 182 
 183 int
 184 main(int argc, char *argv[])
 185 {
 186         char *defp;
 187         struct rlimit rlim;
 188         int i;
 189         time_t curtime, now;

 190 
 191         prog_name = argv[0];                    /* Save invocation name */
 192 
 193         if (getuid() != 0)  {
 194                 (void) fprintf(stderr,
 195                     "You must be root to run this program\n");
 196                 fatal("You must be root to run this program");
 197         }
 198 
 199         if (argc > 1) {
 200                 if ((argc == 2 && (int)strlen(argv[1]) >= 2) &&
 201                     (argv[1][0] == '-' && argv[1][1] == 'd')) {
 202                         Debug = 1;
 203                 } else {
 204                         (void) fprintf(stderr,
 205                             "%s: Wrong number of arguments\n", prog_name);
 206                         (void) fprintf(stderr,
 207                             "Usage: %s [-debug]\n", prog_name);
 208                         exit(2);
 209                 }
 210         }
 211 
 212         /*
 213          * Read defaults file for poll timeout

 214          */
 215         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                 }

 220 
 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                 }

 226 
 227                 /*
 228                  * Paranoia - if polling on large number of FDs is expensive /
 229                  * buggy the number can be set lower in the field.
 230                  */
 231                 if ((defp = defread("MAX_FDS=")) != NULL) {
 232                         Max_fds = atol(defp);
 233                         dprintf(("Max_fds set to %d\n", Max_fds));



 234                 }

 235                 (void) defopen((char *)NULL);
 236         }
 237 
 238         if (Debug == 0) {
 239                 /*
 240                  * Daemonize ourselves
 241                  */
 242                 if (fork()) {
 243                         exit(0);
 244                 }
 245                 (void) close(0);
 246                 (void) close(1);
 247                 (void) close(2);
 248                 /*
 249                  * We open these to avoid accidentally writing to a proc file
 250                  */
 251                 (void) open("/dev/null", O_RDONLY);
 252                 (void) open("/dev/null", O_WRONLY);
 253                 (void) open("/dev/null", O_WRONLY);
 254                 (void) setsid();                /* release process from tty */


 264         if ((pidtable = malloc(Max_fds * sizeof (struct pidentry))) == NULL)
 265                 fatal("Malloc failed");
 266         if ((fdtable = malloc(Max_fds * sizeof (pollfd_t))) == NULL)
 267                 fatal("Malloc failed");
 268 
 269         /*
 270          * Up the limit on FDs
 271          */
 272         if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
 273                 rlim.rlim_cur = Max_fds + EXTRA_MARGIN + 1;
 274                 rlim.rlim_max = Max_fds + EXTRA_MARGIN + 1;
 275                 if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
 276                         fatal("Out of File Descriptors");
 277                 }
 278         } else
 279                 fatal("getrlimit returned failure");
 280 
 281         (void) enable_extended_FILE_stdio(-1, -1);
 282 
 283         if ((WTMPXfd = open(WTMPX_FILE, O_RDONLY)) < 0)
 284                 nonfatal("WARNING: unable to open " WTMPX_FILE "for update.");
 285 
 286         /*
 287          * Loop here scanning the utmpx file and waiting for processes
 288          * to terminate.  Most of the activity is directed out of wait_for_pids.
 289          * If wait_for_pids fails we reload the table and try again.
 290          */
 291 
 292         curtime = time(NULL);
 293         dprintf(("utmp warning timer set to %d seconds\n", WARN_TIME));
 294 
 295         for (i = 0; i < MAX_RESETS; i++) {
 296                 load_tables();
 297                 while (wait_for_pids() == 1) {
 298                         now = time(NULL);
 299                         if ((now - curtime) >= WARN_TIME) {
 300                                 dprintf(("utmp warning timer expired\n"));
 301                                 warn_utmp();
 302                                 curtime = now;
 303                         }
 304                 }


 665                         rem_pid(p->pd_pid, -1, DONT_CLEAN);
 666                         break;
 667                 default:
 668                         nonfatal("Bad message on utmppipe\n");
 669                                 break;
 670                 }
 671         }
 672 }
 673 
 674 
 675 /*
 676  *              *** Utilities for add and removing entries in the tables ***
 677  */
 678 
 679 /*
 680  * add_pid      - add a pid to the fd table and the pidtable.
 681  *                these tables are sorted tables for quick lookups.
 682  *
 683  */
 684 static void
 685 add_pid(pid)
 686         pid_t pid;
 687 {
 688         int fd = 0;
 689         int i = 0, move_amt;
 690         int j;
 691         static int first_time = 1;
 692 
 693         /*
 694          * Check to see if the pid is already in our table, or being passed
 695          * pid zero.
 696          */
 697         if (pidcnt != 0 && (find_pid(pid, &j) == 1 || pid == 0))
 698                 return;
 699 
 700         if (pidcnt >= Max_fds) {
 701                 if (first_time == 1) {
 702                         /*
 703                          * Print this error only once
 704                          */
 705                         nonfatal("File Descriptor limit exceeded");
 706                         first_time = 0;


 752          */
 753         fdtable[i].events = 0;
 754         fdtable[i].revents = 0;
 755         fdtable[i].fd = fd;
 756 
 757         /*
 758          * Likewise, setup pid field and pointer (index) to the fdtable entry
 759          */
 760         pidtable[i].pl_pid = pid;
 761 
 762         pidcnt++;                       /* Bump the pid count */
 763         dprintf(("  add_pid: pid = %d fd = %d index = %d pidcnt = %d\n",
 764                 (int)pid, fd, i, pidcnt));
 765 }
 766 
 767 
 768 /*
 769  * rem_pid      - Remove an entry from the table and check to see if its
 770  *                not in the utmpx file.
 771  *                If i != -1 don't look up the pid, use i as index






 772  */
 773 
 774 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? */
 779 {
 780         int move_amt;
 781 
 782         dprintf(("  rem_pid: pid = %d i = %d", (int)pid, i));
 783 
 784         /*
 785          * Don't allow slot 0 in the table to be removed - utmppipe fd
 786          */
 787         if ((i == -1 && pid == 0) || (i == 0))  {
 788                 dprintf((" - attempted to remove proc 0\n"));
 789                 return;
 790         }
 791 
 792         if (i != -1 || find_pid(pid, &i) == 1) {    /* Found the entry */
 793                 (void) close(fdtable[i].fd);    /* We're done with the fd */
 794 
 795                 dprintf((" fd = %d\n", fdtable[i].fd));
 796 
 797                 if (clean_it == CLEANIT)
 798                         clean_entry(i);


 806 
 807                 (void) memmove(&fdtable[i], &fdtable[i+1],
 808                         move_amt * sizeof (pollfd_t));
 809 
 810                 /*
 811                  * decrement the pid count - one less pid to worry about
 812                  */
 813                 pidcnt--;
 814         }
 815         if (i == -1)
 816                 dprintf((" - entry not found \n"));
 817 }
 818 
 819 
 820 /*
 821  * find_pid     - Returns an index into the pidtable of the specifed pid,
 822  *                else -1 if not found
 823  */
 824 
 825 static int
 826 find_pid(pid, i)
 827         pid_t pid;
 828         int *i;
 829 {
 830         struct pidentry pe;
 831         struct pidentry *p;
 832 
 833         pe.pl_pid = pid;
 834         p = bsearch(&pe, pidtable, pidcnt, sizeof (struct pidentry), pidcmp);
 835 
 836         if (p == NULL)
 837                 return (0);
 838         else {
 839                 *i = p - (struct pidentry *)pidtable;
 840                 return (1);
 841         }
 842 }
 843 
 844 
 845 /*
 846  * Pidcmp - Used by besearch for sorting and finding  process IDs.
 847  */
 848 
 849 static int
 850 pidcmp(a, b)
 851         struct pidentry *a, *b;
 852 {
 853         if (b == NULL || a == NULL)
 854                 return (0);
 855         return (a->pl_pid - b->pl_pid);
 856 }
 857 
 858 
 859 /*
 860  * proc_to_fd   - Take a process ID and return an open file descriptor to the
 861  *                /proc file for the specified process.
 862  */
 863 static int
 864 proc_to_fd(pid)
 865         pid_t pid;
 866 {
 867         char procname[64];
 868         int fd, dfd;
 869 
 870         (void) sprintf(procname, "/proc/%d/psinfo", (int)pid);
 871 
 872         if ((fd = open(procname, O_RDONLY)) >= 0) {
 873                 /*
 874                  * dup the fd above the low order values to assure
 875                  * stdio works for other fds - paranoia.
 876                  */
 877                 if (fd < EXTRA_MARGIN) {
 878                         dfd = fcntl(fd, F_DUPFD, EXTRA_MARGIN);
 879                         if (dfd > 0) {
 880                                 (void) close(fd);
 881                                 fd = dfd;
 882                         }
 883                 }
 884                 /*
 885                  * More paranoia - set the close on exec flag


 894                 /*
 895                  * This is fatal, since libc won't be able to allocate
 896                  * any fds for the pututxline() routines
 897                  */
 898                 fatal("Out of file descriptors");
 899         }
 900         fatal(procname);                /* Only get here on error */
 901         return (-1);
 902 }
 903 
 904 
 905 /*
 906  *              *** Utmpx Cleaning Utilities ***
 907  */
 908 
 909 /*
 910  * Clean_entry  - Cleans the specified entry - where i is an index
 911  *                into the pid_table.
 912  */
 913 static void
 914 clean_entry(i)
 915         int i;
 916 {
 917         struct utmpx *u;
 918 
 919         if (pidcnt == 0)
 920                 return;
 921 
 922         dprintf(("    Cleaning %d\n", (int)pidtable[i].pl_pid));
 923 
 924         /*
 925          * Double check if the process is dead.
 926          */
 927         if (proc_is_alive(pidtable[i].pl_pid)) {
 928                 dprintf(("      Bad attempt to clean %d\n", \
 929                         (int)pidtable[i].pl_pid));
 930                 return;
 931         }
 932 
 933         /*
 934          * Find the entry that corresponds to this pid.
 935          * Do nothing if entry not found in utmpx file.
 936          */
 937         setutxent();
 938         while ((u = getutxent()) != NULL) {
 939                 if (u->ut_pid == pidtable[i].pl_pid) {
 940                         if (u->ut_type == USER_PROCESS) {
 941                                 clean_utmpx_ent(u);
 942                         }
 943                 }
 944         }
 945         endutxent();
 946 }
 947 
 948 
 949 /*
 950  * clean_utmpx_ent      - Clean a utmpx entry
 951  */
 952 
 953 static void
 954 clean_utmpx_ent(u)
 955         struct utmpx *u;
 956 {
 957         dprintf(("      clean_utmpx_ent: %d\n", (int)u->ut_pid));
 958         u->ut_type = DEAD_PROCESS;
 959         (void) time(&u->ut_xtime);
 960         (void) pututxline(u);
 961         updwtmpx(WTMPX_FILE, u);
 962         /*
 963          * XXX update wtmp for ! nonuserx entries?
 964          */
 965 }
 966 
 967 /*
 968  *              *** Error Handling and Debugging Routines ***
 969  */
 970 
 971 /*
 972  * fatal - Catastrophic failure
 973  */
 974 
 975 static void


1016         if (Debug == 0)
1017                 return;
1018 
1019         dprintf(("pidtable: "));
1020         for (i = 0; i < pidcnt; i++)
1021                 dprintf(("%d: %d  ", i, (int)pidtable[i].pl_pid));
1022         dprintf(("\n"));
1023         dprintf(("fdtable:  "));
1024         for (i = 0; i < pidcnt; i++)
1025                 dprintf(("%d: %d  ", i, fdtable[i].fd));
1026         dprintf(("\n"));
1027 }
1028 
1029 /*
1030  * proc_is_alive        - Check to see if a process is alive AND its
1031  *                        not a zombie.  Returns 1 if process is alive
1032  *                        and zero if it is dead or a zombie.
1033  */
1034 
1035 static int
1036 proc_is_alive(pid)
1037         pid_t pid;
1038 {
1039         char psinfoname[64];
1040         int fd;
1041         psinfo_t psinfo;
1042 
1043         if (kill(pid, 0) != 0)
1044                 return (0);             /* Kill failed - no process */
1045 
1046         /*
1047          * The process exists, so check if it's a zombie.
1048          */
1049         (void) sprintf(psinfoname, "/proc/%d/psinfo", (int)pid);
1050 
1051         if ((fd = open(psinfoname, O_RDONLY)) < 0 ||
1052             read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1053                 /*
1054                  * We either couldn't open the proc, or we did but the
1055                  * read of the psinfo file failed, so pid is nonexistent.
1056                  */
1057                 psinfo.pr_nlwp = 0;


1065 
1066 /*
1067  * warn_utmp -  /var/adm/utmp has been deprecated. It should no longer
1068  *              be used.  Applications that try to directly manipulate
1069  *              it may cause problems. Since the file is no longer
1070  *              shipped, if it appears on a system it's because an
1071  *              old application created it.  We'll have utmpd
1072  *              complain about it periodically.
1073  */
1074 
1075 static void
1076 warn_utmp()
1077 {
1078         struct stat s;
1079 
1080         if (lstat(UTMP_FILE, &s) == 0 &&
1081             s.st_size % sizeof (struct utmp) == 0) {
1082                 nonfatal("WARNING: /var/adm/utmp exists!\nSee "
1083                     "utmp(4) for more information");
1084         }


























1085 }


   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2014, 2015 Shruti V Sampat <shrutisampat@gmail.com>
  23  */
  24 
  25 /*
  26  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  30 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  31 /*        All Rights Reserved   */
  32 
  33 /*
  34  * Portions of such source code were derived from Berkeley 4.3 BSD
  35  * under license from the Regents of the University of California.
  36  */
  37 
  38 /*
  39  * utmpd        - utmp daemon
  40  *
  41  *              This program receives requests from  pututxline(3)
  42  *              via a named pipe to watch the process to make sure it cleans up


  61 #include        <unistd.h>
  62 #include        <utmpx.h>
  63 #include        <errno.h>
  64 #include        <termio.h>
  65 #include        <sys/termios.h>
  66 #include        <sys/tty.h>
  67 #include        <ctype.h>
  68 #include        <sys/stat.h>
  69 #include        <sys/statvfs.h>
  70 #include        <fcntl.h>
  71 #include        <time.h>
  72 #include        <sys/stropts.h>
  73 #include        <wait.h>
  74 #include        <syslog.h>
  75 #include        <stdlib.h>
  76 #include        <string.h>
  77 #include        <poll.h>
  78 #include        <deflt.h>
  79 #include        <procfs.h>
  80 #include        <sys/resource.h>
  81 #include        <limits.h>
  82 
  83 #define dprintf(x)      if (Debug) (void) printf x
  84 
  85 /*
  86  * Memory allocation keyed off MAX_FDS
  87  */
  88 #define MAX_FDS         4064    /* Maximum # file descriptors */
  89 #define EXTRA_MARGIN    32      /* Allocate this many more FDS over Max_Fds */
  90 /*
  91  * MAX_POLLNV & RESETS - paranoia to cover an error case that might not exist
  92  */
  93 #define MAX_POLL_ERRS   1024    /* Count of bad errors */
  94 #define MAX_RESETS      1024    /* Maximum times to reload tables */
  95 #define POLL_TIMEOUT    300     /* Default Timeout for poll() in seconds */
  96 #define CLEANIT         1       /* Used by rem_pid() */
  97 #define DONT_CLEAN      0       /* Used by rem_pid() */
  98 #define UTMP_DEFAULT    "/etc/default/utmpd"
  99 #define WARN_TIME       3600    /* seconds between utmp checks */
 100 #define WTMPX_UFREQ     60      /* seconds between updating WTMPX's atime */
 101 


 160 static void scan_utmps();       /* Scanner, reads utmpx file */
 161 static void drain_pipe();       /* Receiver - reads mesgs over UTMPPIPE */
 162 static void setup_pipe();       /* For setting up receiver */
 163 
 164 static void add_pid();          /* Adds a process to the table */
 165 static void rem_pid();          /* Removes a process from the table */
 166 static int find_pid();          /* Finds a process in the table */
 167 static int proc_to_fd();        /* Takes a pid and returns an fd for its proc */
 168 static void load_tables();      /* Loads up the tables the first time around */
 169 static int pidcmp();            /* For sorting pids */
 170 
 171 static void clean_entry();      /* Removes entry from our table and calls ... */
 172 static void clean_utmpx_ent();  /* Cleans a utmpx entry */
 173 
 174 static void fatal() __NORETURN; /* Prints error message and calls exit */
 175 static void nonfatal();         /* Prints error message */
 176 static void print_tables();     /* Prints out internal tables for Debug */
 177 static int proc_is_alive(pid_t pid);    /* Check if a process is alive */
 178 static void warn_utmp(void);
 179 
 180 /* Validate defaults from file and assign */
 181 static int validate_default(char *defp, int *flag);
 182 
 183 /*
 184  * main()  - Main does basic setup and calls wait_for_pids() to do the work
 185  */
 186 
 187 int
 188 main(int argc, char *argv[])
 189 {
 190         char *defp;
 191         struct rlimit rlim;
 192         int i;
 193         time_t curtime, now;
 194         char msg[256];
 195 
 196         prog_name = argv[0];                    /* Save invocation name */
 197 
 198         if (getuid() != 0)  {
 199                 (void) fprintf(stderr,
 200                     "You must be root to run this program\n");
 201                 fatal("You must be root to run this program");
 202         }
 203 
 204         if (argc > 1) {
 205                 if ((argc == 2 && (int)strlen(argv[1]) >= 2) &&
 206                     (argv[1][0] == '-' && argv[1][1] == 'd')) {
 207                         Debug = 1;
 208                 } else {
 209                         (void) fprintf(stderr,
 210                             "%s: Wrong number of arguments\n", prog_name);
 211                         (void) fprintf(stderr,
 212                             "Usage: %s [-debug]\n", prog_name);
 213                         exit(2);
 214                 }
 215         }
 216 
 217         /*
 218          * Read defaults file for poll timeout, WTMPX update frequency
 219          * and maximum number of processes to monitor.
 220          */
 221         if (defopen(UTMP_DEFAULT) == 0) {
 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));
 230 
 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));
 239 
 240                 /*
 241                  * Paranoia - if polling on large number of FDs is expensive /
 242                  * buggy the number can be set lower in the field.
 243                  */
 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));
 252                 (void) defopen((char *)NULL);
 253         }
 254 
 255         if (Debug == 0) {
 256                 /*
 257                  * Daemonize ourselves
 258                  */
 259                 if (fork()) {
 260                         exit(0);
 261                 }
 262                 (void) close(0);
 263                 (void) close(1);
 264                 (void) close(2);
 265                 /*
 266                  * We open these to avoid accidentally writing to a proc file
 267                  */
 268                 (void) open("/dev/null", O_RDONLY);
 269                 (void) open("/dev/null", O_WRONLY);
 270                 (void) open("/dev/null", O_WRONLY);
 271                 (void) setsid();                /* release process from tty */


 281         if ((pidtable = malloc(Max_fds * sizeof (struct pidentry))) == NULL)
 282                 fatal("Malloc failed");
 283         if ((fdtable = malloc(Max_fds * sizeof (pollfd_t))) == NULL)
 284                 fatal("Malloc failed");
 285 
 286         /*
 287          * Up the limit on FDs
 288          */
 289         if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
 290                 rlim.rlim_cur = Max_fds + EXTRA_MARGIN + 1;
 291                 rlim.rlim_max = Max_fds + EXTRA_MARGIN + 1;
 292                 if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
 293                         fatal("Out of File Descriptors");
 294                 }
 295         } else
 296                 fatal("getrlimit returned failure");
 297 
 298         (void) enable_extended_FILE_stdio(-1, -1);
 299 
 300         if ((WTMPXfd = open(WTMPX_FILE, O_RDONLY)) < 0)
 301                 nonfatal("WARNING: unable to open " WTMPX_FILE " for update.");
 302 
 303         /*
 304          * Loop here scanning the utmpx file and waiting for processes
 305          * to terminate.  Most of the activity is directed out of wait_for_pids.
 306          * If wait_for_pids fails we reload the table and try again.
 307          */
 308 
 309         curtime = time(NULL);
 310         dprintf(("utmp warning timer set to %d seconds\n", WARN_TIME));
 311 
 312         for (i = 0; i < MAX_RESETS; i++) {
 313                 load_tables();
 314                 while (wait_for_pids() == 1) {
 315                         now = time(NULL);
 316                         if ((now - curtime) >= WARN_TIME) {
 317                                 dprintf(("utmp warning timer expired\n"));
 318                                 warn_utmp();
 319                                 curtime = now;
 320                         }
 321                 }


 682                         rem_pid(p->pd_pid, -1, DONT_CLEAN);
 683                         break;
 684                 default:
 685                         nonfatal("Bad message on utmppipe\n");
 686                                 break;
 687                 }
 688         }
 689 }
 690 
 691 
 692 /*
 693  *              *** Utilities for add and removing entries in the tables ***
 694  */
 695 
 696 /*
 697  * add_pid      - add a pid to the fd table and the pidtable.
 698  *                these tables are sorted tables for quick lookups.
 699  *
 700  */
 701 static void
 702 add_pid(pid_t pid)

 703 {
 704         int fd = 0;
 705         int i = 0, move_amt;
 706         int j;
 707         static int first_time = 1;
 708 
 709         /*
 710          * Check to see if the pid is already in our table, or being passed
 711          * pid zero.
 712          */
 713         if (pidcnt != 0 && (find_pid(pid, &j) == 1 || pid == 0))
 714                 return;
 715 
 716         if (pidcnt >= Max_fds) {
 717                 if (first_time == 1) {
 718                         /*
 719                          * Print this error only once
 720                          */
 721                         nonfatal("File Descriptor limit exceeded");
 722                         first_time = 0;


 768          */
 769         fdtable[i].events = 0;
 770         fdtable[i].revents = 0;
 771         fdtable[i].fd = fd;
 772 
 773         /*
 774          * Likewise, setup pid field and pointer (index) to the fdtable entry
 775          */
 776         pidtable[i].pl_pid = pid;
 777 
 778         pidcnt++;                       /* Bump the pid count */
 779         dprintf(("  add_pid: pid = %d fd = %d index = %d pidcnt = %d\n",
 780             (int)pid, fd, i, pidcnt));
 781 }
 782 
 783 
 784 /*
 785  * rem_pid      - Remove an entry from the table and check to see if its
 786  *                not in the utmpx file.
 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?
 794  */
 795 
 796 static void
 797 rem_pid(pid_t pid, int i, int clean_it)



 798 {
 799         int move_amt;
 800 
 801         dprintf(("  rem_pid: pid = %d i = %d", (int)pid, i));
 802 
 803         /*
 804          * Don't allow slot 0 in the table to be removed - utmppipe fd
 805          */
 806         if ((i == -1 && pid == 0) || (i == 0))  {
 807                 dprintf((" - attempted to remove proc 0\n"));
 808                 return;
 809         }
 810 
 811         if (i != -1 || find_pid(pid, &i) == 1) {    /* Found the entry */
 812                 (void) close(fdtable[i].fd);    /* We're done with the fd */
 813 
 814                 dprintf((" fd = %d\n", fdtable[i].fd));
 815 
 816                 if (clean_it == CLEANIT)
 817                         clean_entry(i);


 825 
 826                 (void) memmove(&fdtable[i], &fdtable[i+1],
 827                     move_amt * sizeof (pollfd_t));
 828 
 829                 /*
 830                  * decrement the pid count - one less pid to worry about
 831                  */
 832                 pidcnt--;
 833         }
 834         if (i == -1)
 835                 dprintf((" - entry not found \n"));
 836 }
 837 
 838 
 839 /*
 840  * find_pid     - Returns an index into the pidtable of the specifed pid,
 841  *                else -1 if not found
 842  */
 843 
 844 static int
 845 find_pid(pid_t pid, int *i)


 846 {
 847         struct pidentry pe;
 848         struct pidentry *p;
 849 
 850         pe.pl_pid = pid;
 851         p = bsearch(&pe, pidtable, pidcnt, sizeof (struct pidentry), pidcmp);
 852 
 853         if (p == NULL)
 854                 return (0);
 855         else {
 856                 *i = p - (struct pidentry *)pidtable;
 857                 return (1);
 858         }
 859 }
 860 
 861 
 862 /*
 863  * Pidcmp - Used by besearch for sorting and finding  process IDs.
 864  */
 865 
 866 static int
 867 pidcmp(struct pidentry *a, struct pidentry *b)

 868 {
 869         if (b == NULL || a == NULL)
 870                 return (0);
 871         return (a->pl_pid - b->pl_pid);
 872 }
 873 
 874 
 875 /*
 876  * proc_to_fd   - Take a process ID and return an open file descriptor to the
 877  *                /proc file for the specified process.
 878  */
 879 static int
 880 proc_to_fd(pid_t pid)

 881 {
 882         char procname[64];
 883         int fd, dfd;
 884 
 885         (void) sprintf(procname, "/proc/%d/psinfo", (int)pid);
 886 
 887         if ((fd = open(procname, O_RDONLY)) >= 0) {
 888                 /*
 889                  * dup the fd above the low order values to assure
 890                  * stdio works for other fds - paranoia.
 891                  */
 892                 if (fd < EXTRA_MARGIN) {
 893                         dfd = fcntl(fd, F_DUPFD, EXTRA_MARGIN);
 894                         if (dfd > 0) {
 895                                 (void) close(fd);
 896                                 fd = dfd;
 897                         }
 898                 }
 899                 /*
 900                  * More paranoia - set the close on exec flag


 909                 /*
 910                  * This is fatal, since libc won't be able to allocate
 911                  * any fds for the pututxline() routines
 912                  */
 913                 fatal("Out of file descriptors");
 914         }
 915         fatal(procname);                /* Only get here on error */
 916         return (-1);
 917 }
 918 
 919 
 920 /*
 921  *              *** Utmpx Cleaning Utilities ***
 922  */
 923 
 924 /*
 925  * Clean_entry  - Cleans the specified entry - where i is an index
 926  *                into the pid_table.
 927  */
 928 static void
 929 clean_entry(int i)

 930 {
 931         struct utmpx *u;
 932 
 933         if (pidcnt == 0)
 934                 return;
 935 
 936         dprintf(("    Cleaning %d\n", (int)pidtable[i].pl_pid));
 937 
 938         /*
 939          * Double check if the process is dead.
 940          */
 941         if (proc_is_alive(pidtable[i].pl_pid)) {
 942                 dprintf(("      Bad attempt to clean %d\n",
 943                     (int)pidtable[i].pl_pid));
 944                 return;
 945         }
 946 
 947         /*
 948          * Find the entry that corresponds to this pid.
 949          * Do nothing if entry not found in utmpx file.
 950          */
 951         setutxent();
 952         while ((u = getutxent()) != NULL) {
 953                 if (u->ut_pid == pidtable[i].pl_pid) {
 954                         if (u->ut_type == USER_PROCESS) {
 955                                 clean_utmpx_ent(u);
 956                         }
 957                 }
 958         }
 959         endutxent();
 960 }
 961 
 962 
 963 /*
 964  * clean_utmpx_ent      - Clean a utmpx entry
 965  */
 966 
 967 static void
 968 clean_utmpx_ent(struct utmpx *u)

 969 {
 970         dprintf(("      clean_utmpx_ent: %d\n", (int)u->ut_pid));
 971         u->ut_type = DEAD_PROCESS;
 972         (void) time(&u->ut_xtime);
 973         (void) pututxline(u);
 974         updwtmpx(WTMPX_FILE, u);
 975         /*
 976          * XXX update wtmp for ! nonuserx entries?
 977          */
 978 }
 979 
 980 /*
 981  *              *** Error Handling and Debugging Routines ***
 982  */
 983 
 984 /*
 985  * fatal - Catastrophic failure
 986  */
 987 
 988 static void


1029         if (Debug == 0)
1030                 return;
1031 
1032         dprintf(("pidtable: "));
1033         for (i = 0; i < pidcnt; i++)
1034                 dprintf(("%d: %d  ", i, (int)pidtable[i].pl_pid));
1035         dprintf(("\n"));
1036         dprintf(("fdtable:  "));
1037         for (i = 0; i < pidcnt; i++)
1038                 dprintf(("%d: %d  ", i, fdtable[i].fd));
1039         dprintf(("\n"));
1040 }
1041 
1042 /*
1043  * proc_is_alive        - Check to see if a process is alive AND its
1044  *                        not a zombie.  Returns 1 if process is alive
1045  *                        and zero if it is dead or a zombie.
1046  */
1047 
1048 static int
1049 proc_is_alive(pid_t pid)

1050 {
1051         char psinfoname[64];
1052         int fd;
1053         psinfo_t psinfo;
1054 
1055         if (kill(pid, 0) != 0)
1056                 return (0);             /* Kill failed - no process */
1057 
1058         /*
1059          * The process exists, so check if it's a zombie.
1060          */
1061         (void) sprintf(psinfoname, "/proc/%d/psinfo", (int)pid);
1062 
1063         if ((fd = open(psinfoname, O_RDONLY)) < 0 ||
1064             read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1065                 /*
1066                  * We either couldn't open the proc, or we did but the
1067                  * read of the psinfo file failed, so pid is nonexistent.
1068                  */
1069                 psinfo.pr_nlwp = 0;


1077 
1078 /*
1079  * warn_utmp -  /var/adm/utmp has been deprecated. It should no longer
1080  *              be used.  Applications that try to directly manipulate
1081  *              it may cause problems. Since the file is no longer
1082  *              shipped, if it appears on a system it's because an
1083  *              old application created it.  We'll have utmpd
1084  *              complain about it periodically.
1085  */
1086 
1087 static void
1088 warn_utmp()
1089 {
1090         struct stat s;
1091 
1092         if (lstat(UTMP_FILE, &s) == 0 &&
1093             s.st_size % sizeof (struct utmp) == 0) {
1094                 nonfatal("WARNING: /var/adm/utmp exists!\nSee "
1095                     "utmp(4) for more information");
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);
1123 }