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