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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 2012 by Delphix. All rights reserved */
27 #include <sys/types.h>
29 #include <sys/param.h>
31 #include <sys/contract.h>
47 #include <libcontract_priv.h>
59 #define TEXT_DOMAIN "SYS_TEST"
62 #define OPT_SETB 0x0001 /* Set the bits specified by o_bits */
63 #define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */
64 #define OPT_FUNC 0x0004 /* Call the function specified by o_func */
65 #define OPT_STR 0x0008 /* Set the string specified by o_ptr */
66 #define OPT_CRIT 0x0010 /* Option is part of selection criteria */
68 #define F_LONG_FMT 0x0001 /* Match against long format cmd */
69 #define F_NEWEST 0x0002 /* Match only newest pid */
70 #define F_REVERSE 0x0004 /* Reverse matching criteria */
71 #define F_EXACT_MATCH 0x0008 /* Require exact match */
72 #define F_HAVE_CRIT 0x0010 /* Criteria specified */
73 #define F_OUTPUT 0x0020 /* Some output has been printed */
74 #define F_KILL 0x0040 /* Pkill semantics active (vs pgrep) */
75 #define F_LONG_OUT 0x0080 /* Long output format (pgrep -l) */
76 #define F_OLDEST 0x0100 /* Match only oldest pid */
78 static int opt_euid(char, char *);
79 static int opt_uid(char, char *);
80 static int opt_gid(char, char *);
81 static int opt_ppid(char, char *);
82 static int opt_pgrp(char, char *);
83 static int opt_sid(char, char *);
84 static int opt_term(char, char *);
85 static int opt_projid(char, char *);
86 static int opt_taskid(char, char *);
87 static int opt_zoneid(char, char *);
88 static int opt_ctid(char, char *);
90 static const char *g_procdir
= "/proc"; /* Default procfs mount point */
91 static const char *g_delim
= "\n"; /* Default output delimiter */
92 static const char *g_pname
; /* Program name for error messages */
93 static ushort_t g_flags
; /* Miscellaneous flags */
95 static optdesc_t g_optdtab
[] = {
96 { 0, 0, 0, 0 }, /* 'A' */
97 { 0, 0, 0, 0 }, /* 'B' */
98 { 0, 0, 0, 0 }, /* 'C' */
99 { OPT_STR
, 0, 0, &g_procdir
}, /* -D procfsdir */
100 { 0, 0, 0, 0 }, /* 'E' */
101 { 0, 0, 0, 0 }, /* 'F' */
102 { OPT_FUNC
| OPT_CRIT
, 0, opt_gid
, 0 }, /* -G gid */
103 { 0, 0, 0, 0 }, /* 'H' */
104 { 0, 0, 0, 0 }, /* 'I' */
105 { OPT_FUNC
| OPT_CRIT
, 0, opt_projid
, 0 }, /* -J projid */
106 { 0, 0, 0, 0 }, /* 'K' */
107 { 0, 0, 0, 0 }, /* 'L' */
108 { 0, 0, 0, 0 }, /* 'M' */
109 { 0, 0, 0, 0 }, /* 'N' */
110 { 0, 0, 0, 0 }, /* 'O' */
111 { OPT_FUNC
| OPT_CRIT
, 0, opt_ppid
, 0 }, /* -P ppid */
112 { 0, 0, 0, 0 }, /* 'Q' */
113 { 0, 0, 0, 0 }, /* 'R' */
114 { 0, 0, 0, 0 }, /* 'S' */
115 { OPT_FUNC
| OPT_CRIT
, 0, opt_taskid
, 0 }, /* -T taskid */
116 { OPT_FUNC
| OPT_CRIT
, 0, opt_uid
, 0 }, /* -U uid */
117 { 0, 0, 0, 0 }, /* 'V' */
118 { 0, 0, 0, 0 }, /* 'W' */
119 { 0, 0, 0, 0 }, /* 'X' */
120 { 0, 0, 0, 0 }, /* 'Y' */
121 { 0, 0, 0, 0 }, /* 'Z' */
122 { 0, 0, 0, 0 }, /* '[' */
123 { 0, 0, 0, 0 }, /* '\\' */
124 { 0, 0, 0, 0 }, /* ']' */
125 { 0, 0, 0, 0 }, /* '^' */
126 { 0, 0, 0, 0 }, /* '_' */
127 { 0, 0, 0, 0 }, /* '`' */
128 { 0, 0, 0, 0 }, /* 'a' */
129 { 0, 0, 0, 0 }, /* 'b' */
130 { OPT_FUNC
| OPT_CRIT
, 0, opt_ctid
, 0 }, /* -c ctid */
131 { OPT_STR
, 0, 0, &g_delim
}, /* -d delim */
132 { 0, 0, 0, 0 }, /* 'e' */
133 { OPT_SETB
, F_LONG_FMT
, 0, &g_flags
}, /* -f */
134 { OPT_FUNC
| OPT_CRIT
, 0, opt_pgrp
, 0 }, /* -g pgrp */
135 { 0, 0, 0, 0 }, /* 'h' */
136 { 0, 0, 0, 0 }, /* 'i' */
137 { 0, 0, 0, 0 }, /* 'j' */
138 { 0, 0, 0, 0 }, /* 'k' */
139 { OPT_SETB
, F_LONG_OUT
, 0, &g_flags
}, /* 'l' */
140 { 0, 0, 0, 0 }, /* 'm' */
141 { OPT_SETB
, F_NEWEST
, 0, &g_flags
}, /* -n */
142 { OPT_SETB
, F_OLDEST
, 0, &g_flags
}, /* -o */
143 { 0, 0, 0, 0 }, /* 'p' */
144 { 0, 0, 0, 0 }, /* 'q' */
145 { 0, 0, 0, 0 }, /* 'r' */
146 { OPT_FUNC
| OPT_CRIT
, 0, opt_sid
, 0 }, /* -s sid */
147 { OPT_FUNC
| OPT_CRIT
, 0, opt_term
, 0 }, /* -t term */
148 { OPT_FUNC
| OPT_CRIT
, 0, opt_euid
, 0 }, /* -u euid */
149 { OPT_SETB
, F_REVERSE
, 0, &g_flags
}, /* -v */
150 { 0, 0, 0, 0 }, /* 'w' */
151 { OPT_SETB
, F_EXACT_MATCH
, 0, &g_flags
}, /* -x */
152 { 0, 0, 0, 0 }, /* 'y' */
153 { OPT_FUNC
| OPT_CRIT
, 0, opt_zoneid
, 0 } /* -z zoneid */
156 static const char PGREP_USAGE
[] = "\
157 Usage: %s [-flnovx] [-d delim] [-P ppidlist] [-g pgrplist] [-s sidlist]\n\
158 [-u euidlist] [-U uidlist] [-G gidlist] [-J projidlist]\n\
159 [-T taskidlist] [-t termlist] [-z zonelist] [-c ctidlist] [pattern]\n";
161 static const char PKILL_USAGE
[] = "\
162 Usage: %s [-signal] [-fnovx] [-P ppidlist] [-g pgrplist] [-s sidlist]\n\
163 [-u euidlist] [-U uidlist] [-G gidlist] [-J projidlist]\n\
164 [-T taskidlist] [-t termlist] [-z zonelist] [-c ctidlist] [pattern]\n";
166 static const char PGREP_OPTS
[] = ":flnovxc:d:D:u:U:G:P:g:s:t:z:J:T:";
167 static const char PKILL_OPTS
[] = ":fnovxc:D:u:U:G:P:g:s:t:z:J:T:";
169 static const char LSEP
[] = ",\t "; /* Argument list delimiter chars */
171 static psexp_t g_psexp
; /* Process matching expression */
172 static pid_t g_pid
; /* Current pid */
173 static int g_signal
= SIGTERM
; /* Signal to send */
176 print_proc(psinfo_t
*psinfo
)
178 if (g_flags
& F_OUTPUT
)
179 (void) printf("%s%d", g_delim
, (int)psinfo
->pr_pid
);
181 (void) printf("%d", (int)psinfo
->pr_pid
);
187 mbstrip(char *buf
, size_t nbytes
)
193 buf
[nbytes
- 1] = '\0';
197 n
= mbtowc(&wc
, p
, MB_LEN_MAX
);
199 if (n
< 0 || !iswprint(wc
)) {
208 (void) memmove(p
, p
+ n
, nbytes
- n
);
220 print_proc_long(psinfo_t
*psinfo
)
224 if (g_flags
& F_LONG_FMT
)
225 name
= mbstrip(psinfo
->pr_psargs
, PRARGSZ
);
227 name
= psinfo
->pr_fname
;
229 if (g_flags
& F_OUTPUT
)
230 (void) printf("%s%5d %s", g_delim
, (int)psinfo
->pr_pid
, name
);
232 (void) printf("%5d %s", (int)psinfo
->pr_pid
, name
);
238 kill_proc(psinfo_t
*psinfo
)
240 if (psinfo
->pr_pid
> 0 && kill(psinfo
->pr_pid
, g_signal
) == -1)
241 uu_warn(gettext("Failed to signal pid %d"),
242 (int)psinfo
->pr_pid
);
246 open_proc_dir(const char *dirpath
)
251 if ((dirp
= opendir(dirpath
)) == NULL
) {
252 uu_warn(gettext("Failed to open %s"), dirpath
);
256 if (fstat(dirp
->dd_fd
, &buf
) == -1) {
257 uu_warn(gettext("Failed to stat %s"), dirpath
);
258 (void) closedir(dirp
);
262 if (strcmp(buf
.st_fstype
, "proc") != 0) {
263 uu_warn(gettext("%s is not a procfs mount point\n"), dirpath
);
264 (void) closedir(dirp
);
271 #define NEWER(ps1, ps2) \
272 ((ps1.pr_start.tv_sec > ps2.pr_start.tv_sec) || \
273 (ps1.pr_start.tv_sec == ps2.pr_start.tv_sec && \
274 ps1.pr_start.tv_nsec > ps2.pr_start.tv_nsec))
277 scan_proc_dir(const char *dirpath
, DIR *dirp
, psexp_t
*psexp
,
278 void (*funcp
)(psinfo_t
*))
280 char procpath
[MAXPATHLEN
];
285 int reverse
= (g_flags
& F_REVERSE
) ? 1 : 0;
286 int ovalid
= 0, nmatches
= 0, flags
= 0;
288 if (g_flags
& F_LONG_FMT
)
289 flags
|= PSEXP_PSARGS
;
291 if (g_flags
& F_EXACT_MATCH
)
292 flags
|= PSEXP_EXACT
;
294 while ((dent
= readdir(dirp
)) != NULL
) {
296 if (dent
->d_name
[0] == '.')
299 (void) snprintf(procpath
, sizeof (procpath
), "%s/%s/psinfo",
300 dirpath
, dent
->d_name
);
302 if ((procfd
= open(procpath
, O_RDONLY
)) == -1)
305 if ((read(procfd
, &ps
, sizeof (ps
)) == sizeof (psinfo_t
)) &&
306 (ps
.pr_nlwp
!= 0) && (ps
.pr_pid
!= g_pid
) &&
307 (psexp_match(psexp
, &ps
, flags
) ^ reverse
)) {
309 if (g_flags
& F_NEWEST
) {
310 /* LINTED - opsinfo use ok */
311 if (!ovalid
|| NEWER(ps
, ops
)) {
312 (void) memcpy(&ops
, &ps
,
316 } else if (g_flags
& F_OLDEST
) {
317 if (!ovalid
|| NEWER(ops
, ps
)) {
318 (void) memcpy(&ops
, &ps
,
328 (void) close(procfd
);
331 if ((g_flags
& (F_NEWEST
| F_OLDEST
)) && ovalid
) {
340 parse_ids(idtab_t
*idt
, char *arg
, int base
, int opt
, idkey_t zero
)
345 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
346 if ((id
= (idkey_t
)strtoul(ptr
, &next
, base
)) != 0)
347 idtab_append(idt
, id
);
349 idtab_append(idt
, zero
);
351 if (next
== ptr
|| *next
!= 0) {
352 uu_warn("invalid argument for option '%c' -- %s\n",
362 parse_uids(idtab_t
*idt
, char *arg
)
365 struct passwd
*pwent
;
368 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
369 if (isdigit(ptr
[0])) {
370 id
= strtol(ptr
, &next
, 10);
372 if (next
!= ptr
&& *next
== '\0') {
373 idtab_append(idt
, id
);
378 if ((pwent
= getpwnam(ptr
)) != NULL
)
379 idtab_append(idt
, pwent
->pw_uid
);
387 uu_warn(gettext("invalid user name -- %s\n"), ptr
);
392 parse_gids(idtab_t
*idt
, char *arg
)
398 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
399 if (isdigit(ptr
[0])) {
400 id
= strtol(ptr
, &next
, 10);
402 if (next
!= ptr
&& *next
== '\0') {
403 idtab_append(idt
, id
);
408 if ((grent
= getgrnam(ptr
)) != NULL
)
409 idtab_append(idt
, grent
->gr_gid
);
417 uu_warn(gettext("invalid group name -- %s\n"), ptr
);
422 parse_ttys(idtab_t
*idt
, char *arg
)
424 char devpath
[MAXPATHLEN
];
428 int seen_console
= 0; /* Flag so we only stat syscon and systty once */
430 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
431 if (strcmp(ptr
, "none") == 0) {
432 idtab_append(idt
, (idkey_t
)PRNODEV
);
436 if (strcmp(ptr
, "console") == 0) {
440 if (stat("/dev/syscon", &buf
) == 0)
441 idtab_append(idt
, (idkey_t
)buf
.st_rdev
);
443 if (stat("/dev/systty", &buf
) == 0)
444 idtab_append(idt
, (idkey_t
)buf
.st_rdev
);
449 (void) snprintf(devpath
, MAXPATHLEN
- 1, "/dev/%s", ptr
);
451 if (stat(devpath
, &buf
) == -1)
454 idtab_append(idt
, (idkey_t
)buf
.st_rdev
);
460 uu_warn(gettext("unknown terminal name -- %s\n"), ptr
);
465 parse_projects(idtab_t
*idt
, char *arg
)
471 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
472 if (isdigit(ptr
[0])) {
473 id
= strtol(ptr
, &next
, 10);
475 if (next
!= ptr
&& *next
== '\0') {
476 idtab_append(idt
, id
);
481 if ((projid
= getprojidbyname(ptr
)) != -1)
482 idtab_append(idt
, projid
);
490 uu_warn(gettext("invalid project name -- %s\n"), ptr
);
495 parse_zones(idtab_t
*idt
, char *arg
)
500 for (ptr
= strtok(arg
, LSEP
); ptr
!= NULL
; ptr
= strtok(NULL
, LSEP
)) {
501 if (zone_get_id(ptr
, &id
) != 0) {
502 uu_warn(gettext("invalid zone name -- %s\n"), ptr
);
505 idtab_append(idt
, id
);
513 opt_euid(char c
, char *arg
)
515 return (parse_uids(&g_psexp
.ps_euids
, arg
));
520 opt_uid(char c
, char *arg
)
522 return (parse_uids(&g_psexp
.ps_ruids
, arg
));
527 opt_gid(char c
, char *arg
)
529 return (parse_gids(&g_psexp
.ps_rgids
, arg
));
533 opt_ppid(char c
, char *arg
)
535 return (parse_ids(&g_psexp
.ps_ppids
, arg
, 10, c
, 0));
539 opt_pgrp(char c
, char *arg
)
541 return (parse_ids(&g_psexp
.ps_pgids
, arg
, 10, c
, getpgrp()));
545 opt_sid(char c
, char *arg
)
547 return (parse_ids(&g_psexp
.ps_sids
, arg
, 10, c
, getsid(0)));
552 opt_term(char c
, char *arg
)
554 return (parse_ttys(&g_psexp
.ps_ttys
, arg
));
559 opt_projid(char c
, char *arg
)
561 return (parse_projects(&g_psexp
.ps_projids
, arg
));
565 opt_taskid(char c
, char *arg
)
567 return (parse_ids(&g_psexp
.ps_taskids
, arg
, 10, c
, gettaskid()));
572 opt_zoneid(char c
, char *arg
)
574 return (parse_zones(&g_psexp
.ps_zoneids
, arg
));
578 opt_ctid(char c
, char *arg
)
580 return (parse_ids(&g_psexp
.ps_ctids
, arg
, 10, c
, getctid()));
584 print_usage(FILE *stream
)
586 if (g_flags
& F_KILL
)
587 (void) fprintf(stream
, gettext(PKILL_USAGE
), g_pname
);
589 (void) fprintf(stream
, gettext(PGREP_USAGE
), g_pname
);
593 main(int argc
, char *argv
[])
595 void (*funcp
)(psinfo_t
*);
603 (void) setlocale(LC_ALL
, "");
604 (void) textdomain(TEXT_DOMAIN
);
606 UU_EXIT_FATAL
= E_ERROR
;
608 g_pname
= uu_setpname(argv
[0]);
611 psexp_create(&g_psexp
);
613 if (strcmp(g_pname
, "pkill") == 0) {
615 if (argc
> 1 && argv
[1][0] == '-' &&
616 str2sig(&argv
[1][1], &g_signal
) == 0) {
629 while (optind
< argc
) {
630 while ((c
= getopt(argc
, argv
, optstr
)) != (int)EOF
) {
632 if (c
== ':' || c
== '?' ||
633 g_optdtab
[c
- 'A'].o_opts
== 0) {
636 gettext("missing argument -- %c\n"),
638 } else if (optopt
!= '?') {
640 gettext("illegal option -- %c\n"),
648 optd
= &g_optdtab
[c
- 'A'];
650 if (optd
->o_opts
& OPT_SETB
)
651 *((ushort_t
*)optd
->o_ptr
) |= optd
->o_bits
;
653 if (optd
->o_opts
& OPT_CLRB
)
654 *((ushort_t
*)optd
->o_ptr
) &= ~optd
->o_bits
;
656 if (optd
->o_opts
& OPT_STR
)
657 *((char **)optd
->o_ptr
) = optarg
;
659 if (optd
->o_opts
& OPT_CRIT
)
660 g_flags
|= F_HAVE_CRIT
;
662 if (optd
->o_opts
& OPT_FUNC
) {
663 if (optd
->o_func(c
, optarg
) == -1)
669 if (g_psexp
.ps_pat
!= NULL
) {
670 uu_warn(gettext("illegal argument -- %s\n"),
676 g_psexp
.ps_pat
= argv
[optind
++];
677 g_flags
|= F_HAVE_CRIT
;
681 if ((g_flags
& F_NEWEST
) && (g_flags
& F_OLDEST
)) {
682 uu_warn(gettext("-n and -o are mutually exclusive\n"));
687 if ((g_flags
& F_HAVE_CRIT
) == 0) {
688 uu_warn(gettext("No matching criteria specified\n"));
693 if (psexp_compile(&g_psexp
) == -1) {
694 psexp_destroy(&g_psexp
);
698 if ((dirp
= open_proc_dir(g_procdir
)) == NULL
)
701 if (g_flags
& F_KILL
)
703 else if (g_flags
& F_LONG_OUT
)
704 funcp
= print_proc_long
;
708 nmatches
= scan_proc_dir(g_procdir
, dirp
, &g_psexp
, funcp
);
710 if (g_flags
& F_OUTPUT
)
711 (void) fputc('\n', stdout
);
713 psexp_destroy(&g_psexp
);
714 return (nmatches
? E_MATCH
: E_NOMATCH
);