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 /*
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2019 Joyent, Inc.
25 */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/proc.h>
30
31 #include <libgen.h>
32 #include <limits.h>
33 #include <alloca.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <dirent.h>
40
41 #include "Pcontrol.h"
42
43 static int
open_psinfo(const char * arg,int * perr)44 open_psinfo(const char *arg, int *perr)
45 {
46 /*
47 * Allocate enough space for procfs_path + arg + "/psinfo"
48 */
49 char *path = alloca(strlen(arg) + strlen(procfs_path) + 9);
50
51 struct stat64 st;
52 int fd;
53
54 if (strchr(arg, '/') == NULL) {
55 (void) strcpy(path, procfs_path);
56 (void) strcat(path, "/");
57 (void) strcat(path, arg);
58 } else
59 (void) strcpy(path, arg);
60
61 (void) strcat(path, "/psinfo");
62
63 /*
64 * Attempt to open the psinfo file, and return the fd if we can
65 * confirm this is a regular file provided by /proc.
66 */
67 if ((fd = open64(path, O_RDONLY)) >= 0) {
68 if (fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) ||
69 strcmp(st.st_fstype, "proc") != 0) {
70 (void) close(fd);
71 fd = -1;
72 }
73 } else if (errno == EACCES || errno == EPERM)
74 *perr = G_PERM;
75
76 return (fd);
77 }
78
79 static int
open_core(const char * arg,int * perr)80 open_core(const char *arg, int *perr)
81 {
82 #ifdef _BIG_ENDIAN
83 uchar_t order = ELFDATA2MSB;
84 #else
85 uchar_t order = ELFDATA2LSB;
86 #endif
87 GElf_Ehdr ehdr;
88 int fd;
89 int is_noelf = -1;
90
91 /*
92 * Attempt to open the core file, and return the fd if we can confirm
93 * this is an ELF file of type ET_CORE.
94 */
95 if ((fd = open64(arg, O_RDONLY)) >= 0) {
96 if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) {
97 (void) close(fd);
98 fd = -1;
99 } else if ((is_noelf = memcmp(&ehdr.e_ident[EI_MAG0], ELFMAG,
100 SELFMAG)) != 0 || ehdr.e_type != ET_CORE) {
101 (void) close(fd);
102 fd = -1;
103 if (is_noelf == 0 &&
104 ehdr.e_ident[EI_DATA] != order)
105 *perr = G_ISAINVAL;
106 }
107 } else if (errno == EACCES || errno == EPERM)
108 *perr = G_PERM;
109
110 return (fd);
111 }
112
113 /*
114 * Make the error message precisely match the type of arguments the caller
115 * wanted to process. This ensures that a tool which only accepts pids does
116 * not produce an error message saying "no such process or core file 'foo'".
117 */
118 static int
open_error(int oflag)119 open_error(int oflag)
120 {
121 if ((oflag & PR_ARG_ANY) == PR_ARG_PIDS)
122 return (G_NOPROC);
123
124 if ((oflag & PR_ARG_ANY) == PR_ARG_CORES)
125 return (G_NOCORE);
126
127 return (G_NOPROCORCORE);
128 }
129
130 static void *
proc_grab_common(const char * arg,const char * path,int oflag,int gflag,int * perr,const char ** lwps,psinfo_t * psp)131 proc_grab_common(const char *arg, const char *path, int oflag, int gflag,
132 int *perr, const char **lwps, psinfo_t *psp)
133 {
134 psinfo_t psinfo;
135 char *core;
136 int fd;
137 char *slash;
138 struct ps_prochandle *Pr;
139
140 *perr = 0;
141 if (lwps)
142 *lwps = NULL;
143
144 if (lwps != NULL && (slash = strrchr(arg, '/')) != NULL) {
145 /*
146 * Check to see if the user has supplied an lwp range. First,
147 * try to grab it as a pid/lwp combo.
148 */
149 *slash = '\0';
150 if ((oflag & PR_ARG_PIDS) &&
151 (fd = open_psinfo(arg, perr)) != -1) {
152 if (read(fd, &psinfo,
153 sizeof (psinfo_t)) == sizeof (psinfo_t)) {
154 (void) close(fd);
155 *lwps = slash + 1;
156 *slash = '/';
157 if (proc_lwp_range_valid(*lwps) != 0) {
158 *perr = G_BADLWPS;
159 return (NULL);
160 }
161 if (psp) {
162 *psp = psinfo;
163 return (psp);
164 } else {
165 return (Pgrab(psinfo.pr_pid, gflag,
166 perr));
167 }
168 }
169 (void) close(fd);
170 }
171
172 /*
173 * Next, try grabbing it as a corefile.
174 */
175 if ((oflag & PR_ARG_CORES) &&
176 (fd = open_core(arg, perr)) != -1) {
177 *lwps = slash + 1;
178 *slash = '/';
179 if (proc_lwp_range_valid(*lwps) != 0) {
180 *perr = G_BADLWPS;
181 return (NULL);
182 }
183 core = strdupa(arg);
184 if ((Pr = Pfgrab_core(fd, path == NULL ?
185 dirname(core) : path, perr)) != NULL) {
186 if (psp) {
187 (void) memcpy(psp, Ppsinfo(Pr),
188 sizeof (psinfo_t));
189 Prelease(Pr, 0);
190 return (psp);
191 } else {
192 return (Pr);
193 }
194 }
195 }
196
197 *slash = '/';
198 }
199
200 if ((oflag & PR_ARG_PIDS) && (fd = open_psinfo(arg, perr)) != -1) {
201 if (read(fd, &psinfo, sizeof (psinfo_t)) == sizeof (psinfo_t)) {
202 (void) close(fd);
203 if (psp) {
204 *psp = psinfo;
205 return (psp);
206 } else {
207 return (Pgrab(psinfo.pr_pid, gflag, perr));
208 }
209 }
210 /*
211 * If the read failed, the process may have gone away;
212 * we continue checking for core files or fail with G_NOPROC
213 */
214 (void) close(fd);
215 }
216
217 if ((oflag & PR_ARG_CORES) && (fd = open_core(arg, perr)) != -1) {
218 core = strdupa(arg);
219 if ((Pr = Pfgrab_core(fd, path == NULL ? dirname(core) : path,
220 perr)) != NULL) {
221 if (psp) {
222 (void) memcpy(psp, Ppsinfo(Pr),
223 sizeof (psinfo_t));
224 Prelease(Pr, 0);
225 return (psp);
226 } else {
227 return (Pr);
228 }
229 }
230 }
231
232 /*
233 * We were unable to open the corefile. If we have no meaningful
234 * information, report the (ambiguous) error from open_error().
235 */
236
237 if (*perr == 0)
238 *perr = open_error(oflag);
239
240 return (NULL);
241 }
242
243 struct ps_prochandle *
proc_arg_xgrab(const char * arg,const char * path,int oflag,int gflag,int * perr,const char ** lwps)244 proc_arg_xgrab(const char *arg, const char *path, int oflag, int gflag,
245 int *perr, const char **lwps)
246 {
247 return (proc_grab_common(arg, path, oflag, gflag, perr, lwps, NULL));
248 }
249
250 struct ps_prochandle *
proc_arg_grab(const char * arg,int oflag,int gflag,int * perr)251 proc_arg_grab(const char *arg, int oflag, int gflag, int *perr)
252 {
253 return (proc_grab_common(arg, NULL, oflag, gflag, perr, NULL, NULL));
254 }
255
256 pid_t
proc_arg_psinfo(const char * arg,int oflag,psinfo_t * psp,int * perr)257 proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr)
258 {
259 psinfo_t psinfo;
260
261 if (psp == NULL)
262 psp = &psinfo;
263
264 if (proc_grab_common(arg, NULL, oflag, 0, perr, NULL, psp) == NULL)
265 return (-1);
266 else
267 return (psp->pr_pid);
268 }
269
270 pid_t
proc_arg_xpsinfo(const char * arg,int oflag,psinfo_t * psp,int * perr,const char ** lwps)271 proc_arg_xpsinfo(const char *arg, int oflag, psinfo_t *psp, int *perr,
272 const char **lwps)
273 {
274 psinfo_t psinfo;
275
276 if (psp == NULL)
277 psp = &psinfo;
278
279 if (proc_grab_common(arg, NULL, oflag, 0, perr, lwps, psp) == NULL)
280 return (-1);
281 else
282 return (psp->pr_pid);
283 }
284
285 /*
286 * Convert psinfo_t.pr_psargs string into itself, replacing unprintable
287 * characters with space along the way. Stop on a null character.
288 */
289 void
proc_unctrl_psinfo(psinfo_t * psp)290 proc_unctrl_psinfo(psinfo_t *psp)
291 {
292 char *s = &psp->pr_psargs[0];
293 size_t n = PRARGSZ;
294 int c;
295
296 while (n-- != 0 && (c = (*s & UCHAR_MAX)) != '\0') {
297 if (!isprint(c))
298 c = ' ';
299 *s++ = (char)c;
300 }
301
302 *s = '\0';
303 }
304
305 static int
proc_lwp_get_range(char * range,id_t * low,id_t * high)306 proc_lwp_get_range(char *range, id_t *low, id_t *high)
307 {
308 if (*range == '-')
309 *low = 0;
310 else
311 *low = (id_t)strtol(range, &range, 10);
312
313 if (*range == '\0' || *range == ',') {
314 *high = *low;
315 return (0);
316 }
317 if (*range != '-') {
318 return (-1);
319 }
320 range++;
321
322 if (*range == '\0')
323 *high = INT_MAX;
324 else
325 *high = (id_t)strtol(range, &range, 10);
326
327 if (*range != '\0' && *range != ',') {
328 return (-1);
329 }
330
331 if (*high < *low) {
332 id_t tmp = *high;
333 *high = *low;
334 *low = tmp;
335 }
336
337 return (0);
338 }
339
340 /*
341 * Determine if the specified lwpid is in the given set of lwpids.
342 * The set can include multiple lwpid ranges separated by commas
343 * and has the following syntax:
344 *
345 * lwp_range[,lwp_range]*
346 *
347 * where lwp_range is specifed as:
348 *
349 * -n lwpid <= n
350 * n-m n <= lwpid <= m
351 * n- lwpid >= n
352 * n lwpid == n
353 */
354 int
proc_lwp_in_set(const char * set,lwpid_t lwpid)355 proc_lwp_in_set(const char *set, lwpid_t lwpid)
356 {
357 id_t low, high;
358 id_t id = (id_t)lwpid;
359 char *comma;
360 char *range = (char *)set;
361
362 /*
363 * A NULL set indicates that all LWPs are valid.
364 */
365 if (set == NULL)
366 return (1);
367
368 while (range != NULL) {
369 comma = strchr(range, ',');
370 if (comma != NULL)
371 *comma = '\0';
372 if (proc_lwp_get_range(range, &low, &high) != 0) {
373 if (comma != NULL)
374 *comma = ',';
375 return (0);
376 }
377 if (comma != NULL) {
378 *comma = ',';
379 range = comma + 1;
380 } else {
381 range = NULL;
382 }
383 if (id >= low && id <= high)
384 return (1);
385 }
386
387 return (0);
388 }
389
390 int
proc_lwp_range_valid(const char * set)391 proc_lwp_range_valid(const char *set)
392 {
393 char *comma;
394 char *range = (char *)set;
395 id_t low, high;
396 int ret;
397
398 if (range == NULL || *range == '\0' || *range == ',')
399 return (-1);
400
401 while (range != NULL) {
402 comma = strchr(range, ',');
403 if (comma != NULL)
404 *comma = '\0';
405 if ((ret = proc_lwp_get_range(range, &low, &high)) != 0) {
406 if (comma != NULL)
407 *comma = ',';
408 return (ret);
409 }
410 if (comma != NULL) {
411 *comma = ',';
412 range = comma + 1;
413 } else {
414 range = NULL;
415 }
416 }
417
418 return (0);
419 }
420
421 /*
422 * Walk all processes or LWPs in /proc and call func() for each.
423 * Omit system processes (like process-IDs 0, 2, and 3).
424 * Stop calling func() if it returns non 0 value and return it.
425 */
426 int
proc_walk(proc_walk_f * func,void * arg,int flag)427 proc_walk(proc_walk_f *func, void *arg, int flag)
428 {
429 DIR *procdir;
430 struct dirent *dirent;
431 char *errptr;
432 char pidstr[PATH_MAX];
433 psinfo_t psinfo;
434 lwpsinfo_t *lwpsinfo;
435 prheader_t prheader;
436 void *buf;
437 char *ptr;
438 int bufsz;
439 id_t pid;
440 int fd, i;
441 int ret = 0;
442 boolean_t walk_sys = B_FALSE;
443
444 if ((flag & PR_WALK_INCLUDE_SYS) != 0)
445 walk_sys = B_TRUE;
446 flag &= ~PR_WALK_INCLUDE_SYS;
447
448 if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) {
449 errno = EINVAL;
450 return (-1);
451 }
452 if ((procdir = opendir(procfs_path)) == NULL)
453 return (-1);
454 while (dirent = readdir(procdir)) {
455 if (dirent->d_name[0] == '.') /* skip . and .. */
456 continue;
457 pid = (id_t)strtol(dirent->d_name, &errptr, 10);
458 if (errptr != NULL && *errptr != '\0')
459 continue;
460 /* PR_WALK_PROC case */
461 (void) snprintf(pidstr, sizeof (pidstr),
462 "%s/%ld/psinfo", procfs_path, pid);
463 fd = open(pidstr, O_RDONLY);
464 if (fd < 0)
465 continue;
466 if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo) ||
467 ((psinfo.pr_flag & SSYS) != 0 && !walk_sys)) {
468 (void) close(fd);
469 continue;
470 }
471 (void) close(fd);
472 if (flag == PR_WALK_PROC) {
473 if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0)
474 break;
475 continue;
476 }
477 /* PR_WALK_LWP case */
478 (void) snprintf(pidstr, sizeof (pidstr),
479 "%s/%ld/lpsinfo", procfs_path, pid);
480 fd = open(pidstr, O_RDONLY);
481 if (fd < 0)
482 continue;
483 if (read(fd, &prheader, sizeof (prheader)) !=
484 sizeof (prheader)) {
485 (void) close(fd);
486 continue;
487 }
488 bufsz = prheader.pr_nent * prheader.pr_entsize;
489 if ((buf = malloc(bufsz)) == NULL) {
490 (void) close(fd);
491 ret = -1;
492 break;
493 }
494 ptr = buf;
495 if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) {
496 free(buf);
497 (void) close(fd);
498 continue;
499 }
500 (void) close(fd);
501 for (i = 0; i < prheader.pr_nent;
502 i++, ptr += prheader.pr_entsize) {
503 /*LINTED ALIGNMENT*/
504 lwpsinfo = (lwpsinfo_t *)ptr;
505 if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) {
506 free(buf);
507 break;
508 }
509 }
510 free(buf);
511 }
512 (void) closedir(procdir);
513 return (ret);
514 }
515