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
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
↓ 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
33 33 /*
34 34 * Portions of such source code were derived from Berkeley 4.3 BSD
35 35 * under license from the Regents of the University of California.
36 36 */
37 37
38 38 /*
39 39 * utmpd - utmp daemon
40 40 *
41 41 * This program receives requests from pututxline(3)
42 42 * via a named pipe to watch the process to make sure it cleans up
43 43 * its utmpx entry on termination.
44 44 * The program keeps a list of procs
45 45 * and uses poll() on their /proc files to detect termination.
46 46 * Also the program periodically scans the /etc/utmpx file for
47 47 * processes that aren't in the table so they can be watched.
48 48 *
49 49 * If utmpd doesn't hear back over the pipe from pututline(3) that
50 50 * the process has removed its entry it cleans the entry when the
51 51 * the process terminates.
52 52 * The AT&T Copyright above is there since we borrowed the pipe
53 53 * mechanism from init(1m).
54 54 */
55 55
56 56
57 57 #include <sys/types.h>
58 58 #include <signal.h>
59 59 #include <stdio.h>
60 60 #include <stdio_ext.h>
61 61 #include <unistd.h>
62 62 #include <utmpx.h>
63 63 #include <errno.h>
64 64 #include <termio.h>
65 65 #include <sys/termios.h>
66 66 #include <sys/tty.h>
67 67 #include <ctype.h>
68 68 #include <sys/stat.h>
69 69 #include <sys/statvfs.h>
70 70 #include <fcntl.h>
↓ 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
91 92 */
92 93 #define MAX_POLL_ERRS 1024 /* Count of bad errors */
93 94 #define MAX_RESETS 1024 /* Maximum times to reload tables */
94 95 #define POLL_TIMEOUT 300 /* Default Timeout for poll() in seconds */
95 96 #define CLEANIT 1 /* Used by rem_pid() */
96 97 #define DONT_CLEAN 0 /* Used by rem_pid() */
97 98 #define UTMP_DEFAULT "/etc/default/utmpd"
98 99 #define WARN_TIME 3600 /* seconds between utmp checks */
99 100 #define WTMPX_UFREQ 60 /* seconds between updating WTMPX's atime */
100 101
↓ 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 */
165 166 static int find_pid(); /* Finds a process in the table */
166 167 static int proc_to_fd(); /* Takes a pid and returns an fd for its proc */
167 168 static void load_tables(); /* Loads up the tables the first time around */
168 169 static int pidcmp(); /* For sorting pids */
↓ 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) {
200 205 if ((argc == 2 && (int)strlen(argv[1]) >= 2) &&
201 206 (argv[1][0] == '-' && argv[1][1] == 'd')) {
202 207 Debug = 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 }
245 262 (void) close(0);
246 263 (void) close(1);
247 264 (void) close(2);
248 265 /*
249 266 * We open these to avoid accidentally writing to a proc file
250 267 */
251 268 (void) open("/dev/null", O_RDONLY);
252 269 (void) open("/dev/null", O_WRONLY);
253 270 (void) open("/dev/null", O_WRONLY);
254 271 (void) setsid(); /* release process from tty */
255 272 }
256 273
257 274 openlog(prog_name, LOG_PID, LOG_DAEMON); /* For error messages */
258 275 warn_utmp(); /* check to see if utmp came back by accident */
259 276
260 277 /*
261 278 * Allocate the pidtable and fdtable. An earlier version did
262 279 * this as we go, but this is simpler.
263 280 */
264 281 if ((pidtable = malloc(Max_fds * sizeof (struct pidentry))) == NULL)
265 282 fatal("Malloc failed");
266 283 if ((fdtable = malloc(Max_fds * sizeof (pollfd_t))) == NULL)
267 284 fatal("Malloc failed");
268 285
269 286 /*
270 287 * Up the limit on FDs
271 288 */
272 289 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
273 290 rlim.rlim_cur = Max_fds + EXTRA_MARGIN + 1;
↓ 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
295 312 for (i = 0; i < MAX_RESETS; i++) {
296 313 load_tables();
297 314 while (wait_for_pids() == 1) {
298 315 now = time(NULL);
299 316 if ((now - curtime) >= WARN_TIME) {
300 317 dprintf(("utmp warning timer expired\n"));
301 318 warn_utmp();
302 319 curtime = now;
303 320 }
304 321 }
305 322 }
306 323
307 324 (void) close(WTMPXfd);
308 325
309 326 /*
310 327 * We only get here if we had a bunch of resets - so give up
311 328 */
312 329 fatal("Too many resets, giving up");
313 330 return (1);
314 331 }
315 332
316 333 /*
317 334 * load_tables() - Designed to be called repeatedly if we need to
318 335 * restart things. Zeros the pidcount, and loads
319 336 * the tables by scanning utmpx
320 337 */
321 338
322 339 static void
323 340 load_tables()
324 341 {
325 342 int i;
326 343
327 344 dprintf(("Load tables\n"));
328 345
329 346 /*
330 347 * Close any open files.
331 348 */
332 349 for (i = 0; i < pidcnt; i++)
333 350 (void) close(fdtable[i].fd);
334 351
335 352 pidcnt = 0;
336 353 Pfd = -1;
337 354 setup_pipe(); /* Setup the pipe to receive messages */
338 355 scan_utmps(); /* Read in USER procs entries to watch */
339 356 }
340 357
341 358
342 359 /*
343 360 * *** The Watcher ***
344 361 *
345 362 * Wait_for_pids - wait for the termination of a process in the table.
346 363 * Returns 1 on normal exist, 0 on failure.
347 364 */
348 365
349 366 static int
350 367 wait_for_pids()
351 368 {
352 369 register struct pollfd *pfd;
353 370 register int i;
354 371 pid_t pid;
355 372 int ret_val = 0;
356 373 int timeout;
357 374 static time_t last_timeout = 0;
358 375 static int bad_error = 0; /* Count of POLL errors */
359 376
360 377 /*
361 378 * First time through we initialize last_timeout to now.
362 379 */
363 380 if (last_timeout == 0)
364 381 last_timeout = time(NULL);
365 382
366 383 /*
367 384 * Recalculate timeout - checking to see if time expired.
368 385 */
369 386
370 387 if ((timeout = Poll_timeout - (time(NULL) - last_timeout)) <= 0) {
371 388 timeout = Poll_timeout;
372 389 last_timeout = time(NULL);
373 390 scan_utmps();
374 391 }
375 392
376 393 fdtable[0].events = POLLRDNORM;
377 394
378 395 for (i = 0; i < (timeout / WTMPX_ufreq); i++) {
379 396
380 397 /*
381 398 * Loop here while getting EAGAIN
382 399 */
383 400
384 401 while ((ret_val = poll(fdtable, pidcnt, WTMPX_ufreq*1000)) < 0)
385 402 if (errno == EAGAIN)
386 403 (void) sleep(2);
387 404 else
388 405 fatal("poll");
389 406 /*
390 407 * The results of pread(2) are discarded; we only want
391 408 * to update the access time of WTMPX_FILE.
392 409 * Periodically touching WTMPX helps determine when the
393 410 * OS became unavailable when the OS boots again .
394 411 * See PSARC 2004/462 for more information.
395 412 */
396 413
397 414 (void) pread(WTMPXfd, (void *)&pid, sizeof (pid), 0);
398 415
399 416 if (ret_val) /* file descriptor(s) need attention */
400 417 break;
401 418 }
402 419
403 420 /*
404 421 * If ret_val == 0 the poll timed out - reset last_time and
405 422 * call scan_utmps
406 423 */
407 424 if (ret_val == 0) {
408 425 last_timeout = time(NULL);
409 426 scan_utmps();
410 427 return (1);
411 428 }
412 429
413 430 /*
414 431 * Check the pipe file descriptor
415 432 */
416 433 if (fdtable[0].revents & POLLRDNORM) {
417 434 drain_pipe();
418 435 fdtable[0].revents = 0;
419 436 ret_val--;
420 437 }
421 438
422 439 (void) sleep(5); /* Give parents time to cleanup children */
423 440
424 441 /*
425 442 * We got here because the status of one of the pids that
426 443 * we are polling on has changed, so search the table looking
427 444 * for the entry.
428 445 *
429 446 * The table is scanned backwards so that entries can be removed
430 447 * while we go since the table is compacted from high down to low
431 448 */
432 449 for (i = pidcnt - 1; i > 0; i--) {
433 450 /*
434 451 * Break out of the loop if we've processed all the entries.
435 452 */
436 453 if (ret_val == 0)
437 454 break;
438 455
439 456 pfd = &fdtable[i];
440 457
441 458 if (pfd->fd < 0) {
442 459 rem_pid((pid_t)0, i, DONT_CLEAN);
443 460 continue;
444 461 }
445 462 /*
446 463 * POLLHUP - Process terminated
447 464 */
448 465 if (pfd->revents & POLLHUP) {
449 466 psinfo_t psinfo;
450 467
451 468 if (pread(pfd->fd, &psinfo, sizeof (psinfo), (off_t)0)
452 469 != sizeof (psinfo)) {
453 470 dprintf(("! %d: terminated, status 0x%.4x\n", \
454 471 (int)pidtable[i].pl_pid, psinfo.pr_wstat));
455 472 pidtable[i].pl_status = psinfo.pr_wstat;
456 473
457 474 } else {
458 475 dprintf(("! %d: terminated\n", \
459 476 (int)pidtable[i].pl_pid));
460 477 pidtable[i].pl_status = 0;
461 478 }
462 479 /*
463 480 * PID gets removed when terminated only
464 481 */
465 482 rem_pid((pid_t)0, i, CLEANIT);
466 483 ret_val--;
467 484 continue;
468 485 }
469 486 /*
470 487 * POLLNVAL and POLLERR
471 488 * These error's shouldn't occurr but until their fixed
472 489 * we perform some simple error recovery.
473 490 */
474 491 if (pfd->revents & (POLLNVAL|POLLERR)) {
475 492 dprintf(("Poll Err = %d pid = %d i = %d\n", \
476 493 pfd->revents, (int)pidtable[i].pl_pid, i));
477 494
478 495 pid = pidtable[i].pl_pid; /* Save pid for below */
479 496 /*
480 497 * If its POLLNVAL we just remove the process for
481 498 * now, it will get picked up in the next scan.
482 499 * POLLERR pids get re-added after being deleted.
483 500 */
484 501 if (pfd->revents & POLLNVAL) {
485 502 rem_pid((pid_t)0, i, DONT_CLEAN);
486 503 } else { /* Else... POLLERR */
487 504 rem_pid((pid_t)0, i, DONT_CLEAN);
488 505 add_pid(pid);
489 506 }
490 507
491 508 if (bad_error++ > MAX_POLL_ERRS) {
492 509 bad_error = 0;
493 510 return (0); /* 0 Indicates severe error */
494 511 }
495 512 ret_val--;
496 513 continue;
497 514 }
498 515
499 516 /*
500 517 * No more bits should be set in revents but check anyway
501 518 */
502 519 if (pfd->revents != 0) {
503 520 dprintf(("%d: unknown err %d\n", \
504 521 (int)pidtable[i].pl_pid, pfd->revents));
505 522
506 523 rem_pid((pid_t)0, i, DONT_CLEAN);
507 524 ret_val--;
508 525
509 526 if (bad_error++ > MAX_POLL_ERRS) {
510 527 bad_error = 0;
511 528 return (0); /* 0 Indicates severe error */
512 529 }
513 530 return (1);
514 531 }
515 532 }
516 533 return (1); /* 1 Indicates Everything okay */
517 534 }
518 535
519 536 /*
520 537 * *** The Scanner ***
521 538 *
522 539 * scan_utmps() - Scan the utmpx file.
523 540 * For each USER_PROCESS check
524 541 * if its alive or dead. If alive and its not in
525 542 * our table to be watched, put it there. If its
526 543 * dead, remove it from our table and clean it up.
527 544 */
528 545
529 546 static void
530 547 scan_utmps()
531 548 {
532 549 struct utmpx *utmpx;
533 550 int i;
534 551
535 552 dprintf(("Scan utmps\n"));
536 553 /*
537 554 * Scan utmpx.
538 555 */
539 556 setutxent();
540 557 while ((utmpx = getutxent()) != NULL) {
541 558 if (utmpx->ut_type == USER_PROCESS) {
542 559 /*
543 560 * Is the process alive?
544 561 */
545 562 if (proc_is_alive(utmpx->ut_pid)) {
546 563 /*
547 564 * Yes, the process is alive, so add it if we
548 565 * don't have it in our table.
549 566 */
550 567 if (find_pid(utmpx->ut_pid, &i) == 0)
551 568 add_pid(utmpx->ut_pid); /* No, add it */
552 569 } else {
553 570 /*
554 571 * No, the process is dead, so remove it if its
555 572 * in our table, otherwise just clean it.
556 573 */
557 574 if (find_pid(utmpx->ut_pid, &i) == 1)
558 575 rem_pid(utmpx->ut_pid, i, CLEANIT);
559 576 else
560 577 clean_utmpx_ent(utmpx);
561 578 }
562 579 }
563 580 }
564 581 /*
565 582 * Close it to flush the buffer.
566 583 */
567 584 endutxent();
568 585 }
569 586
570 587
571 588 /*
572 589 * *** Receiver Routines ***
573 590 */
574 591
575 592 /*
576 593 * setup_pipe - Set up the pipe to read pids over
577 594 */
578 595
579 596 static void
580 597 setup_pipe()
581 598 {
582 599
583 600 struct statvfs statvfs_buf;
584 601 /*
585 602 * This code & comments swiped from init and left stock since it works
586 603 */
587 604
588 605 if (Pfd < 0) {
589 606 if ((statvfs(UTMPPIPE_DIR, &statvfs_buf) == 0) &&
590 607 ((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
591 608 (void) unlink(UTMPPIPE);
592 609 (void) mknod(UTMPPIPE, S_IFIFO | 0600, 0);
593 610 }
594 611 Pfd = open(UTMPPIPE, O_RDWR | O_NDELAY);
595 612 }
596 613 if (Pfd < 0)
597 614 nonfatal(UTMPPIPE);
598 615 /*
599 616 * This code from init modified to be poll based instead of SIGPOLL,
600 617 * signal based.
601 618 */
602 619
603 620 if (Pfd >= 0) {
604 621 /*
605 622 * Read pipe in message discard mode. When read reads a
606 623 * pidrec size record, the remainder of the message will
607 624 * be discarded. Though there shouldn't be any it will
608 625 * help resynch if someone else wrote some garbage.
609 626 */
610 627 (void) ioctl(Pfd, I_SRDOPT, RMSGD);
611 628 }
612 629
613 630 /*
614 631 * My code. We use slot 0 in the table to hold the fd of the pipe
615 632 */
616 633 add_pid(0); /* Proc 0 guaranteed to get slot 0 */
617 634 fdtable[0].fd = Pfd; /* Pfd could be -1, should be okay */
618 635 fdtable[0].events = POLLRDNORM;
619 636 }
620 637
621 638 /*
622 639 * drain_pipe() - The receiver routine that reads the pipe
623 640 */
624 641
625 642 static void
626 643 drain_pipe()
627 644 {
628 645 struct pidrec prec;
629 646 register struct pidrec *p = ≺
630 647 int bytes_read;
631 648 int i;
632 649
633 650 for (;;) {
634 651 /*
635 652 * Important Note: Either read will really fail (in which case
636 653 * return is all we can do) or will get EAGAIN (Pfd was opened
637 654 * O_NDELAY), in which case we also want to return.
638 655 */
639 656
640 657 if ((bytes_read = read(Pfd, p, sizeof (struct pidrec))) !=
641 658 sizeof (struct pidrec)) {
642 659 /*
643 660 * Something went wrong reading, so read until pipe
644 661 * is empty
645 662 */
646 663 if (bytes_read > 0)
647 664 while (read(Pfd, p, sizeof (struct pidrec)) > 0)
648 665 ;
649 666 return;
650 667 }
651 668
652 669 dprintf(("drain_pipe: Recd command %d, pid %d\n",
653 670 p->pd_type, (int)p->pd_pid));
654 671 switch (p->pd_type) {
655 672 case ADDPID:
656 673 /*
657 674 * Check if we already have the process, adding it
658 675 * if we don't.
659 676 */
660 677 if (find_pid(p->pd_pid, &i) == 0)
661 678 add_pid(p->pd_pid);
662 679 break;
663 680
664 681 case REMPID:
665 682 rem_pid(p->pd_pid, -1, DONT_CLEAN);
666 683 break;
667 684 default:
668 685 nonfatal("Bad message on utmppipe\n");
669 686 break;
670 687 }
671 688 }
672 689 }
673 690
674 691
↓ 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 */
697 713 if (pidcnt != 0 && (find_pid(pid, &j) == 1 || pid == 0))
698 714 return;
699 715
700 716 if (pidcnt >= Max_fds) {
701 717 if (first_time == 1) {
702 718 /*
703 719 * Print this error only once
704 720 */
705 721 nonfatal("File Descriptor limit exceeded");
706 722 first_time = 0;
707 723 }
708 724 return;
709 725 }
710 726 /*
711 727 * Open the /proc file checking if there's still a valid proc file.
712 728 */
713 729 if (pid != 0 && (fd = proc_to_fd(pid)) == -1) {
714 730 /*
715 731 * No so the process died before we got to watch for him
716 732 */
717 733 return;
718 734 }
719 735
720 736 /*
721 737 * We only do this code if we're not putting in the first element
722 738 * Which we know will be for proc zero which is used by setup_pipe
723 739 * for its pipe fd.
724 740 */
725 741 if (pidcnt != 0) {
726 742 for (i = 0; i < pidcnt; i++) {
727 743 if (pid <= pidtable[i].pl_pid)
728 744 break;
729 745 }
730 746
731 747 /*
732 748 * Handle the case where we're not sticking our entry on the
↓ 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"));
789 808 return;
790 809 }
791 810
792 811 if (i != -1 || find_pid(pid, &i) == 1) { /* Found the entry */
793 812 (void) close(fdtable[i].fd); /* We're done with the fd */
794 813
↓ 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 {
839 856 *i = p - (struct pidentry *)pidtable;
↓ 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.
876 891 */
877 892 if (fd < EXTRA_MARGIN) {
878 893 dfd = fcntl(fd, F_DUPFD, EXTRA_MARGIN);
879 894 if (dfd > 0) {
880 895 (void) close(fd);
881 896 fd = dfd;
882 897 }
883 898 }
884 899 /*
885 900 * More paranoia - set the close on exec flag
886 901 */
887 902 (void) fcntl(fd, F_SETFD, 1);
888 903 return (fd);
889 904 }
890 905 if (errno == ENOENT)
891 906 return (-1);
892 907
893 908 if (errno == EMFILE) {
894 909 /*
895 910 * This is fatal, since libc won't be able to allocate
896 911 * any fds for the pututxline() routines
897 912 */
898 913 fatal("Out of file descriptors");
899 914 }
900 915 fatal(procname); /* Only get here on error */
901 916 return (-1);
902 917 }
903 918
↓ 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) {
940 954 if (u->ut_type == USER_PROCESS) {
941 955 clean_utmpx_ent(u);
942 956 }
943 957 }
↓ 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 }
966 979
967 980 /*
968 981 * *** Error Handling and Debugging Routines ***
969 982 */
970 983
971 984 /*
972 985 * fatal - Catastrophic failure
973 986 */
974 987
975 988 static void
976 989 fatal(char *str)
977 990 {
978 991 int oerrno = errno;
979 992
980 993 syslog(LOG_ALERT, "%s", str);
981 994 if (Debug == 1) {
982 995 if ((errno = oerrno) != 0)
983 996 perror(prog_name);
984 997 dprintf(("%s\n", str));
985 998 }
986 999 exit(1);
987 1000 }
988 1001
989 1002 /*
990 1003 * nonfatal - Non-Catastrophic failure - print message and errno
991 1004 */
992 1005
993 1006 static void
994 1007 nonfatal(char *str)
995 1008 {
996 1009 syslog(LOG_WARNING, "%s", str);
997 1010
998 1011 if (Debug == 1) {
999 1012 if (errno != 0)
1000 1013 perror(prog_name);
1001 1014 dprintf(("%c%s\n", 7, str));
1002 1015 print_tables();
1003 1016 (void) sleep(5); /* Time to read debug messages */
1004 1017 }
1005 1018 }
1006 1019
1007 1020 /*
1008 1021 * print_tables - Print internal tables - for debugging
1009 1022 */
1010 1023
1011 1024 static void
1012 1025 print_tables()
1013 1026 {
1014 1027 int i;
1015 1028
1016 1029 if (Debug == 0)
1017 1030 return;
1018 1031
1019 1032 dprintf(("pidtable: "));
1020 1033 for (i = 0; i < pidcnt; i++)
1021 1034 dprintf(("%d: %d ", i, (int)pidtable[i].pl_pid));
1022 1035 dprintf(("\n"));
1023 1036 dprintf(("fdtable: "));
1024 1037 for (i = 0; i < pidcnt; i++)
1025 1038 dprintf(("%d: %d ", i, fdtable[i].fd));
↓ 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.
1048 1060 */
1049 1061 (void) sprintf(psinfoname, "/proc/%d/psinfo", (int)pid);
1050 1062
1051 1063 if ((fd = open(psinfoname, O_RDONLY)) < 0 ||
1052 1064 read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1053 1065 /*
1054 1066 * We either couldn't open the proc, or we did but the
1055 1067 * read of the psinfo file failed, so pid is nonexistent.
1056 1068 */
1057 1069 psinfo.pr_nlwp = 0;
1058 1070 }
1059 1071 if (fd >= 0)
1060 1072 (void) close(fd);
1061 1073
1062 1074 /* if pr_nlwp == 0, process is a zombie */
1063 1075 return (psinfo.pr_nlwp != 0);
1064 1076 }
1065 1077
1066 1078 /*
1067 1079 * warn_utmp - /var/adm/utmp has been deprecated. It should no longer
1068 1080 * be used. Applications that try to directly manipulate
1069 1081 * it may cause problems. Since the file is no longer
1070 1082 * shipped, if it appears on a system it's because an
1071 1083 * old application created it. We'll have utmpd
1072 1084 * complain about it periodically.
1073 1085 */
1074 1086
↓ 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