1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <ctype.h> 32 #include <string.h> 33 #include <signal.h> 34 #include <errno.h> 35 #include <sys/types.h> 36 #include <sys/wait.h> 37 #include <libproc.h> 38 39 #define NOREAP_TIME 60 /* wait 60 seconds before allow a reap */ 40 41 static volatile int interrupt; 42 static int Fflag; 43 static char *command; 44 45 static void 46 intr(int sig) 47 { 48 interrupt = sig; 49 } 50 51 static int 52 open_usage(pid_t pid, int *perr) 53 { 54 char path[64]; 55 struct stat64 st; 56 int fd; 57 58 (void) snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid); 59 60 /* 61 * Attempt to open the usage file, and return the fd if we can 62 * confirm this is a regular file provided by /proc. 63 */ 64 if ((fd = open64(path, O_RDONLY)) >= 0) { 65 if (fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) || 66 strcmp(st.st_fstype, "proc") != 0) { 67 (void) close(fd); 68 fd = -1; 69 } 70 } else if (errno == EACCES || errno == EPERM) 71 *perr = G_PERM; 72 73 return (fd); 74 } 75 76 static int 77 proc_usage(pid_t pid, prusage_t *pup, int *perr) 78 { 79 int fd; 80 81 *perr = G_NOPROC; 82 83 if ((fd = open_usage(pid, perr)) != -1) { 84 if (read(fd, pup, sizeof (prusage_t)) == sizeof (prusage_t)) { 85 *perr = 0; 86 (void) close(fd); 87 return (0); 88 } 89 90 /* 91 * If the read failed, the process may have gone away. 92 */ 93 (void) close(fd); 94 } 95 return (-1); 96 } 97 98 /* 99 * Force the parent process (ppid) to wait for its child process (pid). 100 */ 101 static int 102 reap(char *arg, pid_t *reap_pid, int *exit_status) 103 { 104 struct ps_prochandle *Pr; 105 siginfo_t siginfo; 106 psinfo_t psinfo; 107 prusage_t usage; 108 pid_t pid, ppid; 109 time_t elapsed; 110 int gret; 111 112 /* 113 * get the specified pid and the psinfo struct 114 */ 115 if ((pid = proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gret)) == -1) { 116 (void) fprintf(stderr, "%s: cannot examine %s: %s\n", 117 command, arg, Pgrab_error(gret)); 118 return (1); 119 } 120 121 if (psinfo.pr_nlwp != 0) { 122 (void) fprintf(stderr, "%s: process not defunct: %d\n", 123 command, (int)pid); 124 return (1); 125 } 126 127 *exit_status = psinfo.pr_wstat; 128 *reap_pid = psinfo.pr_pid; 129 ppid = psinfo.pr_ppid; 130 131 if (ppid == 1) { 132 (void) fprintf(stderr, "%s: Failed to reap %d: the only " 133 "non-defunct ancestor is 'init'\n", command, 134 (int)pid); 135 return (1); 136 } 137 138 if (proc_usage(pid, &usage, &gret) == 0) { 139 elapsed = usage.pr_tstamp.tv_sec - usage.pr_term.tv_sec; 140 } else { 141 (void) fprintf(stderr, "%s: cannot examine %d: %s\n", 142 command, (int)pid, Pgrab_error(gret)); 143 return (1); 144 } 145 146 if ((Fflag == 0) && (elapsed < NOREAP_TIME)) { 147 (void) fprintf(stderr, "%s: unsafe to reap %d; it has been " 148 "defunct less than %d seconds\n", command, (int)pid, 149 NOREAP_TIME); 150 return (1); 151 } 152 153 if ((Pr = Pgrab(ppid, Fflag | PGRAB_NOSTOP, &gret)) == NULL) { 154 (void) fprintf(stderr, "%s: cannot examine %d: %s\n", command, 155 (int)ppid, Pgrab_error(gret)); 156 return (1); 157 } 158 159 if ((Fflag == 0) && (Pstate(Pr) == PS_STOP)) { 160 Prelease(Pr, 0); 161 (void) fprintf(stderr, "%s: unsafe to reap %d; parent is " 162 "stopped and may reap status upon restart\n", command, 163 (int)pid); 164 return (1); 165 } 166 167 /* 168 * Pstop() will fail if the process to be stopped has become a zombie. 169 * This means that we can say with certainty that the child of this 170 * process has not changed parents (i.e. been reparented to init) once 171 * the Pstop() succeeds. 172 */ 173 if (Pstop(Pr, 1000) != 0) { 174 Prelease(Pr, 0); 175 (void) fprintf(stderr, "%s: failed to stop %d: %s", command, 176 (int)ppid, strerror(errno)); 177 return (1); 178 } 179 180 if (pr_waitid(Pr, P_PID, pid, &siginfo, WEXITED|WNOHANG) != 0) { 181 Prelease(Pr, 0); 182 (void) fprintf(stderr, "%s: waitid() in process %d failed: %s", 183 command, (int)ppid, strerror(errno)); 184 return (1); 185 } 186 187 Prelease(Pr, 0); 188 return (0); 189 } 190 191 static void 192 print_exit_status(pid_t pid, int wstat) 193 { 194 (void) printf("%d: ", (int)pid); 195 if (WIFSIGNALED(wstat)) { 196 char buf[SIG2STR_MAX]; 197 int sig = WTERMSIG(wstat); 198 199 if (sig2str(sig, buf) == 0) 200 (void) printf("killed by signal %s", buf); 201 else 202 (void) printf("killed by signal %d", sig); 203 204 if (WCOREDUMP(wstat)) 205 (void) printf(" (core dumped)"); 206 } else { 207 (void) printf("exited with status %d", WEXITSTATUS(wstat)); 208 } 209 (void) printf("\n"); 210 } 211 212 int 213 main(int argc, char *argv[]) 214 { 215 int retc = 0; 216 int opt; 217 int errflg = 0; 218 219 if ((command = strrchr(argv[0], '/')) != NULL) 220 command++; 221 else 222 command = argv[0]; 223 224 while ((opt = getopt(argc, argv, "F")) != EOF) { 225 switch (opt) { 226 case 'F': /* force grabbing (no O_EXCL) */ 227 Fflag = PGRAB_FORCE; 228 break; 229 default: 230 errflg = 1; 231 break; 232 } 233 } 234 235 argc -= optind; 236 argv += optind; 237 238 if (errflg || argc <= 0) { 239 (void) fprintf(stderr, "usage: %s pid ...\n", command); 240 (void) fprintf(stderr, " (Reap a defunct process by forcing " 241 "its parent to wait(2) for it)\n"); 242 exit(2); 243 } 244 245 /* catch signals from terminal */ 246 if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) 247 (void) sigset(SIGHUP, intr); 248 if (sigset(SIGINT, SIG_IGN) == SIG_DFL) 249 (void) sigset(SIGINT, intr); 250 if (sigset(SIGPIPE, SIG_IGN) == SIG_DFL) 251 (void) sigset(SIGPIPE, intr); 252 if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) 253 (void) sigset(SIGQUIT, intr); 254 (void) sigset(SIGTERM, intr); 255 256 while (--argc >= 0 && !interrupt) { 257 pid_t pid; 258 int wstat, r; 259 260 retc += r = reap(*argv++, &pid, &wstat); 261 262 if (r == 0) 263 print_exit_status(pid, wstat); 264 } 265 266 if (interrupt && retc == 0) 267 retc++; 268 return (retc == 0 ? 0 : 1); 269 } 270