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