/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include "limits.h" #include "ulimit.h" #include "sys/utsname.h" #include "lpsched.h" #include #include /* to up the max # of fds */ #include #include #include #include int lock_fd = -1; int isStartingForms = 0; int Starting = 0; int Shutdown = 0; int DoneChildren = 0; int Sig_Alrm = 0; int OpenMax = OPEN_MAX; int Reserve_Fds = 0; char *Local_System = 0; char *SHELL = 0; gid_t Lp_Gid; uid_t Lp_Uid; #if defined(DEBUG) unsigned long debug = 0; static int signals = 0; #endif extern int errno; extern void shutdown_messages(); int am_in_background = 0; static void disable_signals(); static void startup(); static void process(); static void ticktock(int); static void background(); static void usage(); static void Exit(); static void disable_signals(); /** ** main() **/ int main(int argc, char *argv[]) { int c; extern char *optarg; extern int optopt; extern int opterr; char * cp; struct rlimit rlim; int fd_limit = 4096; (void) setlocale(LC_ALL, ""); if ((cp = strrchr(argv[0], '/')) == NULL) cp = argv[0]; else cp++; /* open the syslog() */ openlog(cp, LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR); SHELL = DEFAULT_SHELL; opterr = 0; while((c = getopt(argc, (char * const *)argv, "dsf:n:r:M:p:")) != EOF) switch(c) { # if defined (DEBUG) case 'd': debug = DB_ALL; syslog(LOG_DEBUG, "debug = DB_ALL"); break; case 's': signals++; break; # endif /* DEBUG */ case 'f': if ((ET_SlowSize = atoi(optarg)) < 1) ET_SlowSize = 1; syslog(LOG_DEBUG, "-f option is %d", ET_SlowSize); break; case 'n': if ((ET_NotifySize = atoi(optarg)) < 1) ET_NotifySize = 1; syslog(LOG_DEBUG, "-n option is %d", ET_NotifySize); break; case 'r': if ((Reserve_Fds = atoi(optarg)) < 0) Reserve_Fds = 0; syslog(LOG_DEBUG, "-r option is %d", Reserve_Fds); break; case 'p': if ((fd_limit = atoi(optarg)) < 16) fd_limit = 4096; syslog(LOG_DEBUG, "-p option is %d", fd_limit); break; case '?': if (optopt == '?') { usage (); exit (0); } else fail ("%s: illegal option -- %c\n", argv[0], optopt); } /* reset the fd resource limit */ rlim.rlim_max = rlim.rlim_cur = fd_limit; setrlimit(RLIMIT_NOFILE, &rlim); getrlimit(RLIMIT_NOFILE, &rlim); (void) enable_extended_FILE_stdio(-1, -1); syslog(LOG_DEBUG, "file descriptor resource limit is %d (~%d printers)", rlim.rlim_cur, (rlim.rlim_cur - 12)/ 2); lp_alloc_fail_handler = mallocfail; startup(); process(); lpshut(1); /* one last time to clean up */ /*NOTREACHED*/ return (0); } static void startup() { struct passwd *p; Starting = 1; getpaths(); /* * There must be a user named "lp". */ if ((p = getpwnam(LPUSER)) == NULL) fail ("Can't find the user \"lp\" on this system!\n"); Lp_Uid = p->pw_uid; Lp_Gid = p->pw_gid; /* * Only "root" is allowed to run us. */ if ((getuid() != 0) && (geteuid() != 0)) fail ("You must be \"root\" to run this program.\n"); setuid (0); Local_System = Strdup("localhost"); /* * Make sure that all critical directories are present and that * symbolic links are correct. */ lpfsck(); /* * Try setting the lock file to see if another Spooler is running. * We'll release it immediately; this allows us to fork the child * that will run in the background. The child will relock the file. */ if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0) if (errno == EAGAIN) fail ("Print services already active.\n"); else fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); close(lock_fd); background(); /* * We are the child process now. */ if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0) fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); Close (0); Close (2); if (am_in_background) Close (1); if ((OpenMax = ulimit(4, 0L)) == -1) OpenMax = OPEN_MAX; disable_signals(); init_messages(); init_memory(); note ("Print services started.\n"); Starting = 0; } void lpshut(int immediate) { int i; extern MESG * Net_md; /* * If this is the first time here, stop all running * child processes, and shut off the alarm clock so * it doesn't bug us. */ if (!Shutdown) { mputm (Net_md, S_SHUTDOWN, 1); for (i = 0; Exec_Table != NULL && Exec_Table[i] != NULL; i++) terminate (Exec_Table[i]); alarm (0); Shutdown = (immediate? 2 : 1); } /* * If this is an express shutdown, or if all the * child processes have been cleaned up, clean up * and get out. */ if (Shutdown == 2) { /* * We don't shut down the message queues until * now, to give the children a chance to answer. * This means an LP command may have been snuck * in while we were waiting for the children to * finish, but that's OK because we'll have * stored the jobs on disk (that's part of the * normal operation, not just during shutdown phase). */ shutdown_messages(); (void) close(lock_fd); (void) Unlink(Lp_Schedlock); note ("Print services stopped.\n"); exit (0); /*NOTREACHED*/ } } static void process() { FSTATUS *pfs; PWSTATUS *ppws; int i; /* * Call the "check_..._alert()" routines for each form/print-wheel; * we need to do this at this point because these routines * short-circuit themselves while we are in startup mode. * Calling them now will kick off any necessary alerts. */ isStartingForms = 1; for (i = 0; FStatus != NULL && FStatus[i] != NULL; i++) check_form_alert (FStatus[i], (_FORM *)0); isStartingForms = 0; for (i = 0; PWStatus != NULL && PWStatus[i] != NULL; i++) check_pwheel_alert (PWStatus[i], (PWHEEL *)0); /* * Clear the alarm, then schedule an EV_ALARM. This will clear * all events that had been scheduled for later without waiting * for the next tick. */ alarm (0); schedule (EV_ALARM); /* * Start the ball rolling. */ schedule (EV_INTERF, (PSTATUS *)0); schedule (EV_NOTIFY, (RSTATUS *)0); schedule (EV_SLOWF, (RSTATUS *)0); for (EVER) { take_message (); if (Sig_Alrm) schedule (EV_ALARM); if (DoneChildren) dowait (); if (Shutdown) check_children(); if (Shutdown == 2) break; } } /*ARGSUSED*/ static void ticktock(int sig) { Sig_Alrm = 1; (void)signal (SIGALRM, ticktock); return; } static void background() { #if defined(DEBUG) if (debug & DB_SDB) return; #endif switch(fork()) { case -1: fail ("Failed to fork child process (%s).\n", PERROR); /*NOTREACHED*/ case 0: (void) setpgrp(); am_in_background = 1; return; default: note ("Print services started.\n"); exit(0); /* NOTREACHED */ } } static void usage() { note ("\ usage: lpsched [ options ]\n\ [ -f #filter-slots ] (increase no. concurrent slow filters)\n\ [ -n #notify-slots ] (increase no. concurrent notifications)\n\ [ -r #reserved-fds ] (increase margin of file descriptors)\n" ); #if defined(DEBUG) note ("\ [ -d ] (same as -D ALL)\n\ [ -s ] (don't trap most signals)\n" ); #endif note ("\ WARNING: all these options are currently unsupported\n" ); return; } static void Exit(n) int n; { fail ("Received unexpected signal %d; terminating.\n", n); } static void disable_signals() { int i; # if defined(DEBUG) if (!signals) # endif for (i = 0; i < NSIG; i++) if (signal(i, SIG_IGN) != SIG_IGN) signal (i, Exit); (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGALRM, ticktock); (void) signal(SIGTERM, lpshut); /* needs arg, but sig# OK */ (void) signal(SIGCLD, SIG_IGN); (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGCONT, SIG_DFL); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); (void) signal(SIGXFSZ, SIG_IGN); /* could be a problem */ (void) signal(SIGWINCH, SIG_IGN); /* if started in a window */ (void) signal(SIGTHAW, SIG_IGN); /* used by CPR - energystar */ #if defined(DEBUG) if (debug & DB_ABORT) (void) signal(SIGABRT, SIG_DFL); #endif }