xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpsched.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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 /*
23  * Copyright 2006 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 #include <stdio_ext.h>
44 
45 
46 int			lock_fd		= -1;
47 int			isStartingForms = 0;
48 int			Starting	= 0;
49 int			Shutdown	= 0;
50 int			DoneChildren	= 0;
51 int			Sig_Alrm	= 0;
52 int			OpenMax		= OPEN_MAX;
53 int			Reserve_Fds	= 0;
54 
55 char			*Local_System	= 0;
56 char			*SHELL		= 0;
57 
58 gid_t			Lp_Gid;
59 uid_t			Lp_Uid;
60 
61 #if	defined(DEBUG)
62 unsigned long		debug = 0;
63 static int		signals = 0;
64 #endif
65 
66 extern int		errno;
67 extern void		shutdown_messages();
68 
69 int			am_in_background	= 0;
70 
71 static void		disable_signals();
72 static void		startup();
73 static void		process();
74 static void		ticktock(int);
75 static void		background();
76 static void		usage();
77 static void		Exit();
78 static void		disable_signals();
79 
80 /**
81  ** main()
82  **/
83 
84 int
85 main(int argc, char *argv[])
86 {
87     int		c;
88     extern char	*optarg;
89     extern int	optopt;
90     extern int	opterr;
91     char *	cp;
92     struct rlimit rlim;
93     int fd_limit = 4096;
94 
95 	(void) setlocale(LC_ALL, "");
96     if ((cp = strrchr(argv[0], '/')) == NULL)
97 	    cp = argv[0];
98     else
99 	    cp++;
100 
101     /* open the syslog() */
102     openlog(cp, LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);
103 
104 	SHELL = DEFAULT_SHELL;
105 
106     opterr = 0;
107     while((c = getopt(argc, (char * const *)argv, "dsf:n:r:M:p:")) != EOF)
108         switch(c)
109         {
110 # if defined (DEBUG)
111 	    case 'd':
112 		debug = DB_ALL;
113 		syslog(LOG_DEBUG, "debug = DB_ALL");
114 		break;
115 
116 	    case 's':
117 		signals++;
118 		break;
119 # endif /* DEBUG */
120 
121 	    case 'f':
122 		if ((ET_SlowSize = atoi(optarg)) < 1)
123 		    ET_SlowSize = 1;
124 		syslog(LOG_DEBUG, "-f option is %d", ET_SlowSize);
125 		break;
126 
127 	    case 'n':
128 		if ((ET_NotifySize = atoi(optarg)) < 1)
129 		    ET_NotifySize = 1;
130 		syslog(LOG_DEBUG, "-n option is %d", ET_NotifySize);
131 		break;
132 
133 	    case 'r':
134 		if ((Reserve_Fds = atoi(optarg)) < 0)
135 			Reserve_Fds = 0;
136 		syslog(LOG_DEBUG, "-r option is %d", Reserve_Fds);
137 		break;
138 
139 	    case 'p':
140 		if ((fd_limit = atoi(optarg)) < 16)
141 			fd_limit = 4096;
142 		syslog(LOG_DEBUG, "-p option is %d", fd_limit);
143 		break;
144 
145 	    case '?':
146 		if (optopt == '?') {
147 		    usage ();
148 		    exit (0);
149 		} else
150 		    fail ("%s: illegal option -- %c\n", argv[0], optopt);
151 	}
152 
153 	/* reset the fd resource limit */
154 	rlim.rlim_max = rlim.rlim_cur = fd_limit;
155 	setrlimit(RLIMIT_NOFILE, &rlim);
156 	getrlimit(RLIMIT_NOFILE, &rlim);
157 	(void) enable_extended_FILE_stdio(-1, -1);
158 	syslog(LOG_DEBUG, "file descriptor resource limit is %d (~%d printers)",
159 		rlim.rlim_cur, (rlim.rlim_cur - 12)/ 2);
160 
161     lp_alloc_fail_handler = mallocfail;
162 
163     startup();
164 
165     process();
166 
167     lpshut(1);	/* one last time to clean up */
168     /*NOTREACHED*/
169     return (0);
170 }
171 
172 static void
173 startup()
174 {
175     struct passwd		*p;
176 
177 
178     Starting = 1;
179     getpaths();
180 
181     /*
182      * There must be a user named "lp".
183      */
184     if ((p = getpwnam(LPUSER)) == NULL)
185 	fail ("Can't find the user \"lp\" on this system!\n");
186 
187     Lp_Uid = p->pw_uid;
188     Lp_Gid = p->pw_gid;
189 
190     /*
191      * Only "root" is allowed to run us.
192      */
193     if ((getuid() != 0) && (geteuid() != 0))
194 	fail ("You must be \"root\" to run this program.\n");
195 
196     setuid (0);
197 
198     Local_System = Strdup("localhost");
199 
200     /*
201      * Make sure that all critical directories are present and that
202      * symbolic links are correct.
203      */
204     lpfsck();
205 
206     /*
207      * Try setting the lock file to see if another Spooler is running.
208      * We'll release it immediately; this allows us to fork the child
209      * that will run in the background. The child will relock the file.
210      */
211     if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0)
212 	if (errno == EAGAIN)
213 	    fail ("Print services already active.\n");
214 	else
215 	    fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR);
216     close(lock_fd);
217 
218     background();
219     /*
220      * We are the child process now.
221      */
222 
223     if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0)
224 	fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR);
225 
226     Close (0);
227     Close (2);
228     if (am_in_background)
229 	Close (1);
230 
231     if ((OpenMax = ulimit(4, 0L)) == -1)
232 	OpenMax = OPEN_MAX;
233 
234     disable_signals();
235 
236     init_messages();
237 
238     init_memory();
239 
240     note ("Print services started.\n");
241     Starting = 0;
242 }
243 
244 void
245 lpshut(int immediate)
246 {
247 	int			i;
248 	extern MESG *		Net_md;
249 
250 
251 	/*
252 	 * If this is the first time here, stop all running
253 	 * child processes, and shut off the alarm clock so
254 	 * it doesn't bug us.
255 	 */
256 	if (!Shutdown) {
257 		mputm (Net_md, S_SHUTDOWN, 1);
258 		for (i = 0; Exec_Table != NULL && Exec_Table[i] != NULL; i++)
259 			terminate (Exec_Table[i]);
260 		alarm (0);
261 		Shutdown = (immediate? 2 : 1);
262 	}
263 
264 	/*
265 	 * If this is an express shutdown, or if all the
266 	 * child processes have been cleaned up, clean up
267 	 * and get out.
268 	 */
269 	if (Shutdown == 2) {
270 
271 		/*
272 		 * We don't shut down the message queues until
273 		 * now, to give the children a chance to answer.
274 		 * This means an LP command may have been snuck
275 		 * in while we were waiting for the children to
276 		 * finish, but that's OK because we'll have
277 		 * stored the jobs on disk (that's part of the
278 		 * normal operation, not just during shutdown phase).
279 		 */
280 		shutdown_messages();
281 
282 		(void) close(lock_fd);
283 		(void) Unlink(Lp_Schedlock);
284 
285 		note ("Print services stopped.\n");
286 		exit (0);
287 		/*NOTREACHED*/
288 	}
289 }
290 
291 static void
292 process()
293 {
294     FSTATUS	*pfs;
295     PWSTATUS	*ppws;
296     int i;
297 
298 
299     /*
300      * Call the "check_..._alert()" routines for each form/print-wheel;
301      * we need to do this at this point because these routines
302      * short-circuit themselves while we are in startup mode.
303      * Calling them now will kick off any necessary alerts.
304      */
305     isStartingForms = 1;
306     for (i = 0; FStatus != NULL && FStatus[i] != NULL; i++)
307 	check_form_alert (FStatus[i], (_FORM *)0);
308     isStartingForms = 0;
309 
310     for (i = 0; PWStatus != NULL && PWStatus[i] != NULL; i++)
311 	check_pwheel_alert (PWStatus[i], (PWHEEL *)0);
312 
313     /*
314      * Clear the alarm, then schedule an EV_ALARM. This will clear
315      * all events that had been scheduled for later without waiting
316      * for the next tick.
317      */
318     alarm (0);
319     schedule (EV_ALARM);
320 
321     /*
322      * Start the ball rolling.
323      */
324     schedule (EV_INTERF, (PSTATUS *)0);
325     schedule (EV_NOTIFY, (RSTATUS *)0);
326     schedule (EV_SLOWF, (RSTATUS *)0);
327 
328     for (EVER) {
329 	take_message ();
330 
331 	if (Sig_Alrm)
332 		schedule (EV_ALARM);
333 
334 	if (DoneChildren)
335 		dowait ();
336 
337 	if (Shutdown)
338 		check_children();
339 	if (Shutdown == 2)
340 		break;
341     }
342 }
343 
344 /*ARGSUSED*/
345 static void
346 ticktock(int sig)
347 {
348 	Sig_Alrm = 1;
349 	(void)signal (SIGALRM, ticktock);
350 	return;
351 }
352 
353 static void
354 background()
355 {
356 #if	defined(DEBUG)
357     if (debug & DB_SDB)
358 	return;
359 #endif
360 
361     switch(fork())
362     {
363 	case -1:
364 	    fail ("Failed to fork child process (%s).\n", PERROR);
365 	    /*NOTREACHED*/
366 
367 	case 0:
368 	    (void) setpgrp();
369 	    am_in_background = 1;
370 	    return;
371 
372 	default:
373 	    note ("Print services started.\n");
374 	    exit(0);
375 	    /* NOTREACHED */
376     }
377 }
378 
379 static void
380 usage()
381 {
382 	note ("\
383 usage: lpsched [ options ]\n\
384     [ -f #filter-slots ]    (increase no. concurrent slow filters)\n\
385     [ -n #notify-slots ]    (increase no. concurrent notifications)\n\
386     [ -r #reserved-fds ]    (increase margin of file descriptors)\n"
387 	);
388 
389 #if	defined(DEBUG)
390 	note ("\
391     [ -d ]                  (same as -D ALL)\n\
392     [ -s ]                  (don't trap most signals)\n"
393 	);
394 #endif
395 
396 	note ("\
397 WARNING: all these options are currently unsupported\n"
398 	);
399 
400 	return;
401 }
402 
403 static void
404 Exit(n)
405     int		n;
406 {
407     fail ("Received unexpected signal %d; terminating.\n", n);
408 }
409 
410 static void
411 disable_signals()
412 {
413     int		i;
414 
415 # if defined(DEBUG)
416     if (!signals)
417 # endif
418 	for (i = 0; i < NSIG; i++)
419 		if (signal(i, SIG_IGN) != SIG_IGN)
420 			signal (i, Exit);
421 
422     (void) signal(SIGHUP, SIG_IGN);
423     (void) signal(SIGINT, SIG_IGN);
424     (void) signal(SIGQUIT, SIG_IGN);
425     (void) signal(SIGALRM, ticktock);
426     (void) signal(SIGTERM, lpshut);	/* needs arg, but sig# OK */
427     (void) signal(SIGCLD, SIG_IGN);
428     (void) signal(SIGTSTP, SIG_IGN);
429     (void) signal(SIGCONT, SIG_DFL);
430     (void) signal(SIGTTIN, SIG_IGN);
431     (void) signal(SIGTTOU, SIG_IGN);
432     (void) signal(SIGXFSZ, SIG_IGN);	/* could be a problem */
433     (void) signal(SIGWINCH, SIG_IGN);   /* if started in a window   */
434     (void) signal(SIGTHAW, SIG_IGN);   /* used by CPR - energystar */
435 
436 #if	defined(DEBUG)
437     if (debug & DB_ABORT)
438 	(void) signal(SIGABRT, SIG_DFL);
439 #endif
440 
441 }
442