xref: /illumos-gate/usr/src/cmd/ptools/pwait/pwait.c (revision a4955f4fa65e38d70c07d38e657a9aff43fa155f)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2022 Spencer Evans-Cole.
25  */
26 
27 #include <stdio.h>
28 #include <stdio_ext.h>
29 #include <ctype.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <stropts.h>
38 #include <poll.h>
39 #include <procfs.h>
40 #include <sys/resource.h>
41 
42 static int count_my_files();
43 static char *command;
44 
45 /* slop to account for extra file descriptors opened by libraries we call */
46 #define	SLOP	5
47 
48 int
49 main(int argc, char **argv)
50 {
51 	unsigned long remain = 0;
52 	struct pollfd *pollfd;
53 	struct pollfd *pfd;
54 	struct rlimit rlim;
55 	char *arg;
56 	unsigned i;
57 	int verbose = 0;
58 	pid_t mypid = getpid();
59 
60 	if ((command = strrchr(argv[0], '/')) != NULL)
61 		command++;
62 	else
63 		command = argv[0];
64 
65 	argc--;
66 	argv++;
67 
68 	if (argc > 0 && strcmp(argv[0], "-v") == 0) {
69 		verbose = 1;
70 		argc--;
71 		argv++;
72 	}
73 
74 	if (argc <= 0) {
75 		(void) fprintf(stderr, "usage:\t%s [-v] pid ...\n", command);
76 		(void) fprintf(stderr, "  (wait for processes to terminate)\n");
77 		(void) fprintf(stderr,
78 		    "  -v: verbose; report terminations to standard out\n");
79 		return (2);
80 	}
81 
82 	/* make sure we have enough file descriptors */
83 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
84 		int nfiles = count_my_files();
85 
86 		if (rlim.rlim_cur < argc + nfiles + SLOP) {
87 			rlim.rlim_cur = argc + nfiles + SLOP;
88 			if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
89 				(void) fprintf(stderr,
90 				    "%s: insufficient file descriptors\n",
91 				    command);
92 				return (2);
93 			}
94 		}
95 		(void) enable_extended_FILE_stdio(-1, -1);
96 	}
97 
98 	pollfd = (struct pollfd *)malloc(argc*sizeof (struct pollfd));
99 	if (pollfd == NULL) {
100 		perror("malloc");
101 		return (2);
102 	}
103 
104 	for (i = 0; i < argc; i++) {
105 		char psinfofile[100];
106 
107 		arg = argv[i];
108 		if (mypid == atol(arg)) {
109 			if (verbose) {
110 				(void) printf("%s: has the same"
111 				    " pid as this process\n", arg);
112 			}
113 			continue;
114 		}
115 		if (strchr(arg, '/') != NULL)
116 			(void) strncpy(psinfofile, arg, sizeof (psinfofile));
117 		else {
118 			(void) strcpy(psinfofile, "/proc/");
119 			(void) strncat(psinfofile, arg, sizeof (psinfofile)-6);
120 		}
121 		(void) strncat(psinfofile, "/psinfo",
122 		    sizeof (psinfofile) - strlen(psinfofile));
123 
124 		pfd = &pollfd[i];
125 		if ((pfd->fd = open(psinfofile, O_RDONLY)) >= 0) {
126 			remain++;
127 			/*
128 			 * We set POLLPRI to detect system processes.
129 			 * We will get POLLNVAL below for a POLLPRI
130 			 * requested event on a system process.
131 			 */
132 			pfd->events = POLLPRI;
133 			pfd->revents = 0;
134 		} else if (errno == ENOENT) {
135 			(void) fprintf(stderr, "%s: no such process: %s\n",
136 			    command, arg);
137 		} else {
138 			perror(arg);
139 		}
140 	}
141 
142 	while (remain != 0) {
143 		while (poll(pollfd, argc, INFTIM) < 0) {
144 			if (errno != EAGAIN) {
145 				perror("poll");
146 				return (2);
147 			}
148 			(void) sleep(2);
149 		}
150 		for (i = 0; i < argc; i++) {
151 			pfd = &pollfd[i];
152 			if (pfd->fd < 0 || (pfd->revents & ~POLLPRI) == 0) {
153 				/*
154 				 * We don't care if a non-system process
155 				 * stopped.  Don't check for that again.
156 				 */
157 				pfd->events = 0;
158 				pfd->revents = 0;
159 				continue;
160 			}
161 
162 			if (verbose) {
163 				arg = argv[i];
164 				if (pfd->revents & POLLHUP) {
165 					psinfo_t psinfo;
166 
167 					if (pread(pfd->fd, &psinfo,
168 					    sizeof (psinfo), (off_t)0)
169 					    == sizeof (psinfo)) {
170 						(void) printf("%s: terminated,"
171 						    " wait status 0x%.4x\n",
172 						    arg, psinfo.pr_wstat);
173 					} else {
174 						(void) printf(
175 						    "%s: terminated\n", arg);
176 					}
177 				}
178 				if (pfd->revents & POLLNVAL)
179 					(void) printf("%s: system process\n",
180 					    arg);
181 				if (pfd->revents & ~(POLLPRI|POLLHUP|POLLNVAL))
182 					(void) printf("%s: unknown error\n",
183 					    arg);
184 			}
185 
186 			(void) close(pfd->fd);
187 			pfd->fd = -1;
188 			remain--;
189 		}
190 	}
191 
192 	return (0);
193 }
194 
195 /* ARGSUSED1 */
196 static int
197 do_count(void *nofilesp, int fd)
198 {
199 	(*(int *)nofilesp)++;
200 	return (0);
201 }
202 
203 static int
204 count_my_files()
205 {
206 	int nofiles = 0;
207 
208 	(void) fdwalk(do_count, &nofiles);
209 	return (nofiles);
210 }
211