pkill.c revision 127425
1127412Sgad/* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $ */ 2127412Sgad 3127412Sgad/*- 4127412Sgad * Copyright (c) 2002 The NetBSD Foundation, Inc. 5127412Sgad * All rights reserved. 6127412Sgad * 7127412Sgad * This code is derived from software contributed to The NetBSD Foundation 8127412Sgad * by Andrew Doran. 9127412Sgad * 10127412Sgad * Redistribution and use in source and binary forms, with or without 11127412Sgad * modification, are permitted provided that the following conditions 12127412Sgad * are met: 13127412Sgad * 1. Redistributions of source code must retain the above copyright 14127412Sgad * notice, this list of conditions and the following disclaimer. 15127412Sgad * 2. Redistributions in binary form must reproduce the above copyright 16127412Sgad * notice, this list of conditions and the following disclaimer in the 17127412Sgad * documentation and/or other materials provided with the distribution. 18127412Sgad * 3. All advertising materials mentioning features or use of this software 19127412Sgad * must display the following acknowledgement: 20127412Sgad * This product includes software developed by the NetBSD 21127412Sgad * Foundation, Inc. and its contributors. 22127412Sgad * 4. Neither the name of The NetBSD Foundation nor the names of its 23127412Sgad * contributors may be used to endorse or promote products derived 24127412Sgad * from this software without specific prior written permission. 25127412Sgad * 26127412Sgad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27127412Sgad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28127412Sgad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29127412Sgad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30127412Sgad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31127412Sgad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32127412Sgad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33127412Sgad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34127412Sgad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35127412Sgad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36127412Sgad * POSSIBILITY OF SUCH DAMAGE. 37127412Sgad */ 38127412Sgad 39127412Sgad#include <sys/cdefs.h> 40127412Sgad__FBSDID("$FreeBSD: head/usr.bin/pkill/pkill.c 127425 2004-03-25 21:58:56Z gad $"); 41127412Sgad 42127412Sgad#include <sys/types.h> 43127412Sgad#include <sys/param.h> 44127412Sgad#include <sys/sysctl.h> 45127412Sgad#include <sys/proc.h> 46127412Sgad#include <sys/queue.h> 47127412Sgad#include <sys/stat.h> 48127425Sgad#include <sys/user.h> 49127412Sgad 50127412Sgad#include <stdio.h> 51127412Sgad#include <stdlib.h> 52127412Sgad#include <limits.h> 53127425Sgad#include <paths.h> 54127412Sgad#include <string.h> 55127412Sgad#include <unistd.h> 56127412Sgad#include <signal.h> 57127412Sgad#include <regex.h> 58127412Sgad#include <ctype.h> 59127425Sgad#include <fcntl.h> 60127412Sgad#include <kvm.h> 61127412Sgad#include <err.h> 62127412Sgad#include <pwd.h> 63127412Sgad#include <grp.h> 64127412Sgad#include <errno.h> 65127412Sgad 66127412Sgad#define STATUS_MATCH 0 67127412Sgad#define STATUS_NOMATCH 1 68127412Sgad#define STATUS_BADUSAGE 2 69127412Sgad#define STATUS_ERROR 3 70127412Sgad 71127425Sgad#if defined(__FreeBSD__) 72127425Sgad# if __FreeBSD_version < 300000 73127425Sgad# define NEED_KMEM 74127425Sgad# endif 75127425Sgad#endif 76127425Sgad 77127412Sgadenum listtype { 78127412Sgad LT_GENERIC, 79127412Sgad LT_USER, 80127412Sgad LT_GROUP, 81127412Sgad LT_TTY, 82127412Sgad LT_PGRP, 83127412Sgad LT_SID 84127412Sgad}; 85127412Sgad 86127412Sgadstruct list { 87127412Sgad SLIST_ENTRY(list) li_chain; 88127412Sgad long li_number; 89127412Sgad}; 90127412Sgad 91127412SgadSLIST_HEAD(listhead, list); 92127412Sgad 93127425Sgadstruct kinfo_proc *plist; 94127412Sgadchar *selected; 95127412Sgadchar *delim = "\n"; 96127412Sgadint nproc; 97127412Sgadint pgrep; 98127412Sgadint signum = SIGTERM; 99127412Sgadint newest; 100127412Sgadint inverse; 101127412Sgadint longfmt; 102127412Sgadint matchargs; 103127412Sgadint fullmatch; 104127412Sgadkvm_t *kd; 105127412Sgadpid_t mypid; 106127412Sgad 107127412Sgadstruct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 108127412Sgadstruct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 109127412Sgadstruct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 110127412Sgadstruct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 111127412Sgadstruct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 112127412Sgadstruct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 113127412Sgadstruct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 114127412Sgad 115127412Sgadint main(int, char **); 116127412Sgadvoid usage(void); 117127425Sgadvoid killact(struct kinfo_proc *); 118127425Sgadvoid grepact(struct kinfo_proc *); 119127412Sgadvoid makelist(struct listhead *, enum listtype, char *); 120127412Sgad 121127412Sgadint 122127412Sgadmain(int argc, char **argv) 123127412Sgad{ 124127412Sgad extern char *optarg; 125127412Sgad extern int optind; 126127412Sgad char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 127127425Sgad char *execf, *coref, *swapf; 128127412Sgad int i, j, ch, bestidx, rv, criteria; 129127425Sgad void (*action)(struct kinfo_proc *); 130127425Sgad struct kinfo_proc *kp; 131127412Sgad struct list *li; 132127412Sgad u_int32_t bestsec, bestusec; 133127412Sgad regex_t reg; 134127412Sgad regmatch_t regmatch; 135127412Sgad 136127412Sgad if (strcmp(getprogname(), "pgrep") == 0) { 137127412Sgad action = grepact; 138127412Sgad pgrep = 1; 139127412Sgad } else { 140127412Sgad action = killact; 141127412Sgad p = argv[1]; 142127412Sgad 143127412Sgad if (argc > 1 && p[0] == '-') { 144127412Sgad p++; 145127412Sgad i = (int)strtol(p, &q, 10); 146127412Sgad if (*q == '\0') { 147127412Sgad signum = i; 148127412Sgad argv++; 149127412Sgad argc--; 150127412Sgad } else { 151127412Sgad if (strncasecmp(p, "sig", 3) == 0) 152127412Sgad p += 3; 153127412Sgad for (i = 1; i < NSIG; i++) 154127412Sgad if (strcasecmp(sys_signame[i], p) == 0) 155127412Sgad break; 156127412Sgad if (i != NSIG) { 157127412Sgad signum = i; 158127412Sgad argv++; 159127412Sgad argc--; 160127412Sgad } 161127412Sgad } 162127412Sgad } 163127412Sgad } 164127412Sgad 165127425Sgad#if defined(NEED_KMEM) 166127425Sgad execf = coref = swapf = NULL; 167127425Sgad#else 168127425Sgad execf = coref = swapf = _PATH_DEVNULL; 169127425Sgad#endif 170127425Sgad 171127412Sgad criteria = 0; 172127412Sgad 173127412Sgad while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1) 174127412Sgad switch (ch) { 175127412Sgad case 'G': 176127412Sgad makelist(&rgidlist, LT_GROUP, optarg); 177127412Sgad criteria = 1; 178127412Sgad break; 179127412Sgad case 'P': 180127412Sgad makelist(&ppidlist, LT_GENERIC, optarg); 181127412Sgad criteria = 1; 182127412Sgad break; 183127412Sgad case 'U': 184127412Sgad makelist(&ruidlist, LT_USER, optarg); 185127412Sgad criteria = 1; 186127412Sgad break; 187127412Sgad case 'd': 188127412Sgad if (!pgrep) 189127412Sgad usage(); 190127412Sgad delim = optarg; 191127412Sgad break; 192127412Sgad case 'f': 193127412Sgad matchargs = 1; 194127412Sgad break; 195127412Sgad case 'g': 196127412Sgad makelist(&pgrplist, LT_PGRP, optarg); 197127412Sgad criteria = 1; 198127412Sgad break; 199127412Sgad case 'l': 200127412Sgad if (!pgrep) 201127412Sgad usage(); 202127412Sgad longfmt = 1; 203127412Sgad break; 204127412Sgad case 'n': 205127412Sgad newest = 1; 206127412Sgad criteria = 1; 207127412Sgad break; 208127412Sgad case 's': 209127412Sgad makelist(&sidlist, LT_SID, optarg); 210127412Sgad criteria = 1; 211127412Sgad break; 212127412Sgad case 't': 213127412Sgad makelist(&tdevlist, LT_TTY, optarg); 214127412Sgad criteria = 1; 215127412Sgad break; 216127412Sgad case 'u': 217127412Sgad makelist(&euidlist, LT_USER, optarg); 218127412Sgad criteria = 1; 219127412Sgad break; 220127412Sgad case 'v': 221127412Sgad inverse = 1; 222127412Sgad break; 223127412Sgad case 'x': 224127412Sgad fullmatch = 1; 225127412Sgad break; 226127412Sgad default: 227127412Sgad usage(); 228127412Sgad /* NOTREACHED */ 229127412Sgad } 230127412Sgad 231127412Sgad argc -= optind; 232127412Sgad argv += optind; 233127412Sgad if (argc != 0) 234127412Sgad criteria = 1; 235127412Sgad if (!criteria) 236127412Sgad usage(); 237127412Sgad 238127412Sgad mypid = getpid(); 239127412Sgad 240127412Sgad /* 241127412Sgad * Retrieve the list of running processes from the kernel. 242127412Sgad */ 243127425Sgad kd = kvm_openfiles(execf, coref, swapf, O_RDONLY, buf); 244127412Sgad if (kd == NULL) 245127412Sgad errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 246127412Sgad 247127425Sgad plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 248127412Sgad if (plist == NULL) 249127425Sgad errx(STATUS_ERROR, "kvm_getprocs() failed"); 250127412Sgad 251127412Sgad /* 252127412Sgad * Allocate memory which will be used to keep track of the 253127412Sgad * selection. 254127412Sgad */ 255127412Sgad if ((selected = malloc(nproc)) == NULL) 256127412Sgad errx(STATUS_ERROR, "memory allocation failure"); 257127412Sgad memset(selected, 0, nproc); 258127412Sgad 259127412Sgad /* 260127412Sgad * Refine the selection. 261127412Sgad */ 262127412Sgad for (; *argv != NULL; argv++) { 263127412Sgad if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 264127412Sgad regerror(rv, ®, buf, sizeof(buf)); 265127412Sgad errx(STATUS_BADUSAGE, "bad expression: %s", buf); 266127412Sgad } 267127412Sgad 268127412Sgad for (i = 0, kp = plist; i < nproc; i++, kp++) { 269127425Sgad if ((kp->ki_flag & P_SYSTEM) != 0 || kp->ki_pid == mypid) 270127412Sgad continue; 271127412Sgad 272127412Sgad if (matchargs) { 273127425Sgad if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 274127412Sgad continue; 275127412Sgad 276127412Sgad j = 0; 277127412Sgad while (j < sizeof(buf) && *pargv != NULL) { 278127412Sgad j += snprintf(buf + j, sizeof(buf) - j, 279127412Sgad pargv[1] != NULL ? "%s " : "%s", 280127412Sgad pargv[0]); 281127412Sgad pargv++; 282127412Sgad } 283127412Sgad 284127412Sgad mstr = buf; 285127412Sgad } else 286127425Sgad mstr = kp->ki_comm; 287127412Sgad 288127412Sgad rv = regexec(®, mstr, 1, ®match, 0); 289127412Sgad if (rv == 0) { 290127412Sgad if (fullmatch) { 291127412Sgad if (regmatch.rm_so == 0 && 292127412Sgad regmatch.rm_eo == strlen(mstr)) 293127412Sgad selected[i] = 1; 294127412Sgad } else 295127412Sgad selected[i] = 1; 296127412Sgad } else if (rv != REG_NOMATCH) { 297127412Sgad regerror(rv, ®, buf, sizeof(buf)); 298127412Sgad errx(STATUS_ERROR, "regexec(): %s", buf); 299127412Sgad } 300127412Sgad } 301127412Sgad 302127412Sgad regfree(®); 303127412Sgad } 304127412Sgad 305127412Sgad for (i = 0, kp = plist; i < nproc; i++, kp++) { 306127425Sgad if ((kp->ki_flag & P_SYSTEM) != 0) 307127412Sgad continue; 308127412Sgad 309127412Sgad SLIST_FOREACH(li, &ruidlist, li_chain) 310127425Sgad if (kp->ki_ruid == (uid_t)li->li_number) 311127412Sgad break; 312127412Sgad if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 313127412Sgad selected[i] = 0; 314127412Sgad continue; 315127412Sgad } 316127412Sgad 317127412Sgad SLIST_FOREACH(li, &rgidlist, li_chain) 318127425Sgad if (kp->ki_rgid == (gid_t)li->li_number) 319127412Sgad break; 320127412Sgad if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 321127412Sgad selected[i] = 0; 322127412Sgad continue; 323127412Sgad } 324127412Sgad 325127412Sgad SLIST_FOREACH(li, &euidlist, li_chain) 326127425Sgad if (kp->ki_uid == (uid_t)li->li_number) 327127412Sgad break; 328127412Sgad if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 329127412Sgad selected[i] = 0; 330127412Sgad continue; 331127412Sgad } 332127412Sgad 333127412Sgad SLIST_FOREACH(li, &ppidlist, li_chain) 334127425Sgad if (kp->ki_ppid == (uid_t)li->li_number) 335127412Sgad break; 336127412Sgad if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 337127412Sgad selected[i] = 0; 338127412Sgad continue; 339127412Sgad } 340127412Sgad 341127412Sgad SLIST_FOREACH(li, &pgrplist, li_chain) 342127425Sgad if (kp->ki_pgid == (uid_t)li->li_number) 343127412Sgad break; 344127412Sgad if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 345127412Sgad selected[i] = 0; 346127412Sgad continue; 347127412Sgad } 348127412Sgad 349127412Sgad SLIST_FOREACH(li, &tdevlist, li_chain) { 350127412Sgad if (li->li_number == -1 && 351127425Sgad (kp->ki_flag & P_CONTROLT) == 0) 352127412Sgad break; 353127425Sgad if (kp->ki_tdev == (uid_t)li->li_number) 354127412Sgad break; 355127412Sgad } 356127412Sgad if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 357127412Sgad selected[i] = 0; 358127412Sgad continue; 359127412Sgad } 360127412Sgad 361127412Sgad SLIST_FOREACH(li, &sidlist, li_chain) 362127425Sgad if (kp->ki_sid == (uid_t)li->li_number) 363127412Sgad break; 364127412Sgad if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 365127412Sgad selected[i] = 0; 366127412Sgad continue; 367127412Sgad } 368127412Sgad 369127412Sgad if (argc == 0) 370127412Sgad selected[i] = 1; 371127412Sgad } 372127412Sgad 373127412Sgad if (newest) { 374127412Sgad bestsec = 0; 375127412Sgad bestusec = 0; 376127412Sgad bestidx = -1; 377127412Sgad 378127412Sgad for (i = 0, kp = plist; i < nproc; i++, kp++) { 379127412Sgad if (!selected[i]) 380127412Sgad continue; 381127412Sgad 382127425Sgad if (kp->ki_start.tv_sec > bestsec || 383127425Sgad (kp->ki_start.tv_sec == bestsec 384127425Sgad && kp->ki_start.tv_usec> bestusec)) { 385127425Sgad bestsec = kp->ki_start.tv_sec; 386127425Sgad bestusec = kp->ki_start.tv_usec; 387127412Sgad bestidx = i; 388127412Sgad } 389127412Sgad } 390127412Sgad 391127412Sgad memset(selected, 0, nproc); 392127412Sgad if (bestidx != -1) 393127412Sgad selected[bestidx] = 1; 394127412Sgad } 395127412Sgad 396127412Sgad /* 397127412Sgad * Take the appropriate action for each matched process, if any. 398127412Sgad */ 399127412Sgad for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 400127425Sgad if (kp->ki_pid == mypid) 401127412Sgad continue; 402127412Sgad if (selected[i]) { 403127412Sgad if (inverse) 404127412Sgad continue; 405127412Sgad } else if (!inverse) 406127412Sgad continue; 407127412Sgad 408127425Sgad if ((kp->ki_flag & P_SYSTEM) != 0) 409127412Sgad continue; 410127412Sgad 411127412Sgad rv = 1; 412127412Sgad (*action)(kp); 413127412Sgad } 414127412Sgad 415127412Sgad exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 416127412Sgad} 417127412Sgad 418127412Sgadvoid 419127412Sgadusage(void) 420127412Sgad{ 421127412Sgad const char *ustr; 422127412Sgad 423127412Sgad if (pgrep) 424127412Sgad ustr = "[-flnvx] [-d delim]"; 425127412Sgad else 426127412Sgad ustr = "[-signal] [-fnvx]"; 427127412Sgad 428127412Sgad fprintf(stderr, 429127412Sgad "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 430127412Sgad " [-t tty] [-u euid] pattern ...\n", getprogname(), 431127412Sgad ustr); 432127412Sgad 433127412Sgad exit(STATUS_ERROR); 434127412Sgad} 435127412Sgad 436127412Sgadvoid 437127425Sgadkillact(struct kinfo_proc *kp) 438127412Sgad{ 439127412Sgad 440127425Sgad if (kill(kp->ki_pid, signum) == -1) 441127425Sgad err(STATUS_ERROR, "signalling pid %d", (int)kp->ki_pid); 442127412Sgad} 443127412Sgad 444127412Sgadvoid 445127425Sgadgrepact(struct kinfo_proc *kp) 446127412Sgad{ 447127412Sgad char **argv; 448127412Sgad 449127412Sgad if (longfmt && matchargs) { 450127425Sgad if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 451127412Sgad return; 452127412Sgad 453127425Sgad printf("%d ", (int)kp->ki_pid); 454127412Sgad for (; *argv != NULL; argv++) { 455127412Sgad printf("%s", *argv); 456127412Sgad if (argv[1] != NULL) 457127412Sgad putchar(' '); 458127412Sgad } 459127412Sgad } else if (longfmt) 460127425Sgad printf("%d %s", (int)kp->ki_pid, kp->ki_comm); 461127412Sgad else 462127425Sgad printf("%d", (int)kp->ki_pid); 463127412Sgad 464127412Sgad printf("%s", delim); 465127412Sgad} 466127412Sgad 467127412Sgadvoid 468127412Sgadmakelist(struct listhead *head, enum listtype type, char *src) 469127412Sgad{ 470127412Sgad struct list *li; 471127412Sgad struct passwd *pw; 472127412Sgad struct group *gr; 473127412Sgad struct stat st; 474127412Sgad char *sp, *p, buf[MAXPATHLEN]; 475127412Sgad int empty; 476127412Sgad 477127412Sgad empty = 1; 478127412Sgad 479127412Sgad while ((sp = strsep(&src, ",")) != NULL) { 480127412Sgad if (*sp == '\0') 481127412Sgad usage(); 482127412Sgad 483127412Sgad if ((li = malloc(sizeof(*li))) == NULL) 484127412Sgad errx(STATUS_ERROR, "memory allocation failure"); 485127412Sgad SLIST_INSERT_HEAD(head, li, li_chain); 486127412Sgad empty = 0; 487127412Sgad 488127412Sgad li->li_number = (uid_t)strtol(sp, &p, 0); 489127412Sgad if (*p == '\0') { 490127412Sgad switch (type) { 491127412Sgad case LT_PGRP: 492127412Sgad if (li->li_number == 0) 493127412Sgad li->li_number = getpgrp(); 494127412Sgad break; 495127412Sgad case LT_SID: 496127412Sgad if (li->li_number == 0) 497127412Sgad li->li_number = getsid(mypid); 498127412Sgad break; 499127412Sgad case LT_TTY: 500127412Sgad usage(); 501127412Sgad default: 502127412Sgad break; 503127412Sgad } 504127412Sgad continue; 505127412Sgad } 506127412Sgad 507127412Sgad switch (type) { 508127412Sgad case LT_USER: 509127412Sgad if ((pw = getpwnam(sp)) == NULL) 510127412Sgad errx(STATUS_BADUSAGE, "unknown user `%s'", 511127412Sgad optarg); 512127412Sgad li->li_number = pw->pw_uid; 513127412Sgad break; 514127412Sgad case LT_GROUP: 515127412Sgad if ((gr = getgrnam(sp)) == NULL) 516127412Sgad errx(STATUS_BADUSAGE, "unknown group `%s'", 517127412Sgad optarg); 518127412Sgad li->li_number = gr->gr_gid; 519127412Sgad break; 520127412Sgad case LT_TTY: 521127412Sgad if (strcmp(sp, "-") == 0) { 522127412Sgad li->li_number = -1; 523127412Sgad break; 524127412Sgad } else if (strcmp(sp, "co") == 0) 525127412Sgad p = "console"; 526127412Sgad else if (strncmp(sp, "tty", 3) == 0) 527127412Sgad p = sp; 528127412Sgad else 529127412Sgad p = NULL; 530127412Sgad 531127412Sgad if (p == NULL) 532127412Sgad snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 533127412Sgad else 534127412Sgad snprintf(buf, sizeof(buf), "/dev/%s", p); 535127412Sgad 536127412Sgad if (stat(buf, &st) < 0) { 537127412Sgad if (errno == ENOENT) 538127412Sgad errx(STATUS_BADUSAGE, 539127412Sgad "no such tty: `%s'", sp); 540127412Sgad err(STATUS_ERROR, "stat(%s)", sp); 541127412Sgad } 542127412Sgad 543127412Sgad if ((st.st_mode & S_IFCHR) == 0) 544127412Sgad errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 545127412Sgad 546127412Sgad li->li_number = st.st_rdev; 547127412Sgad break; 548127412Sgad default: 549127412Sgad usage(); 550127412Sgad }; 551127412Sgad } 552127412Sgad 553127412Sgad if (empty) 554127412Sgad usage(); 555127412Sgad} 556