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