Print this page
3124 Remove any existing references to utmp, use utmpx instead
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.
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 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
↓ open down ↓ |
23 lines elided |
↑ open up ↑ |
24 24 */
25 25
26 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 27 /* All Rights Reserved */
28 28
29 29 /*
30 30 * Portions of such source code were derived from Berkeley 4.3 BSD
31 31 * under license from the Regents of the University of California.
32 32 */
33 33
34 -#pragma ident "%Z%%M% %I% %E% SMI"
35 -
36 34 /*
37 35 * utmpd - utmp daemon
38 36 *
39 37 * This program receives requests from pututxline(3)
40 38 * via a named pipe to watch the process to make sure it cleans up
41 39 * its utmpx entry on termination.
42 40 * The program keeps a list of procs
43 41 * and uses poll() on their /proc files to detect termination.
44 42 * Also the program periodically scans the /etc/utmpx file for
45 43 * processes that aren't in the table so they can be watched.
46 44 *
47 45 * If utmpd doesn't hear back over the pipe from pututline(3) that
48 46 * the process has removed its entry it cleans the entry when the
49 47 * the process terminates.
50 48 * The AT&T Copyright above is there since we borrowed the pipe
51 49 * mechanism from init(1m).
52 50 */
53 51
54 52
55 53 #include <sys/types.h>
56 54 #include <signal.h>
57 55 #include <stdio.h>
58 56 #include <stdio_ext.h>
59 57 #include <unistd.h>
60 58 #include <utmpx.h>
61 59 #include <errno.h>
62 60 #include <termio.h>
63 61 #include <sys/termios.h>
64 62 #include <sys/tty.h>
65 63 #include <ctype.h>
66 64 #include <sys/stat.h>
67 65 #include <sys/statvfs.h>
68 66 #include <fcntl.h>
69 67 #include <time.h>
70 68 #include <sys/stropts.h>
71 69 #include <wait.h>
72 70 #include <syslog.h>
73 71 #include <stdlib.h>
74 72 #include <string.h>
75 73 #include <poll.h>
76 74 #include <deflt.h>
77 75 #include <procfs.h>
78 76 #include <sys/resource.h>
79 77
80 78 #define dprintf(x) if (Debug) (void) printf x
81 79
82 80 /*
83 81 * Memory allocation keyed off MAX_FDS
84 82 */
85 83 #define MAX_FDS 4064 /* Maximum # file descriptors */
86 84 #define EXTRA_MARGIN 32 /* Allocate this many more FDS over Max_Fds */
87 85 /*
88 86 * MAX_POLLNV & RESETS - paranoia to cover an error case that might not exist
89 87 */
90 88 #define MAX_POLL_ERRS 1024 /* Count of bad errors */
91 89 #define MAX_RESETS 1024 /* Maximum times to reload tables */
92 90 #define POLL_TIMEOUT 300 /* Default Timeout for poll() in seconds */
93 91 #define CLEANIT 1 /* Used by rem_pid() */
94 92 #define DONT_CLEAN 0 /* Used by rem_pid() */
95 93 #define UTMP_DEFAULT "/etc/default/utmpd"
96 94 #define WARN_TIME 3600 /* seconds between utmp checks */
97 95 #define WTMPX_UFREQ 60 /* seconds between updating WTMPX's atime */
98 96
99 97
100 98 /*
101 99 * The pidrec structure describes the data shipped down the pipe to
102 100 * us from the pututxline() library in
103 101 * lib/libc/port/gen/getutx.c
104 102 */
105 103
106 104 /*
107 105 * pd_type's
108 106 */
109 107 #define ADDPID 1
110 108 #define REMPID 2
111 109
112 110 struct pidrec {
113 111 int pd_type; /* Command type */
114 112 pid_t pd_pid; /* pid to add or remove */
115 113 };
116 114
117 115
118 116 /*
119 117 * Since this program uses poll(2) and poll takes an array of file descriptors
120 118 * as an argument we maintain our data in tables.
121 119 * One table is the file descriptor array for poll, another parallel
122 120 * array is a table which contains the process ID of the corresponding
123 121 * open fd. These tables are kept sorted by process ID for quick lookups.
124 122 */
125 123
126 124 struct pidentry {
127 125 pid_t pl_pid; /* pid to watch for */
128 126 int pl_status; /* Exit status of proc */
129 127 };
130 128
131 129 static struct pidentry *pidtable = NULL;
132 130
133 131 static pollfd_t *fdtable = NULL;
134 132
135 133 static int pidcnt = 0; /* Number of procs being watched */
136 134 static char *prog_name; /* To save the invocation name away */
137 135 static char *UTMPPIPE_DIR = "/var/run";
138 136 static char *UTMPPIPE = "/var/run/utmppipe";
139 137 static int Pfd = -1; /* File descriptor of named pipe */
140 138 static int Poll_timeout = POLL_TIMEOUT;
141 139 static int WTMPXfd = -1; /* File descriptor of WTMPX_FILE */
142 140 static int WTMPX_ufreq = WTMPX_UFREQ;
143 141 static int Debug = 0; /* Set by command line argument */
144 142 static int Max_fds = MAX_FDS;
145 143
146 144 /*
147 145 * This program has three main components plus utilities and debug routines
148 146 * Receiver - receives the process ID or process for us to watch.
149 147 * (Uses a named pipe to get messages)
150 148 * Watcher - Use poll(2) to watch for processes to die so they
151 149 * can be cleaned up (get marked as DEAD_PROCESS)
152 150 * Scanner - periodically scans the utmpx file for stale entries
153 151 * or live entries that we don't know about.
154 152 */
155 153
156 154 static int wait_for_pids(); /* Watcher - uses poll */
157 155 static void scan_utmps(); /* Scanner, reads utmpx file */
158 156 static void drain_pipe(); /* Receiver - reads mesgs over UTMPPIPE */
159 157 static void setup_pipe(); /* For setting up receiver */
160 158
161 159 static void add_pid(); /* Adds a process to the table */
162 160 static void rem_pid(); /* Removes a process from the table */
163 161 static int find_pid(); /* Finds a process in the table */
164 162 static int proc_to_fd(); /* Takes a pid and returns an fd for its proc */
165 163 static void load_tables(); /* Loads up the tables the first time around */
166 164 static int pidcmp(); /* For sorting pids */
167 165
168 166 static void clean_entry(); /* Removes entry from our table and calls ... */
169 167 static void clean_utmpx_ent(); /* Cleans a utmpx entry */
170 168
171 169 static void fatal() __NORETURN; /* Prints error message and calls exit */
172 170 static void nonfatal(); /* Prints error message */
173 171 static void print_tables(); /* Prints out internal tables for Debug */
174 172 static int proc_is_alive(pid_t pid); /* Check if a process is alive */
175 173 static void warn_utmp(void);
176 174
177 175 /*
178 176 * main() - Main does basic setup and calls wait_for_pids() to do the work
179 177 */
180 178
181 179 int
182 180 main(argc, argv)
183 181 char **argv;
184 182 {
185 183 char *defp;
186 184 struct rlimit rlim;
187 185 int i;
188 186 time_t curtime, now;
189 187
190 188 prog_name = argv[0]; /* Save invocation name */
191 189
192 190 if (getuid() != 0) {
193 191 (void) fprintf(stderr,
194 192 "You must be root to run this program\n");
195 193 fatal("You must be root to run this program");
196 194 }
197 195
198 196 if (argc > 1) {
199 197 if ((argc == 2 && (int)strlen(argv[1]) >= 2) &&
200 198 (argv[1][0] == '-' && argv[1][1] == 'd')) {
201 199 Debug = 1;
202 200 } else {
203 201 (void) fprintf(stderr,
204 202 "%s: Wrong number of arguments\n", prog_name);
205 203 (void) fprintf(stderr,
206 204 "Usage: %s [-debug]\n", prog_name);
207 205 exit(2);
208 206 }
209 207 }
210 208
211 209 /*
212 210 * Read defaults file for poll timeout
213 211 */
214 212 if (defopen(UTMP_DEFAULT) == 0) {
215 213 if ((defp = defread("SCAN_PERIOD=")) != NULL) {
216 214 Poll_timeout = atol(defp);
217 215 dprintf(("Poll timeout set to %d\n", Poll_timeout));
218 216 }
219 217
220 218 if ((defp = defread("WTMPX_UPDATE_FREQ=")) != NULL) {
221 219 WTMPX_ufreq = atol(defp);
222 220 dprintf(("WTMPX update frequency set to %d\n",
223 221 WTMPX_ufreq));
224 222 }
225 223
226 224 /*
227 225 * Paranoia - if polling on large number of FDs is expensive /
228 226 * buggy the number can be set lower in the field.
229 227 */
230 228 if ((defp = defread("MAX_FDS=")) != NULL) {
231 229 Max_fds = atol(defp);
232 230 dprintf(("Max_fds set to %d\n", Max_fds));
233 231 }
234 232 (void) defopen((char *)NULL);
235 233 }
236 234
237 235 if (Debug == 0) {
238 236 /*
239 237 * Daemonize ourselves
240 238 */
241 239 if (fork()) {
242 240 exit(0);
243 241 }
244 242 (void) close(0);
245 243 (void) close(1);
246 244 (void) close(2);
247 245 /*
248 246 * We open these to avoid accidentally writing to a proc file
249 247 */
250 248 (void) open("/dev/null", O_RDONLY);
251 249 (void) open("/dev/null", O_WRONLY);
252 250 (void) open("/dev/null", O_WRONLY);
253 251 (void) setsid(); /* release process from tty */
254 252 }
255 253
256 254 openlog(prog_name, LOG_PID, LOG_DAEMON); /* For error messages */
257 255 warn_utmp(); /* check to see if utmp came back by accident */
258 256
259 257 /*
260 258 * Allocate the pidtable and fdtable. An earlier version did
261 259 * this as we go, but this is simpler.
262 260 */
263 261 if ((pidtable = malloc(Max_fds * sizeof (struct pidentry))) == NULL)
264 262 fatal("Malloc failed");
265 263 if ((fdtable = malloc(Max_fds * sizeof (pollfd_t))) == NULL)
266 264 fatal("Malloc failed");
267 265
268 266 /*
269 267 * Up the limit on FDs
270 268 */
271 269 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
272 270 rlim.rlim_cur = Max_fds + EXTRA_MARGIN + 1;
273 271 rlim.rlim_max = Max_fds + EXTRA_MARGIN + 1;
274 272 if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
275 273 fatal("Out of File Descriptors");
276 274 }
277 275 } else
278 276 fatal("getrlimit returned failure");
279 277
280 278 (void) enable_extended_FILE_stdio(-1, -1);
281 279
282 280 if ((WTMPXfd = open(WTMPX_FILE, O_RDONLY)) < 0)
283 281 nonfatal("WARNING: unable to open " WTMPX_FILE "for update.");
284 282
285 283 /*
286 284 * Loop here scanning the utmpx file and waiting for processes
287 285 * to terminate. Most of the activity is directed out of wait_for_pids.
288 286 * If wait_for_pids fails we reload the table and try again.
289 287 */
290 288
291 289 curtime = time(NULL);
292 290 dprintf(("utmp warning timer set to %d seconds\n", WARN_TIME));
293 291
294 292 for (i = 0; i < MAX_RESETS; i++) {
295 293 load_tables();
296 294 while (wait_for_pids() == 1) {
297 295 now = time(NULL);
298 296 if ((now - curtime) >= WARN_TIME) {
299 297 dprintf(("utmp warning timer expired\n"));
300 298 warn_utmp();
301 299 curtime = now;
302 300 }
303 301 }
304 302 }
305 303
306 304 (void) close(WTMPXfd);
307 305
308 306 /*
309 307 * We only get here if we had a bunch of resets - so give up
310 308 */
311 309 fatal("Too many resets, giving up");
312 310 return (1);
313 311 }
314 312
315 313 /*
316 314 * load_tables() - Designed to be called repeatedly if we need to
317 315 * restart things. Zeros the pidcount, and loads
318 316 * the tables by scanning utmpx
319 317 */
320 318
321 319 static void
322 320 load_tables()
323 321 {
324 322 int i;
325 323
326 324 dprintf(("Load tables\n"));
327 325
328 326 /*
329 327 * Close any open files.
330 328 */
331 329 for (i = 0; i < pidcnt; i++)
332 330 (void) close(fdtable[i].fd);
333 331
334 332 pidcnt = 0;
335 333 Pfd = -1;
336 334 setup_pipe(); /* Setup the pipe to receive messages */
337 335 scan_utmps(); /* Read in USER procs entries to watch */
338 336 }
339 337
340 338
341 339 /*
342 340 * *** The Watcher ***
343 341 *
344 342 * Wait_for_pids - wait for the termination of a process in the table.
345 343 * Returns 1 on normal exist, 0 on failure.
346 344 */
347 345
348 346 static int
349 347 wait_for_pids()
350 348 {
351 349 register struct pollfd *pfd;
352 350 register int i;
353 351 pid_t pid;
354 352 int ret_val;
355 353 int timeout;
356 354 static time_t last_timeout = 0;
357 355 static int bad_error = 0; /* Count of POLL errors */
358 356
359 357 /*
360 358 * First time through we initialize last_timeout to now.
361 359 */
362 360 if (last_timeout == 0)
363 361 last_timeout = time(NULL);
364 362
365 363 /*
366 364 * Recalculate timeout - checking to see if time expired.
367 365 */
368 366
369 367 if ((timeout = Poll_timeout - (time(NULL) - last_timeout)) <= 0) {
370 368 timeout = Poll_timeout;
371 369 last_timeout = time(NULL);
372 370 scan_utmps();
373 371 }
374 372
375 373 fdtable[0].events = POLLRDNORM;
376 374
377 375 for (i = 0; i < (timeout / WTMPX_ufreq); i++) {
378 376
379 377 /*
380 378 * Loop here while getting EAGAIN
381 379 */
382 380
383 381 while ((ret_val = poll(fdtable, pidcnt, WTMPX_ufreq*1000)) < 0)
384 382 if (errno == EAGAIN)
385 383 (void) sleep(2);
386 384 else
387 385 fatal("poll");
388 386 /*
389 387 * The results of pread(2) are discarded; we only want
390 388 * to update the access time of WTMPX_FILE.
391 389 * Periodically touching WTMPX helps determine when the
392 390 * OS became unavailable when the OS boots again .
393 391 * See PSARC 2004/462 for more information.
394 392 */
395 393
396 394 (void) pread(WTMPXfd, (void *)&pid, sizeof (pid), 0);
397 395
398 396 if (ret_val) /* file descriptor(s) need attention */
399 397 break;
400 398 }
401 399
402 400 /*
403 401 * If ret_val == 0 the poll timed out - reset last_time and
404 402 * call scan_utmps
405 403 */
406 404 if (ret_val == 0) {
407 405 last_timeout = time(NULL);
408 406 scan_utmps();
409 407 return (1);
410 408 }
411 409
412 410 /*
413 411 * Check the pipe file descriptor
414 412 */
415 413 if (fdtable[0].revents & POLLRDNORM) {
416 414 drain_pipe();
417 415 fdtable[0].revents = 0;
418 416 ret_val--;
419 417 }
420 418
421 419 (void) sleep(5); /* Give parents time to cleanup children */
422 420
423 421 /*
424 422 * We got here because the status of one of the pids that
425 423 * we are polling on has changed, so search the table looking
426 424 * for the entry.
427 425 *
428 426 * The table is scanned backwards so that entries can be removed
429 427 * while we go since the table is compacted from high down to low
430 428 */
431 429 for (i = pidcnt - 1; i > 0; i--) {
432 430 /*
433 431 * Break out of the loop if we've processed all the entries.
434 432 */
435 433 if (ret_val == 0)
436 434 break;
437 435
438 436 pfd = &fdtable[i];
439 437
440 438 if (pfd->fd < 0) {
441 439 rem_pid((pid_t)0, i, DONT_CLEAN);
442 440 continue;
443 441 }
444 442 /*
445 443 * POLLHUP - Process terminated
446 444 */
447 445 if (pfd->revents & POLLHUP) {
448 446 psinfo_t psinfo;
449 447
450 448 if (pread(pfd->fd, &psinfo, sizeof (psinfo), (off_t)0)
451 449 != sizeof (psinfo)) {
452 450 dprintf(("! %d: terminated, status 0x%.4x\n", \
453 451 (int)pidtable[i].pl_pid, psinfo.pr_wstat));
454 452 pidtable[i].pl_status = psinfo.pr_wstat;
455 453
456 454 } else {
457 455 dprintf(("! %d: terminated\n", \
458 456 (int)pidtable[i].pl_pid));
459 457 pidtable[i].pl_status = 0;
460 458 }
461 459 /*
462 460 * PID gets removed when terminated only
463 461 */
464 462 rem_pid((pid_t)0, i, CLEANIT);
465 463 ret_val--;
466 464 continue;
467 465 }
468 466 /*
469 467 * POLLNVAL and POLLERR
470 468 * These error's shouldn't occurr but until their fixed
471 469 * we perform some simple error recovery.
472 470 */
473 471 if (pfd->revents & (POLLNVAL|POLLERR)) {
474 472 dprintf(("Poll Err = %d pid = %d i = %d\n", \
475 473 pfd->revents, (int)pidtable[i].pl_pid, i));
476 474
477 475 pid = pidtable[i].pl_pid; /* Save pid for below */
478 476 /*
479 477 * If its POLLNVAL we just remove the process for
480 478 * now, it will get picked up in the next scan.
481 479 * POLLERR pids get re-added after being deleted.
482 480 */
483 481 if (pfd->revents & POLLNVAL) {
484 482 rem_pid((pid_t)0, i, DONT_CLEAN);
485 483 } else { /* Else... POLLERR */
486 484 rem_pid((pid_t)0, i, DONT_CLEAN);
487 485 add_pid(pid);
488 486 }
489 487
490 488 if (bad_error++ > MAX_POLL_ERRS) {
491 489 bad_error = 0;
492 490 return (0); /* 0 Indicates severe error */
493 491 }
494 492 ret_val--;
495 493 continue;
496 494 }
497 495
498 496 /*
499 497 * No more bits should be set in revents but check anyway
500 498 */
501 499 if (pfd->revents != 0) {
502 500 dprintf(("%d: unknown err %d\n", \
503 501 (int)pidtable[i].pl_pid, pfd->revents));
504 502
505 503 rem_pid((pid_t)0, i, DONT_CLEAN);
506 504 ret_val--;
507 505
508 506 if (bad_error++ > MAX_POLL_ERRS) {
509 507 bad_error = 0;
510 508 return (0); /* 0 Indicates severe error */
511 509 }
512 510 return (1);
513 511 }
514 512 }
515 513 return (1); /* 1 Indicates Everything okay */
516 514 }
517 515
518 516 /*
519 517 * *** The Scanner ***
520 518 *
521 519 * scan_utmps() - Scan the utmpx file.
522 520 * For each USER_PROCESS check
523 521 * if its alive or dead. If alive and its not in
524 522 * our table to be watched, put it there. If its
525 523 * dead, remove it from our table and clean it up.
526 524 */
527 525
528 526 static void
529 527 scan_utmps()
530 528 {
531 529 struct utmpx *utmpx;
532 530 int i;
533 531
534 532 dprintf(("Scan utmps\n"));
535 533 /*
536 534 * Scan utmpx.
537 535 */
538 536 setutxent();
539 537 while ((utmpx = getutxent()) != NULL) {
540 538 if (utmpx->ut_type == USER_PROCESS) {
541 539 /*
542 540 * Is the process alive?
543 541 */
544 542 if (proc_is_alive(utmpx->ut_pid)) {
545 543 /*
546 544 * Yes, the process is alive, so add it if we
547 545 * don't have it in our table.
548 546 */
549 547 if (find_pid(utmpx->ut_pid, &i) == 0)
550 548 add_pid(utmpx->ut_pid); /* No, add it */
551 549 } else {
552 550 /*
553 551 * No, the process is dead, so remove it if its
554 552 * in our table, otherwise just clean it.
555 553 */
556 554 if (find_pid(utmpx->ut_pid, &i) == 1)
557 555 rem_pid(utmpx->ut_pid, i, CLEANIT);
558 556 else
559 557 clean_utmpx_ent(utmpx);
560 558 }
561 559 }
562 560 }
563 561 /*
564 562 * Close it to flush the buffer.
565 563 */
566 564 endutxent();
567 565 }
568 566
569 567
570 568 /*
571 569 * *** Receiver Routines ***
572 570 */
573 571
574 572 /*
575 573 * setup_pipe - Set up the pipe to read pids over
576 574 */
577 575
578 576 static void
579 577 setup_pipe()
580 578 {
581 579
582 580 struct statvfs statvfs_buf;
583 581 /*
584 582 * This code & comments swiped from init and left stock since it works
585 583 */
586 584
587 585 if (Pfd < 0) {
588 586 if ((statvfs(UTMPPIPE_DIR, &statvfs_buf) == 0) &&
589 587 ((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
590 588 (void) unlink(UTMPPIPE);
591 589 (void) mknod(UTMPPIPE, S_IFIFO | 0600, 0);
592 590 }
593 591 Pfd = open(UTMPPIPE, O_RDWR | O_NDELAY);
594 592 }
595 593 if (Pfd < 0)
596 594 nonfatal(UTMPPIPE);
597 595 /*
598 596 * This code from init modified to be poll based instead of SIGPOLL,
599 597 * signal based.
600 598 */
601 599
602 600 if (Pfd >= 0) {
603 601 /*
604 602 * Read pipe in message discard mode. When read reads a
605 603 * pidrec size record, the remainder of the message will
606 604 * be discarded. Though there shouldn't be any it will
607 605 * help resynch if someone else wrote some garbage.
608 606 */
609 607 (void) ioctl(Pfd, I_SRDOPT, RMSGD);
610 608 }
611 609
612 610 /*
613 611 * My code. We use slot 0 in the table to hold the fd of the pipe
614 612 */
615 613 add_pid(0); /* Proc 0 guaranteed to get slot 0 */
616 614 fdtable[0].fd = Pfd; /* Pfd could be -1, should be okay */
617 615 fdtable[0].events = POLLRDNORM;
618 616 }
619 617
620 618 /*
621 619 * drain_pipe() - The receiver routine that reads the pipe
622 620 */
623 621
624 622 static void
625 623 drain_pipe()
626 624 {
627 625 struct pidrec prec;
628 626 register struct pidrec *p = ≺
629 627 int bytes_read;
630 628 int i;
631 629
632 630 for (;;) {
633 631 /*
634 632 * Important Note: Either read will really fail (in which case
635 633 * return is all we can do) or will get EAGAIN (Pfd was opened
636 634 * O_NDELAY), in which case we also want to return.
637 635 */
638 636
639 637 if ((bytes_read = read(Pfd, p, sizeof (struct pidrec))) !=
640 638 sizeof (struct pidrec)) {
641 639 /*
642 640 * Something went wrong reading, so read until pipe
643 641 * is empty
644 642 */
645 643 if (bytes_read > 0)
646 644 while (read(Pfd, p, sizeof (struct pidrec)) > 0)
647 645 ;
648 646 return;
649 647 }
650 648
651 649 dprintf(("drain_pipe: Recd command %d, pid %d\n",
652 650 p->pd_type, (int)p->pd_pid));
653 651 switch (p->pd_type) {
654 652 case ADDPID:
655 653 /*
656 654 * Check if we already have the process, adding it
657 655 * if we don't.
658 656 */
659 657 if (find_pid(p->pd_pid, &i) == 0)
660 658 add_pid(p->pd_pid);
661 659 break;
662 660
663 661 case REMPID:
664 662 rem_pid(p->pd_pid, -1, DONT_CLEAN);
665 663 break;
666 664 default:
667 665 nonfatal("Bad message on utmppipe\n");
668 666 break;
669 667 }
670 668 }
671 669 }
672 670
673 671
674 672 /*
675 673 * *** Utilities for add and removing entries in the tables ***
676 674 */
677 675
678 676 /*
679 677 * add_pid - add a pid to the fd table and the pidtable.
680 678 * these tables are sorted tables for quick lookups.
681 679 *
682 680 */
683 681 static void
684 682 add_pid(pid)
685 683 pid_t pid;
686 684 {
687 685 int fd = 0;
688 686 int i = 0, move_amt;
689 687 int j;
690 688 static int first_time = 1;
691 689
692 690 /*
693 691 * Check to see if the pid is already in our table, or being passed
694 692 * pid zero.
695 693 */
696 694 if (pidcnt != 0 && (find_pid(pid, &j) == 1 || pid == 0))
697 695 return;
698 696
699 697 if (pidcnt >= Max_fds) {
700 698 if (first_time == 1) {
701 699 /*
702 700 * Print this error only once
703 701 */
704 702 nonfatal("File Descriptor limit exceeded");
705 703 first_time = 0;
706 704 }
707 705 return;
708 706 }
709 707 /*
710 708 * Open the /proc file checking if there's still a valid proc file.
711 709 */
712 710 if (pid != 0 && (fd = proc_to_fd(pid)) == -1) {
713 711 /*
714 712 * No so the process died before we got to watch for him
715 713 */
716 714 return;
717 715 }
718 716
719 717 /*
720 718 * We only do this code if we're not putting in the first element
721 719 * Which we know will be for proc zero which is used by setup_pipe
722 720 * for its pipe fd.
723 721 */
724 722 if (pidcnt != 0) {
725 723 for (i = 0; i < pidcnt; i++) {
726 724 if (pid <= pidtable[i].pl_pid)
727 725 break;
728 726 }
729 727
730 728 /*
731 729 * Handle the case where we're not sticking our entry on the
732 730 * the end, or overwriting an existing entry.
733 731 */
734 732 if (i != pidcnt && pid != pidtable[i].pl_pid) {
735 733
736 734 move_amt = pidcnt - i;
737 735 /*
738 736 * Move table down
739 737 */
740 738 if (move_amt != 0) {
741 739 (void) memmove(&pidtable[i+1], &pidtable[i],
742 740 move_amt * sizeof (struct pidentry));
743 741 (void) memmove(&fdtable[i+1], &fdtable[i],
744 742 move_amt * sizeof (pollfd_t));
745 743 }
746 744 }
747 745 }
748 746
749 747 /*
750 748 * Fill in the events field for poll and copy the entry into the array
751 749 */
752 750 fdtable[i].events = 0;
753 751 fdtable[i].revents = 0;
754 752 fdtable[i].fd = fd;
755 753
756 754 /*
757 755 * Likewise, setup pid field and pointer (index) to the fdtable entry
758 756 */
759 757 pidtable[i].pl_pid = pid;
760 758
761 759 pidcnt++; /* Bump the pid count */
762 760 dprintf((" add_pid: pid = %d fd = %d index = %d pidcnt = %d\n",
763 761 (int)pid, fd, i, pidcnt));
764 762 }
765 763
766 764
767 765 /*
768 766 * rem_pid - Remove an entry from the table and check to see if its
769 767 * not in the utmpx file.
770 768 * If i != -1 don't look up the pid, use i as index
771 769 */
772 770
773 771 static void
774 772 rem_pid(pid, i, clean_it)
775 773 pid_t pid; /* Pid of process to clean or 0 if we don't know it */
776 774 int i; /* Index into table or -1 if we need to look it up */
777 775 int clean_it; /* Clean the entry, or just remove from table? */
778 776 {
779 777 int move_amt;
780 778
781 779 dprintf((" rem_pid: pid = %d i = %d", (int)pid, i));
782 780
783 781 /*
784 782 * Don't allow slot 0 in the table to be removed - utmppipe fd
785 783 */
786 784 if ((i == -1 && pid == 0) || (i == 0)) {
787 785 dprintf((" - attempted to remove proc 0\n"));
788 786 return;
789 787 }
790 788
791 789 if (i != -1 || find_pid(pid, &i) == 1) { /* Found the entry */
792 790 (void) close(fdtable[i].fd); /* We're done with the fd */
793 791
794 792 dprintf((" fd = %d\n", fdtable[i].fd));
795 793
796 794 if (clean_it == CLEANIT)
797 795 clean_entry(i);
798 796
799 797 move_amt = (pidcnt - i) - 1;
800 798 /*
801 799 * Remove entries from the tables.
802 800 */
803 801 (void) memmove(&pidtable[i], &pidtable[i+1],
804 802 move_amt * sizeof (struct pidentry));
805 803
806 804 (void) memmove(&fdtable[i], &fdtable[i+1],
807 805 move_amt * sizeof (pollfd_t));
808 806
809 807 /*
810 808 * decrement the pid count - one less pid to worry about
811 809 */
812 810 pidcnt--;
813 811 }
814 812 if (i == -1)
815 813 dprintf((" - entry not found \n"));
816 814 }
817 815
818 816
819 817 /*
820 818 * find_pid - Returns an index into the pidtable of the specifed pid,
821 819 * else -1 if not found
822 820 */
823 821
824 822 static int
825 823 find_pid(pid, i)
826 824 pid_t pid;
827 825 int *i;
828 826 {
829 827 struct pidentry pe;
830 828 struct pidentry *p;
831 829
832 830 pe.pl_pid = pid;
833 831 p = bsearch(&pe, pidtable, pidcnt, sizeof (struct pidentry), pidcmp);
834 832
835 833 if (p == NULL)
836 834 return (0);
837 835 else {
838 836 *i = p - (struct pidentry *)pidtable;
839 837 return (1);
840 838 }
841 839 }
842 840
843 841
844 842 /*
845 843 * Pidcmp - Used by besearch for sorting and finding process IDs.
846 844 */
847 845
848 846 static int
849 847 pidcmp(a, b)
850 848 struct pidentry *a, *b;
851 849 {
852 850 if (b == NULL || a == NULL)
853 851 return (0);
854 852 return (a->pl_pid - b->pl_pid);
855 853 }
856 854
857 855
858 856 /*
859 857 * proc_to_fd - Take a process ID and return an open file descriptor to the
860 858 * /proc file for the specified process.
861 859 */
862 860 static int
863 861 proc_to_fd(pid)
864 862 pid_t pid;
865 863 {
866 864 char procname[64];
867 865 int fd, dfd;
868 866
869 867 (void) sprintf(procname, "/proc/%d/psinfo", (int)pid);
870 868
871 869 if ((fd = open(procname, O_RDONLY)) >= 0) {
872 870 /*
873 871 * dup the fd above the low order values to assure
874 872 * stdio works for other fds - paranoia.
875 873 */
876 874 if (fd < EXTRA_MARGIN) {
877 875 dfd = fcntl(fd, F_DUPFD, EXTRA_MARGIN);
878 876 if (dfd > 0) {
879 877 (void) close(fd);
880 878 fd = dfd;
881 879 }
882 880 }
883 881 /*
884 882 * More paranoia - set the close on exec flag
885 883 */
886 884 (void) fcntl(fd, F_SETFD, 1);
887 885 return (fd);
888 886 }
889 887 if (errno == ENOENT)
890 888 return (-1);
891 889
892 890 if (errno == EMFILE) {
893 891 /*
894 892 * This is fatal, since libc won't be able to allocate
895 893 * any fds for the pututxline() routines
896 894 */
897 895 fatal("Out of file descriptors");
898 896 }
899 897 fatal(procname); /* Only get here on error */
900 898 return (-1);
901 899 }
902 900
903 901
904 902 /*
905 903 * *** Utmpx Cleaning Utilities ***
906 904 */
907 905
908 906 /*
909 907 * Clean_entry - Cleans the specified entry - where i is an index
910 908 * into the pid_table.
911 909 */
912 910 static void
913 911 clean_entry(i)
914 912 int i;
915 913 {
916 914 struct utmpx *u;
917 915
918 916 if (pidcnt == 0)
919 917 return;
920 918
921 919 dprintf((" Cleaning %d\n", (int)pidtable[i].pl_pid));
922 920
923 921 /*
924 922 * Double check if the process is dead.
925 923 */
926 924 if (proc_is_alive(pidtable[i].pl_pid)) {
927 925 dprintf((" Bad attempt to clean %d\n", \
928 926 (int)pidtable[i].pl_pid));
929 927 return;
930 928 }
931 929
932 930 /*
933 931 * Find the entry that corresponds to this pid.
934 932 * Do nothing if entry not found in utmpx file.
935 933 */
936 934 setutxent();
937 935 while ((u = getutxent()) != NULL) {
938 936 if (u->ut_pid == pidtable[i].pl_pid) {
939 937 if (u->ut_type == USER_PROCESS) {
940 938 clean_utmpx_ent(u);
941 939 }
942 940 }
943 941 }
944 942 endutxent();
945 943 }
946 944
947 945
948 946 /*
949 947 * clean_utmpx_ent - Clean a utmpx entry
950 948 */
951 949
↓ open down ↓ |
906 lines elided |
↑ open up ↑ |
952 950 static void
953 951 clean_utmpx_ent(u)
954 952 struct utmpx *u;
955 953 {
956 954 dprintf((" clean_utmpx_ent: %d\n", (int)u->ut_pid));
957 955 u->ut_type = DEAD_PROCESS;
958 956 (void) time(&u->ut_xtime);
959 957 (void) pututxline(u);
960 958 updwtmpx(WTMPX_FILE, u);
961 959 /*
962 - * XXX update wtmp for ! nonuser entries?
960 + * XXX update wtmp for ! nonuserx entries?
963 961 */
964 962 }
965 963
966 964 /*
967 965 * *** Error Handling and Debugging Routines ***
968 966 */
969 967
970 968 /*
971 969 * fatal - Catastrophic failure
972 970 */
973 971
974 972 static void
975 973 fatal(char *str)
976 974 {
977 975 int oerrno = errno;
978 976
979 977 syslog(LOG_ALERT, "%s", str);
980 978 if (Debug == 1) {
981 979 if ((errno = oerrno) != 0)
982 980 perror(prog_name);
983 981 dprintf(("%s\n", str));
984 982 }
985 983 exit(1);
986 984 }
987 985
988 986 /*
989 987 * nonfatal - Non-Catastrophic failure - print message and errno
990 988 */
991 989
992 990 static void
993 991 nonfatal(char *str)
994 992 {
995 993 syslog(LOG_WARNING, "%s", str);
996 994
997 995 if (Debug == 1) {
998 996 if (errno != 0)
999 997 perror(prog_name);
1000 998 dprintf(("%c%s\n", 7, str));
1001 999 print_tables();
1002 1000 (void) sleep(5); /* Time to read debug messages */
1003 1001 }
1004 1002 }
1005 1003
1006 1004 /*
1007 1005 * print_tables - Print internal tables - for debugging
1008 1006 */
1009 1007
1010 1008 static void
1011 1009 print_tables()
1012 1010 {
1013 1011 int i;
1014 1012
1015 1013 if (Debug == 0)
1016 1014 return;
1017 1015
1018 1016 dprintf(("pidtable: "));
1019 1017 for (i = 0; i < pidcnt; i++)
1020 1018 dprintf(("%d: %d ", i, (int)pidtable[i].pl_pid));
1021 1019 dprintf(("\n"));
1022 1020 dprintf(("fdtable: "));
1023 1021 for (i = 0; i < pidcnt; i++)
1024 1022 dprintf(("%d: %d ", i, fdtable[i].fd));
1025 1023 dprintf(("\n"));
1026 1024 }
1027 1025
1028 1026 /*
1029 1027 * proc_is_alive - Check to see if a process is alive AND its
1030 1028 * not a zombie. Returns 1 if process is alive
1031 1029 * and zero if it is dead or a zombie.
1032 1030 */
1033 1031
1034 1032 static int
1035 1033 proc_is_alive(pid)
1036 1034 pid_t pid;
1037 1035 {
1038 1036 char psinfoname[64];
1039 1037 int fd;
1040 1038 psinfo_t psinfo;
1041 1039
1042 1040 if (kill(pid, 0) != 0)
1043 1041 return (0); /* Kill failed - no process */
1044 1042
1045 1043 /*
1046 1044 * The process exists, so check if it's a zombie.
1047 1045 */
1048 1046 (void) sprintf(psinfoname, "/proc/%d/psinfo", (int)pid);
1049 1047
1050 1048 if ((fd = open(psinfoname, O_RDONLY)) < 0 ||
1051 1049 read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
1052 1050 /*
1053 1051 * We either couldn't open the proc, or we did but the
1054 1052 * read of the psinfo file failed, so pid is nonexistent.
1055 1053 */
1056 1054 psinfo.pr_nlwp = 0;
1057 1055 }
1058 1056 if (fd >= 0)
1059 1057 (void) close(fd);
1060 1058
1061 1059 /* if pr_nlwp == 0, process is a zombie */
1062 1060 return (psinfo.pr_nlwp != 0);
1063 1061 }
1064 1062
1065 1063 /*
1066 1064 * warn_utmp - /var/adm/utmp has been deprecated. It should no longer
1067 1065 * be used. Applications that try to directly manipulate
1068 1066 * it may cause problems. Since the file is no longer
1069 1067 * shipped, if it appears on a system it's because an
1070 1068 * old application created it. We'll have utmpd
1071 1069 * complain about it periodically.
1072 1070 */
1073 1071
1074 1072 static void
1075 1073 warn_utmp()
1076 1074 {
1077 1075 struct stat s;
1078 1076
1079 1077 if (lstat(UTMP_FILE, &s) == 0 &&
1080 1078 s.st_size % sizeof (struct utmp) == 0) {
1081 1079 nonfatal("WARNING: /var/adm/utmp exists!\nSee "
1082 1080 "utmp(4) for more information");
1083 1081 }
1084 1082 }
↓ open down ↓ |
112 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX