1*94b4a649Stedu /* $OpenBSD: do_command.c,v 1.48 2015/10/03 19:47:21 tedu Exp $ */ 2e134e629Smillert 3df930be7Sderaadt /* Copyright 1988,1990,1993,1994 by Paul Vixie 4a5198fa1Smillert * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5f454ebdeSmillert * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 6df930be7Sderaadt * 7f454ebdeSmillert * Permission to use, copy, modify, and distribute this software for any 8f454ebdeSmillert * purpose with or without fee is hereby granted, provided that the above 9f454ebdeSmillert * copyright notice and this permission notice appear in all copies. 10df930be7Sderaadt * 11a5198fa1Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 12a5198fa1Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a5198fa1Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 14a5198fa1Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a5198fa1Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a5198fa1Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17a5198fa1Smillert * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18df930be7Sderaadt */ 19df930be7Sderaadt 20df930be7Sderaadt #include "cron.h" 21df930be7Sderaadt 22f454ebdeSmillert static void child_process(entry *, user *); 23df930be7Sderaadt 24df930be7Sderaadt void 25785ecd14Stedu do_command(entry *e, user *u) 26785ecd14Stedu { 27df930be7Sderaadt 28df930be7Sderaadt /* fork to become asynchronous -- parent process is done immediately, 29df930be7Sderaadt * and continues to run the normal cron code, which means return to 30df930be7Sderaadt * tick(). the child and grandchild don't leave this function, alive. 31df930be7Sderaadt * 32df930be7Sderaadt * vfork() is unsuitable, since we have much to do, and the parent 33df930be7Sderaadt * needs to be able to run off and fork other processes. 34df930be7Sderaadt */ 35df930be7Sderaadt switch (fork()) { 36df930be7Sderaadt case -1: 37df930be7Sderaadt log_it("CRON", getpid(), "error", "can't fork"); 38df930be7Sderaadt break; 39df930be7Sderaadt case 0: 40df930be7Sderaadt /* child process */ 41df930be7Sderaadt acquire_daemonlock(1); 42df930be7Sderaadt child_process(e, u); 4310ee6820Smillert _exit(EXIT_SUCCESS); 44df930be7Sderaadt break; 45df930be7Sderaadt default: 46df930be7Sderaadt /* parent process */ 47df930be7Sderaadt break; 48df930be7Sderaadt } 49df930be7Sderaadt } 50df930be7Sderaadt 51df930be7Sderaadt static void 52785ecd14Stedu child_process(entry *e, user *u) 53785ecd14Stedu { 542009bb53Smillert FILE *in; 55df930be7Sderaadt int stdin_pipe[2], stdout_pipe[2]; 562009bb53Smillert char *input_data, *usernm; 57d5919ef3Smillert int children = 0; 58df930be7Sderaadt 590aec96c6Smillert /* mark ourselves as different to PS command watchers */ 600aec96c6Smillert setproctitle("running job"); 61df930be7Sderaadt 62df930be7Sderaadt /* discover some useful and important environment settings 63df930be7Sderaadt */ 642b139a93Smillert usernm = e->pwd->pw_name; 65df930be7Sderaadt 66df930be7Sderaadt /* our parent is watching for our death by catching SIGCHLD. we 67df930be7Sderaadt * do not care to watch for our children's deaths this way -- we 68e4d25771Stodd * use wait() explicitly. so we have to reset the signal (which 69df930be7Sderaadt * was inherited from the parent). 70df930be7Sderaadt */ 715815f23fSmillert (void) signal(SIGCHLD, SIG_DFL); 72df930be7Sderaadt 73df930be7Sderaadt /* create some pipes to talk to our future child 74df930be7Sderaadt */ 75df930be7Sderaadt pipe(stdin_pipe); /* child's stdin */ 76df930be7Sderaadt pipe(stdout_pipe); /* child's stdout */ 77df930be7Sderaadt 78df930be7Sderaadt /* since we are a forked process, we can diddle the command string 79df930be7Sderaadt * we were passed -- nobody else is going to use it again, right? 80df930be7Sderaadt * 81df930be7Sderaadt * if a % is present in the command, previous characters are the 82df930be7Sderaadt * command, and subsequent characters are the additional input to 832b139a93Smillert * the command. An escaped % will have the escape character stripped 842b139a93Smillert * from it. Subsequent %'s will be transformed into newlines, 85df930be7Sderaadt * but that happens later. 86df930be7Sderaadt */ 87df930be7Sderaadt /*local*/{ 88f454ebdeSmillert int escaped = FALSE; 89f454ebdeSmillert int ch; 90f454ebdeSmillert char *p; 91df930be7Sderaadt 92f454ebdeSmillert for (input_data = p = e->cmd; 93f454ebdeSmillert (ch = *input_data) != '\0'; 94d94ebf94Sderaadt input_data++, p++) { 95d94ebf94Sderaadt if (p != input_data) 96d94ebf94Sderaadt *p = ch; 97df930be7Sderaadt if (escaped) { 9894cf10eeSmillert if (ch == '%') 99d94ebf94Sderaadt *--p = ch; 100df930be7Sderaadt escaped = FALSE; 101df930be7Sderaadt continue; 102df930be7Sderaadt } 103df930be7Sderaadt if (ch == '\\') { 104df930be7Sderaadt escaped = TRUE; 105df930be7Sderaadt continue; 106df930be7Sderaadt } 107df930be7Sderaadt if (ch == '%') { 108df930be7Sderaadt *input_data++ = '\0'; 109df930be7Sderaadt break; 110df930be7Sderaadt } 111df930be7Sderaadt } 112d94ebf94Sderaadt *p = '\0'; 113df930be7Sderaadt } 114df930be7Sderaadt 115df930be7Sderaadt /* fork again, this time so we can exec the user's command. 116df930be7Sderaadt */ 11714852038Smillert switch (fork()) { 118df930be7Sderaadt case -1: 1192b139a93Smillert log_it("CRON", getpid(), "error", "can't fork"); 12010ee6820Smillert _exit(EXIT_FAILURE); 121df930be7Sderaadt /*NOTREACHED*/ 122df930be7Sderaadt case 0: 123df930be7Sderaadt /* write a log message. we've waited this long to do it 124df930be7Sderaadt * because it was not until now that we knew the PID that 125df930be7Sderaadt * the actual user command shell was going to get and the 126df930be7Sderaadt * PID is part of the log message. 127df930be7Sderaadt */ 128f454ebdeSmillert if ((e->flags & DONT_LOG) == 0) { 129df930be7Sderaadt char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 130df930be7Sderaadt 131df930be7Sderaadt log_it(usernm, getpid(), "CMD", x); 132df930be7Sderaadt free(x); 133df930be7Sderaadt } 134df930be7Sderaadt 135df930be7Sderaadt /* that's the last thing we'll log. close the log files. 136df930be7Sderaadt */ 137d8a30772Smillert log_close(); 138df930be7Sderaadt 139df930be7Sderaadt /* get new pgrp, void tty, etc. 140df930be7Sderaadt */ 141df930be7Sderaadt (void) setsid(); 142df930be7Sderaadt 143df930be7Sderaadt /* close the pipe ends that we won't use. this doesn't affect 144df930be7Sderaadt * the parent, who has to read and write them; it keeps the 145df930be7Sderaadt * kernel from recording us as a potential client TWICE -- 146df930be7Sderaadt * which would keep it from sending SIGPIPE in otherwise 147df930be7Sderaadt * appropriate circumstances. 148df930be7Sderaadt */ 149df930be7Sderaadt close(stdin_pipe[WRITE_PIPE]); 150df930be7Sderaadt close(stdout_pipe[READ_PIPE]); 151df930be7Sderaadt 152df930be7Sderaadt /* grandchild process. make std{in,out} be the ends of 153df930be7Sderaadt * pipes opened by our daddy; make stderr go to stdout. 154df930be7Sderaadt */ 15510ee6820Smillert if (stdin_pipe[READ_PIPE] != STDIN_FILENO) { 15610ee6820Smillert dup2(stdin_pipe[READ_PIPE], STDIN_FILENO); 157df930be7Sderaadt close(stdin_pipe[READ_PIPE]); 15814852038Smillert } 15910ee6820Smillert if (stdout_pipe[WRITE_PIPE] != STDOUT_FILENO) { 16010ee6820Smillert dup2(stdout_pipe[WRITE_PIPE], STDOUT_FILENO); 161a0f600ecSmillert close(stdout_pipe[WRITE_PIPE]); 16214852038Smillert } 16310ee6820Smillert dup2(STDOUT_FILENO, STDERR_FILENO); 164df930be7Sderaadt 165df930be7Sderaadt /* set our directory, uid and gid. Set gid first, since once 166626e3f5fSmiod * we set uid, we've lost root privileges. 167df930be7Sderaadt */ 168fed231abSmillert { 169b746b40dSmillert auth_session_t *as; 1702b139a93Smillert login_cap_t *lc; 1712b139a93Smillert char **p; 1722b139a93Smillert extern char **environ; 173fed231abSmillert 174fed231abSmillert /* XXX - should just pass in a login_cap_t * */ 1752b139a93Smillert if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { 1762b139a93Smillert fprintf(stderr, 1772b139a93Smillert "unable to get login class for %s\n", 1782b139a93Smillert e->pwd->pw_name); 17910ee6820Smillert _exit(EXIT_FAILURE); 180fed231abSmillert } 1812b139a93Smillert if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { 182e134e629Smillert fprintf(stderr, 183e134e629Smillert "setusercontext failed for %s\n", 184e134e629Smillert e->pwd->pw_name); 18510ee6820Smillert _exit(EXIT_FAILURE); 186fed231abSmillert } 187b746b40dSmillert as = auth_open(); 188b746b40dSmillert if (as == NULL || auth_setpwd(as, e->pwd) != 0) { 189b746b40dSmillert fprintf(stderr, "can't malloc\n"); 19010ee6820Smillert _exit(EXIT_FAILURE); 191b746b40dSmillert } 192b746b40dSmillert if (auth_approval(as, lc, usernm, "cron") <= 0) { 1932b139a93Smillert fprintf(stderr, "approval failed for %s\n", 1942b139a93Smillert e->pwd->pw_name); 19510ee6820Smillert _exit(EXIT_FAILURE); 196f454ebdeSmillert } 197b746b40dSmillert auth_close(as); 1982b139a93Smillert login_close(lc); 1992b139a93Smillert 200fed231abSmillert /* If no PATH specified in crontab file but 201f454ebdeSmillert * we just added one via login.conf, add it to 202fed231abSmillert * the crontab environment. 203fed231abSmillert */ 2042b139a93Smillert if (env_get("PATH", e->envp) == NULL && environ != NULL) { 2052b139a93Smillert for (p = environ; *p; p++) { 2062b139a93Smillert if (strncmp(*p, "PATH=", 5) == 0) { 2072b139a93Smillert e->envp = env_set(e->envp, *p); 2082b139a93Smillert break; 209fed231abSmillert } 210fed231abSmillert } 2112b139a93Smillert } 212f454ebdeSmillert } 213df930be7Sderaadt chdir(env_get("HOME", e->envp)); 214df930be7Sderaadt 2158b18fb30Smillert (void) signal(SIGPIPE, SIG_DFL); 2168b18fb30Smillert 217f454ebdeSmillert /* 218f454ebdeSmillert * Exec the command. 219df930be7Sderaadt */ 220df930be7Sderaadt { 221df930be7Sderaadt char *shell = env_get("SHELL", e->envp); 222df930be7Sderaadt 223cce75673Smillert execle(shell, shell, "-c", e->cmd, (char *)NULL, e->envp); 224cce75673Smillert fprintf(stderr, "execle: couldn't exec `%s'\n", shell); 225cce75673Smillert perror("execle"); 22610ee6820Smillert _exit(EXIT_FAILURE); 227df930be7Sderaadt } 228df930be7Sderaadt break; 229df930be7Sderaadt default: 230df930be7Sderaadt /* parent process */ 231df930be7Sderaadt break; 232df930be7Sderaadt } 233df930be7Sderaadt 234df930be7Sderaadt children++; 235df930be7Sderaadt 236df930be7Sderaadt /* middle process, child of original cron, parent of process running 237df930be7Sderaadt * the user's command. 238df930be7Sderaadt */ 239df930be7Sderaadt 240df930be7Sderaadt /* close the ends of the pipe that will only be referenced in the 241df930be7Sderaadt * grandchild process... 242df930be7Sderaadt */ 243df930be7Sderaadt close(stdin_pipe[READ_PIPE]); 244df930be7Sderaadt close(stdout_pipe[WRITE_PIPE]); 245df930be7Sderaadt 246df930be7Sderaadt /* 247df930be7Sderaadt * write, to the pipe connected to child's stdin, any input specified 248df930be7Sderaadt * after a % in the crontab entry. while we copy, convert any 249df930be7Sderaadt * additional %'s to newlines. when done, if some characters were 250df930be7Sderaadt * written and the last one wasn't a newline, write a newline. 251df930be7Sderaadt * 252df930be7Sderaadt * Note that if the input data won't fit into one pipe buffer (2K 253df930be7Sderaadt * or 4K on most BSD systems), and the child doesn't read its stdin, 254df930be7Sderaadt * we would block here. thus we must fork again. 255df930be7Sderaadt */ 256df930be7Sderaadt 257df930be7Sderaadt if (*input_data && fork() == 0) { 258f454ebdeSmillert FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 259f454ebdeSmillert int need_newline = FALSE; 260f454ebdeSmillert int escaped = FALSE; 261f454ebdeSmillert int ch; 262df930be7Sderaadt 263df930be7Sderaadt /* close the pipe we don't use, since we inherited it and 264df930be7Sderaadt * are part of its reference count now. 265df930be7Sderaadt */ 266df930be7Sderaadt close(stdout_pipe[READ_PIPE]); 267df930be7Sderaadt 268df930be7Sderaadt /* translation: 269df930be7Sderaadt * \% -> % 270df930be7Sderaadt * % -> \n 271df930be7Sderaadt * \x -> \x for all x != % 272df930be7Sderaadt */ 2739f52c269Sderaadt while ((ch = *input_data++) != '\0') { 274df930be7Sderaadt if (escaped) { 275df930be7Sderaadt if (ch != '%') 276df930be7Sderaadt putc('\\', out); 277df930be7Sderaadt } else { 278df930be7Sderaadt if (ch == '%') 279df930be7Sderaadt ch = '\n'; 280df930be7Sderaadt } 281df930be7Sderaadt 282df930be7Sderaadt if (!(escaped = (ch == '\\'))) { 283df930be7Sderaadt putc(ch, out); 284df930be7Sderaadt need_newline = (ch != '\n'); 285df930be7Sderaadt } 286df930be7Sderaadt } 287df930be7Sderaadt if (escaped) 288df930be7Sderaadt putc('\\', out); 289df930be7Sderaadt if (need_newline) 290df930be7Sderaadt putc('\n', out); 291df930be7Sderaadt 292df930be7Sderaadt /* close the pipe, causing an EOF condition. fclose causes 293df930be7Sderaadt * stdin_pipe[WRITE_PIPE] to be closed, too. 294df930be7Sderaadt */ 295df930be7Sderaadt fclose(out); 296df930be7Sderaadt 29710ee6820Smillert _exit(EXIT_SUCCESS); 298df930be7Sderaadt } 299df930be7Sderaadt 300df930be7Sderaadt /* close the pipe to the grandkiddie's stdin, since its wicked uncle 301df930be7Sderaadt * ernie back there has it open and will close it when he's done. 302df930be7Sderaadt */ 303df930be7Sderaadt close(stdin_pipe[WRITE_PIPE]); 304df930be7Sderaadt 305df930be7Sderaadt children++; 306df930be7Sderaadt 307df930be7Sderaadt /* 308df930be7Sderaadt * read output from the grandchild. it's stderr has been redirected to 309df930be7Sderaadt * it's stdout, which has been redirected to our pipe. if there is any 310df930be7Sderaadt * output, we'll be mailing it to the user whose crontab this is... 311df930be7Sderaadt * when the grandchild exits, we'll get EOF. 312df930be7Sderaadt */ 313df930be7Sderaadt 3142009bb53Smillert (void) signal(SIGPIPE, SIG_IGN); 3152009bb53Smillert in = fdopen(stdout_pipe[READ_PIPE], "r"); 3162009bb53Smillert if (in != NULL) { 317f454ebdeSmillert int ch = getc(in); 318df930be7Sderaadt 319df930be7Sderaadt if (ch != EOF) { 3202009bb53Smillert FILE *mail = NULL; 3212009bb53Smillert char *mailto; 322f454ebdeSmillert int bytes = 1; 323df930be7Sderaadt int status = 0; 324*94b4a649Stedu pid_t mailpid; 325df930be7Sderaadt 326df930be7Sderaadt /* get name of recipient. this is MAILTO if set to a 327df930be7Sderaadt * valid local username; USER otherwise. 328df930be7Sderaadt */ 3292009bb53Smillert mailto = env_get("MAILTO", e->envp); 33070805688Smillert if (!mailto) { 331df930be7Sderaadt /* MAILTO not present, set to USER. 332df930be7Sderaadt */ 333df930be7Sderaadt mailto = usernm; 33470805688Smillert } else if (!*mailto || !safe_p(usernm, mailto)) { 33570805688Smillert mailto = NULL; 336df930be7Sderaadt } 337df930be7Sderaadt 338df930be7Sderaadt /* if we are supposed to be mailing, MAILTO will 339df930be7Sderaadt * be non-NULL. only in this case should we set 340df930be7Sderaadt * up the mail command and subjects and stuff... 341df930be7Sderaadt */ 342df930be7Sderaadt 34370805688Smillert if (mailto) { 344f454ebdeSmillert char **env; 345f454ebdeSmillert char mailcmd[MAX_COMMAND]; 34675e8a723Smillert char hostname[HOST_NAME_MAX + 1]; 347df930be7Sderaadt 3484af80cabSmpech gethostname(hostname, sizeof(hostname)); 349f454ebdeSmillert if (snprintf(mailcmd, sizeof mailcmd, MAILFMT, 350f454ebdeSmillert MAILARG) >= sizeof mailcmd) { 351f454ebdeSmillert fprintf(stderr, "mailcmd too long\n"); 35210ee6820Smillert (void) _exit(EXIT_FAILURE); 353f454ebdeSmillert } 354*94b4a649Stedu if (!(mail = cron_popen(mailcmd, "w", e->pwd, 355*94b4a649Stedu &mailpid))) { 356f454ebdeSmillert perror(mailcmd); 35710ee6820Smillert (void) _exit(EXIT_FAILURE); 358df930be7Sderaadt } 359df930be7Sderaadt fprintf(mail, "From: root (Cron Daemon)\n"); 360df930be7Sderaadt fprintf(mail, "To: %s\n", mailto); 361df930be7Sderaadt fprintf(mail, "Subject: Cron <%s@%s> %s\n", 362df930be7Sderaadt usernm, first_word(hostname, "."), 363df930be7Sderaadt e->cmd); 36413c10301Smillert fprintf(mail, "Auto-Submitted: auto-generated\n"); 365f454ebdeSmillert #ifdef MAIL_DATE 366df930be7Sderaadt fprintf(mail, "Date: %s\n", 367f326c411Sderaadt arpadate(&StartTime)); 368df930be7Sderaadt #endif /*MAIL_DATE*/ 369df930be7Sderaadt for (env = e->envp; *env; env++) 370df930be7Sderaadt fprintf(mail, "X-Cron-Env: <%s>\n", 371df930be7Sderaadt *env); 372df930be7Sderaadt fprintf(mail, "\n"); 373df930be7Sderaadt 374df930be7Sderaadt /* this was the first char from the pipe 375df930be7Sderaadt */ 376f454ebdeSmillert fputc(ch, mail); 377df930be7Sderaadt } 378df930be7Sderaadt 379df930be7Sderaadt /* we have to read the input pipe no matter whether 380df930be7Sderaadt * we mail or not, but obviously we only write to 381df930be7Sderaadt * mail pipe if we ARE mailing. 382df930be7Sderaadt */ 383df930be7Sderaadt 384df930be7Sderaadt while (EOF != (ch = getc(in))) { 385df930be7Sderaadt bytes++; 3862009bb53Smillert if (mail) 387f454ebdeSmillert fputc(ch, mail); 388df930be7Sderaadt } 389df930be7Sderaadt 390df930be7Sderaadt /* only close pipe if we opened it -- i.e., we're 391df930be7Sderaadt * mailing... 392df930be7Sderaadt */ 393df930be7Sderaadt 3942009bb53Smillert if (mail) { 395df930be7Sderaadt /* Note: the pclose will probably see 396df930be7Sderaadt * the termination of the grandchild 397df930be7Sderaadt * in addition to the mail process, since 398df930be7Sderaadt * it (the grandchild) is likely to exit 399df930be7Sderaadt * after closing its stdout. 400df930be7Sderaadt */ 401*94b4a649Stedu status = cron_pclose(mail, mailpid); 402df930be7Sderaadt } 403df930be7Sderaadt 404df930be7Sderaadt /* if there was output and we could not mail it, 405df930be7Sderaadt * log the facts so the poor user can figure out 406df930be7Sderaadt * what's going on. 407df930be7Sderaadt */ 4082009bb53Smillert if (mail && status) { 409df930be7Sderaadt char buf[MAX_TEMPSTR]; 410df930be7Sderaadt 411f5e9cd7eSderaadt snprintf(buf, sizeof buf, 412df930be7Sderaadt "mailed %d byte%s of output but got status 0x%04x\n", 413df930be7Sderaadt bytes, (bytes==1)?"":"s", 414df930be7Sderaadt status); 415df930be7Sderaadt log_it(usernm, getpid(), "MAIL", buf); 416df930be7Sderaadt } 417df930be7Sderaadt 418df930be7Sderaadt } /*if data from grandchild*/ 419df930be7Sderaadt 420df930be7Sderaadt fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 421df930be7Sderaadt } 422df930be7Sderaadt 423df930be7Sderaadt /* wait for children to die. 424df930be7Sderaadt */ 425f454ebdeSmillert for (; children > 0; children--) { 426afc409ecSmillert int waiter; 427afc409ecSmillert pid_t pid; 428df930be7Sderaadt 429dd2fc19cStedu while ((pid = wait(&waiter)) < 0 && errno == EINTR) 430e134e629Smillert ; 431dd2fc19cStedu if (pid < 0) { 432df930be7Sderaadt break; 433df930be7Sderaadt } 434585d9be6Stedu /* 4353bca6180Stedu if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 436df930be7Sderaadt Debug(DPROC, (", dumped core")) 437585d9be6Stedu */ 438df930be7Sderaadt } 439df930be7Sderaadt } 440df930be7Sderaadt 4412b139a93Smillert int 442785ecd14Stedu safe_p(const char *usernm, const char *s) 443785ecd14Stedu { 4441e998768Smillert static const char safe_delim[] = "@!:%+-.,"; /* conservative! */ 445f454ebdeSmillert const char *t; 446f454ebdeSmillert int ch, first; 447df930be7Sderaadt 448db091496Sderaadt for (t = s, first = 1; (ch = (unsigned char)*t++) != '\0'; first = 0) { 449f454ebdeSmillert if (isascii(ch) && isprint(ch) && 4503c2f3a11Smillert (isalnum(ch) || ch == '_' || 4513c2f3a11Smillert (!first && strchr(safe_delim, ch)))) 452f454ebdeSmillert continue; 453f454ebdeSmillert log_it(usernm, getpid(), "UNSAFE", s); 454f454ebdeSmillert return (FALSE); 455df930be7Sderaadt } 456f454ebdeSmillert return (TRUE); 457df930be7Sderaadt } 458