xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpsched.c (revision bbc88f3a6c6d8e21cb05884590e32f7fb7b52e05)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include "limits.h"
33 #include "ulimit.h"
34 #include "sys/utsname.h"
35 
36 #include "lpsched.h"
37 
38 #include <sys/stat.h>
39 #include <sys/time.h>		/* to up the max # of fds */
40 #include <sys/resource.h>
41 #include <syslog.h>
42 #include <locale.h>
43 
44 
45 int			lock_fd		= -1;
46 int			isStartingForms = 0;
47 int			Starting	= 0;
48 int			Shutdown	= 0;
49 int			DoneChildren	= 0;
50 int			Sig_Alrm	= 0;
51 int			OpenMax		= OPEN_MAX;
52 int			Reserve_Fds	= 0;
53 
54 char			*Local_System	= 0;
55 char			*SHELL		= 0;
56 
57 char *LP_TRAY_UNMOUNT = NULL;
58 char *LP_KILL_NO_PAPER = NULL;
59 char *LP_ALL_NEW = NULL;
60 
61 gid_t			Lp_Gid;
62 uid_t			Lp_Uid;
63 
64 #if	defined(DEBUG)
65 unsigned long		debug = 0;
66 static int		signals = 0;
67 #endif
68 
69 extern int		errno;
70 extern char		*lpsched_buildinfo;
71 extern void		shutdown_messages();
72 
73 int			am_in_background	= 0;
74 
75 static void		disable_signals();
76 static void		startup();
77 static void		process();
78 static void		ticktock(int);
79 static void		background();
80 static void		usage();
81 static void		Exit();
82 static void		disable_signals();
83 
84 /**
85  ** main()
86  **/
87 
88 int
89 main(int argc, char *argv[])
90 {
91     int		c;
92     extern char	*optarg;
93     extern int	optopt;
94     extern int	opterr;
95     char *	cp;
96     struct rlimit rlim;
97     int fd_limit = 4096;
98 
99 	(void) setlocale(LC_ALL, "");
100     if ((cp = strrchr(argv[0], '/')) == NULL)
101 	    cp = argv[0];
102     else
103 	    cp++;
104 
105     /* open the syslog() */
106     openlog(cp, LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);
107 
108 	SHELL = DEFAULT_SHELL;
109 	LP_TRAY_UNMOUNT = getenv("LP_TRAY_UNMOUNT");
110 	LP_KILL_NO_PAPER = getenv("LP_KILL_NO_PAPER");
111 	LP_ALL_NEW = getenv("LP_ALL_NEW");
112 
113     opterr = 0;
114     while((c = getopt(argc, (char * const *)argv, "D:dsf:n:r:M:p:")) != EOF)
115         switch(c)
116         {
117 # if defined (DEBUG)
118 	    case 'd':
119 		debug = DB_ALL;
120 		syslog(LOG_DEBUG, "debug = DB_ALL\n");
121 		goto SkipD;
122 	    case 'D':
123 		if (*optarg == '?') {
124 			note (
125 "-D flag[,flag...]    (all logs \"foo\" are in /var/lp/logs,\n"
126 "                      although \"lpsched\" goes to stdout if SDB)\n"
127 "\n"
128 "  EXEC               (log all exec's in \"exec\")\n"
129 "  DONE               (log just exec finishes in \"exec\")\n"
130 "  INIT               (log initialization info in \"lpsched\" or stdout)\n"
131 "  ABORT              (issue abort(2) on fatal error)\n"
132 "  SCHEDLOG           (log additional debugging info in \"lpsched\")\n"
133 "  SDB                (don't start lpsched as background process)\n"
134 "  MESSAGES           (log all message traffic in \"messages\")\n"
135 			);
136 			note ("\
137   ALL                (all of the above; equivalent to -d)\n"
138 			);
139 			exit (0);
140 		}
141 		while ((cp = strtok(optarg, ", "))) {
142 #define IFSETDB(P,S,F)	if (STREQU(P, S)) debug |= F
143 			IFSETDB (cp, "EXEC", DB_EXEC);
144 			else IFSETDB (cp, "DONE", DB_DONE);
145 			else IFSETDB (cp, "INIT", DB_INIT);
146 			else IFSETDB (cp, "ABORT", DB_ABORT);
147 			else IFSETDB (cp, "SCHEDLOG", DB_SCHEDLOG);
148 			else IFSETDB (cp, "SDB", DB_SDB);
149 			else IFSETDB (cp, "MESSAGES", DB_MESSAGES);
150 			else IFSETDB (cp, "ALL", DB_ALL);
151 			else {
152 				note ("-D flag not recognized; try -D?\n");
153 				exit (1);
154 			}
155 			optarg = 0;
156 		}
157 		syslog(LOG_DEBUG, "-D set\n");
158 SkipD:
159 		break;
160 
161 	    case 's':
162 		signals++;
163 		break;
164 # endif /* DEBUG */
165 
166 	    case 'f':
167 		if ((ET_SlowSize = atoi(optarg)) < 1)
168 		    ET_SlowSize = 1;
169 		syslog(LOG_DEBUG, "-f option is %d\n", ET_SlowSize);
170 		break;
171 
172 	    case 'n':
173 		if ((ET_NotifySize = atoi(optarg)) < 1)
174 		    ET_NotifySize = 1;
175 		syslog(LOG_DEBUG, "-n option is %d\n", ET_NotifySize);
176 		break;
177 
178 	    case 'r':
179 		if ((Reserve_Fds = atoi(optarg)) < 0)
180 			Reserve_Fds = 0;
181 		syslog(LOG_DEBUG, "-r option is %d\n", Reserve_Fds);
182 		break;
183 
184 	    case 'p':
185 		if ((fd_limit = atoi(optarg)) < 16)
186 			fd_limit = 4096;
187 		syslog(LOG_DEBUG, "-p option is %d\n", fd_limit);
188 		break;
189 
190 	    case '?':
191 		if (optopt == '?') {
192 		    usage ();
193 		    exit (0);
194 		} else
195 		    fail ("%s: illegal option -- %c\n", argv[0], optopt);
196 	}
197 
198 	/* reset the fd resource limit */
199 	rlim.rlim_max = rlim.rlim_cur = fd_limit;
200 	setrlimit(RLIMIT_NOFILE, &rlim);
201 	getrlimit(RLIMIT_NOFILE, &rlim);
202 	syslog(LOG_DEBUG, "file descriptor resource limit is %d (~%d printers)",
203 		rlim.rlim_cur, (rlim.rlim_cur - 12)/ 2);
204 
205     lp_alloc_fail_handler = mallocfail;
206 
207     startup();
208 
209     process();
210 
211     lpshut(1);	/* one last time to clean up */
212     /*NOTREACHED*/
213     return (0);
214 }
215 
216 static void
217 startup()
218 {
219     struct passwd		*p;
220     struct utsname		utsbuf;
221 
222 
223     Starting = 1;
224     getpaths();
225 
226     /*
227      * There must be a user named "lp".
228      */
229     if ((p = getpwnam(LPUSER)) == NULL)
230 	fail ("Can't find the user \"lp\" on this system!\n");
231 
232     Lp_Uid = p->pw_uid;
233     Lp_Gid = p->pw_gid;
234 
235     /*
236      * Only "root" is allowed to run us.
237      */
238     if ((getuid() != 0) && (geteuid() != 0))
239 	fail ("You must be \"root\" to run this program.\n");
240 
241     setuid (0);
242 
243     uname(&utsbuf);
244     if (utsbuf.nodename[0] != '\0')
245     	Local_System = Strdup(utsbuf.nodename);
246     else
247 	Local_System = Strdup("localhost");
248 
249     /*
250      * Make sure that all critical directories are present and that
251      * symbolic links are correct.
252      */
253     lpfsck();
254 
255     /*
256      * Try setting the lock file to see if another Spooler is running.
257      * We'll release it immediately; this allows us to fork the child
258      * that will run in the background. The child will relock the file.
259      */
260     if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0)
261 	if (errno == EAGAIN)
262 	    fail ("Print services already active.\n");
263 	else
264 	    fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR);
265     close(lock_fd);
266 
267     background();
268     /*
269      * We are the child process now.
270      */
271 
272     if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0)
273 	fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR);
274 
275     Close (0);
276     Close (2);
277     if (am_in_background)
278 	Close (1);
279 
280     if ((OpenMax = ulimit(4, 0L)) == -1)
281 	OpenMax = OPEN_MAX;
282 
283     disable_signals();
284 
285     init_messages();
286 
287     init_memory();
288 
289     note (lpsched_buildinfo);
290     note ("Print services started.\n");
291     Starting = 0;
292 }
293 
294 void
295 lpshut(int immediate)
296 {
297 	int			i;
298 	extern MESG *		Net_md;
299 
300 
301 	/*
302 	 * If this is the first time here, stop all running
303 	 * child processes, and shut off the alarm clock so
304 	 * it doesn't bug us.
305 	 */
306 	if (!Shutdown) {
307 		mputm (Net_md, S_SHUTDOWN, 1);
308 		for (i = 0; i < ET_Size; i++)
309 			terminate (&Exec_Table[i]);
310 		alarm (0);
311 		Shutdown = (immediate? 2 : 1);
312 	}
313 
314 	/*
315 	 * If this is an express shutdown, or if all the
316 	 * child processes have been cleaned up, clean up
317 	 * and get out.
318 	 */
319 	if (Shutdown == 2) {
320 
321 		/*
322 		 * We don't shut down the message queues until
323 		 * now, to give the children a chance to answer.
324 		 * This means an LP command may have been snuck
325 		 * in while we were waiting for the children to
326 		 * finish, but that's OK because we'll have
327 		 * stored the jobs on disk (that's part of the
328 		 * normal operation, not just during shutdown phase).
329 		 */
330 		shutdown_messages();
331 
332 		(void) close(lock_fd);
333 		(void) Unlink(Lp_Schedlock);
334 
335 		note ("Print services stopped.\n");
336 		exit (0);
337 		/*NOTREACHED*/
338 	}
339 }
340 
341 static void
342 process()
343 {
344     register FSTATUS	*pfs;
345     register PWSTATUS	*ppws;
346 
347 
348     /*
349      * Call the "check_..._alert()" routines for each form/print-wheel;
350      * we need to do this at this point because these routines
351      * short-circuit themselves while we are in startup mode.
352      * Calling them now will kick off any necessary alerts.
353      */
354     isStartingForms = 1;
355     for (pfs = walk_ftable(1); pfs; pfs = walk_ftable(0))
356 	check_form_alert (pfs, (_FORM *)0);
357     isStartingForms = 0;
358 
359     for (ppws = walk_pwtable(1); ppws; ppws = walk_pwtable(0))
360 	check_pwheel_alert (ppws, (PWHEEL *)0);
361 
362     /*
363      * Clear the alarm, then schedule an EV_ALARM. This will clear
364      * all events that had been scheduled for later without waiting
365      * for the next tick.
366      */
367     alarm (0);
368     schedule (EV_ALARM);
369 
370     /*
371      * Start the ball rolling.
372      */
373     schedule (EV_INTERF, (PSTATUS *)0);
374     schedule (EV_NOTIFY, (RSTATUS *)0);
375     schedule (EV_SLOWF, (RSTATUS *)0);
376 
377     for (EVER) {
378 	take_message ();
379 
380 	if (Sig_Alrm)
381 		schedule (EV_ALARM);
382 
383 	if (DoneChildren)
384 		dowait ();
385 
386 	if (Shutdown)
387 		check_children();
388 	if (Shutdown == 2)
389 		break;
390     }
391 }
392 
393 /*ARGSUSED*/
394 static void
395 ticktock(int sig)
396 {
397 	Sig_Alrm = 1;
398 	(void)signal (SIGALRM, ticktock);
399 	return;
400 }
401 
402 static void
403 background()
404 {
405 #if	defined(DEBUG)
406     if (debug & DB_SDB)
407 	return;
408 #endif
409 
410     switch(fork())
411     {
412 	case -1:
413 	    fail ("Failed to fork child process (%s).\n", PERROR);
414 	    /*NOTREACHED*/
415 
416 	case 0:
417 	    (void) setpgrp();
418 	    am_in_background = 1;
419 	    return;
420 
421 	default:
422 	    note ("Print services started.\n");
423 	    exit(0);
424 	    /* NOTREACHED */
425     }
426 }
427 
428 static void
429 usage()
430 {
431 	note ("\
432 usage: lpsched [ options ]\n\
433     [ -f #filter-slots ]    (increase no. concurrent slow filters)\n\
434     [ -n #notify-slots ]    (increase no. concurrent notifications)\n\
435     [ -r #reserved-fds ]    (increase margin of file descriptors)\n"
436 	);
437 
438 #if	defined(DEBUG)
439 	note ("\
440     [ -D flag[,flag...] ]   (debug modes; use -D? for usage info.)\n\
441     [ -d ]                  (same as -D ALL)\n\
442     [ -s ]                  (don't trap most signals)\n"
443 	);
444 #endif
445 
446 	note ("\
447 WARNING: all these options are currently unsupported\n"
448 	);
449 
450 	return;
451 }
452 
453 static void
454 Exit(n)
455     int		n;
456 {
457     fail ("Received unexpected signal %d; terminating.\n", n);
458 }
459 
460 static void
461 disable_signals()
462 {
463     int		i;
464 
465 # if defined(DEBUG)
466     if (!signals)
467 # endif
468 	for (i = 0; i < NSIG; i++)
469 		if (signal(i, SIG_IGN) != SIG_IGN)
470 			signal (i, Exit);
471 
472     (void) signal(SIGHUP, SIG_IGN);
473     (void) signal(SIGINT, SIG_IGN);
474     (void) signal(SIGQUIT, SIG_IGN);
475     (void) signal(SIGALRM, ticktock);
476     (void) signal(SIGTERM, lpshut);	/* needs arg, but sig# OK */
477     (void) signal(SIGCLD, SIG_IGN);
478     (void) signal(SIGTSTP, SIG_IGN);
479     (void) signal(SIGCONT, SIG_DFL);
480     (void) signal(SIGTTIN, SIG_IGN);
481     (void) signal(SIGTTOU, SIG_IGN);
482     (void) signal(SIGXFSZ, SIG_IGN);	/* could be a problem */
483     (void) signal(SIGWINCH, SIG_IGN);   /* if started in a window   */
484     (void) signal(SIGTHAW, SIG_IGN);   /* used by CPR - energystar */
485 
486 #if	defined(DEBUG)
487     if (debug & DB_ABORT)
488 	(void) signal(SIGABRT, SIG_DFL);
489 #endif
490 
491 }
492