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