xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/dowait.c (revision 2a8bcb4e)
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 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
31 
32 #include "lpsched.h"
33 #include "ctype.h"
34 #include "sys/stat.h"
35 #include <syslog.h>
36 
37 /*
38  * Macro to test if we should notify the user.
39  */
40 #define SHOULD_NOTIFY(PRS) \
41 	( \
42 		(PRS)->request->actions & (ACT_MAIL|ACT_WRITE|ACT_NOTIFY)\
43 	     || (PRS)->request->alert \
44 	)
45 
46 static char *		geterrbuf ( RSTATUS * );
47 
48 /**
49  ** dowait() - CLEAN UP CHILD THAT HAS FINISHED, RESCHEDULE ANOTHER TASK
50  **/
51 
52 void
dowait(void)53 dowait (void)
54 {
55 	int			exited,
56 				killed,
57 				canned,
58 				i;
59 	EXEC			*ep;
60 	char			*errbuf = NULL;
61 	register RSTATUS	*prs;
62 	register PSTATUS	*pps;
63 	register ALERT		*pas;
64 
65 	syslog(LOG_DEBUG, "dowait(%d)", DoneChildren);
66 	while (DoneChildren > 0) {
67 		DoneChildren--;
68 
69 		for (i = 0; (ep = Exec_Table[i]) != NULL; i++)
70 			if (ep->pid == -99)
71 				break;
72 
73 		syslog(LOG_DEBUG, "dowait(): 0x%8.8x", ep);
74 
75 		if (Exec_Table[i] == NULL)	/* nothing to cleanup */
76 			continue;
77 
78 		syslog(LOG_DEBUG, "dowait(): cleaning up 0x%8.8x", ep);
79 
80 		ep->pid = 0;
81 		ep->key = 0;	/* avoid subsequent sneaks */
82 		if (ep->md)
83 			DROP_MD(ep->md);
84 
85 		killed = KILLED(ep->status);
86 		exited = EXITED(ep->status);
87 
88 		syslog(LOG_DEBUG, "dowait(): type %d, killed %d, exited %d",
89 			ep->type, killed, exited);
90 
91 		switch (ep->type) {
92 
93 		case EX_INTERF:
94 			/*
95 			 * WARNING: It could be that when we get here
96 			 *
97 			 *	pps->request->printer != pps
98 			 *
99 			 * because the request has been assigned to
100 			 * another printer.
101 			 */
102 			pps = ep->ex.printer;
103 			prs = pps->request;
104 			pps->request = 0;
105 			pps->status &= ~PS_BUSY;
106 
107 			/*
108 			 * If the interface program exited cleanly
109 			 * or with just a user error, the printer
110 			 * is assumed to be working.
111 			 */
112 			if (0 <= exited && exited < EXEC_EXIT_USER) {
113 				pps->status &= ~PS_FAULTED;
114 				if (pps->alert->active)
115 					cancel_alert (A_PRINTER, pps);
116 			}
117 
118 			/*
119 			 * If the interface program was killed with
120 			 * SIGTERM, it may have been because we canceled
121 			 * the request, disabled the printer, or for some
122 			 * other reason stopped the request.
123 			 * If so, clear the "killed" flag because that's
124 			 * not the condition of importance here.
125 			 */
126 			canned = 0;
127 			if (killed == SIGTERM) {
128 				if (prs->request->outcome & RS_CANCELLED)
129 					canned = 1;
130 
131 				if (
132 					canned
133 				     || pps->status & (PS_DISABLED|PS_FAULTED)
134 				     || prs->request->outcome & RS_STOPPED
135 				     || Shutdown
136 				)
137 					killed = 0;
138 			}
139 
140 			/*
141 			 * If there was standard error output from the
142 			 * interface program, or if the interface program
143 			 * exited with a (user) exit code, or if it got
144 			 * a strange signal, the user should be notified.
145 			 */
146 			errbuf = geterrbuf(prs);
147 			if (
148 				errbuf
149 			     || (0 < exited && exited <= EXEC_EXIT_USER)
150 			     || killed
151 			) {
152 				if (exited != EXIT_RETRY) {
153 					prs->request->outcome |= RS_FAILED;
154 				}
155 				prs->request->outcome |= RS_NOTIFY;
156 				notify (prs, errbuf, killed, exited, 0);
157 				if (errbuf)
158 					Free (errbuf);
159 
160 			/*
161 			 * If the request was canceled, call "notify()"
162 			 * in case we're to notify the user.
163 			 */
164 			} else if (canned) {
165 				if (SHOULD_NOTIFY(prs))
166 					prs->request->outcome |= RS_NOTIFY;
167 				notify (prs, (char *)0, 0, 0, 0);
168 
169 			/*
170 			 * If the request finished successfully, call
171 			 * "notify()" in case we're to notify the user.
172 			 */
173 			} else if (exited == 0) {
174 				prs->request->outcome |= RS_PRINTED;
175 
176 				if (SHOULD_NOTIFY(prs))
177 					prs->request->outcome |= RS_NOTIFY;
178 				notify (prs, (char *)0, 0, 0, 0);
179 			}
180 
181 			/*
182 			 * If the interface program exits with an
183 			 * exit code higher than EXEC_EXIT_USER, it's
184 			 * a special case.
185 			 */
186 
187 			switch (exited) {
188 
189 			case EXEC_EXIT_FAULT:
190 				printer_fault (pps, prs, 0, 0);
191 				break;
192 
193 			case EXEC_EXIT_HUP:
194 				printer_fault (pps, prs, HANGUP_FAULT, 0);
195 				break;
196 
197 			case EXEC_EXIT_INTR:
198 				printer_fault (pps, prs, INTERRUPT_FAULT, 0);
199 				break;
200 
201 			case EXEC_EXIT_PIPE:
202 				printer_fault (pps, prs, PIPE_FAULT, 0);
203 				break;
204 
205 			case EXEC_EXIT_EXIT:
206 				note (
207 					"Bad exit from interface program for printer %s: %d\n",
208 					pps->printer->name,
209 					ep->Errno
210 				);
211 				printer_fault (pps, prs, EXIT_FAULT, 0);
212 				break;
213 
214 			case EXEC_EXIT_NPORT:
215 				printer_fault (pps, prs, OPEN_FAULT, ep->Errno);
216 				break;
217 
218 			case EXEC_EXIT_TMOUT:
219 				printer_fault (pps, prs, TIMEOUT_FAULT, 0);
220 				break;
221 
222 			case EXEC_EXIT_NOPEN:
223 				errno = ep->Errno;
224 				note (
225 					"Failed to open a print service file (%s).\n",
226 					PERROR
227 				);
228 				break;
229 
230 			case EXEC_EXIT_NEXEC:
231 				errno = ep->Errno;
232 				note (
233 					"Failed to exec child process (%s).\n",
234 					PERROR
235 				);
236 				break;
237 
238 			case EXEC_EXIT_NOMEM:
239 				mallocfail ();
240 				break;
241 
242 			case EXEC_EXIT_NFORK:
243 				errno = ep->Errno;
244 				note (
245 					"Failed to fork child process (%s).\n",
246 					PERROR
247 				);
248 				break;
249 
250 			case EXEC_EXIT_NPUSH:
251 				printer_fault (pps, prs, PUSH_FAULT, ep->Errno);
252 				break;
253 
254 			default:
255 				if ((exited & EXEC_EXIT_NMASK) == EXEC_EXIT_NDIAL)
256 					dial_problem (
257 						pps,
258 						prs,
259 						exited & ~EXEC_EXIT_NMASK
260 					);
261 
262 				else if (
263 					exited < -1
264 				     || exited > EXEC_EXIT_USER
265 				)
266 					note (
267 						"Bad exit from exec() for printer %s: %d\n",
268 						pps->printer->name,
269 						exited
270 					);
271 
272 				break;
273 			}
274 
275 			/*
276 			 * Being in the "dowait()" routine means the
277 			 * interface (and fast filter!) have stopped.
278 			 * If we have a fault and we're expected to try
279 			 * again later, make sure we try again later.
280 			 */
281 			if (
282 				(pps->status & PS_FAULTED)
283 			     && !STREQU(pps->printer->fault_rec, NAME_WAIT)
284 			     && !(pps->status & (PS_LATER|PS_DISABLED))
285 			) {
286 				load_str (&pps->dis_reason, CUZ_STOPPED);
287 				schedule (EV_LATER, WHEN_PRINTER, EV_ENABLE, pps);
288 			}
289 
290 			prs->request->outcome &= ~(RS_PRINTING|RS_STOPPED);
291 
292 			/*
293 			 * If the printer to which this request was
294 			 * assigned is not able to handle requests now,
295 			 * push waiting requests off on to another
296 			 * printer.
297 			 */
298 			if (prs->printer->status & (PS_FAULTED|PS_DISABLED|PS_LATER))
299 				(void)queue_repel (prs->printer, 0, (qchk_fnc_type)0);
300 
301 			/*
302 			 * If the request is now assigned to a different
303 			 * printer, call "schedule()" to fire up an
304 			 * interface. If this request also happens to
305 			 * be dead, or in need of refiltering, it won't
306 			 * get scheduled.
307 			 */
308 			if (
309 				prs->printer != pps
310 			)
311 				schedule (EV_INTERF, prs->printer);
312 
313 			check_request (prs);
314 
315 			/*
316 			 * Attract the FIRST request that is waiting to
317 			 * print to this printer, unless the printer isn't
318 			 * ready to print another request. We do this
319 			 * even though requests may already be assigned
320 			 * to this printer, because a request NOT assigned
321 			 * might be ahead of them in the queue.
322 			 */
323 			if (!(pps->status & (PS_FAULTED|PS_DISABLED|PS_LATER)))
324 				queue_attract (pps, qchk_waiting, 1);
325 
326 			break;
327 
328 		case EX_SLOWF:
329 			prs = ep->ex.request;
330 			ep->ex.request = 0;
331 			prs->exec = 0;
332 			prs->request->outcome &= ~RS_FILTERING;
333 
334 			/*
335 			 * If the slow filter was killed with SIGTERM,
336 			 * it may have been because we canceled the
337 			 * request, stopped the filtering, or put a
338 			 * change hold on the request. If so, clear
339 			 * the "killed" flag because that's not the
340 			 * condition of importance.
341 			 */
342 			canned = 0;
343 			if (killed == SIGTERM){
344 				if (prs->request->outcome & RS_CANCELLED)
345 					canned = 1;
346 
347 				if (
348 					canned
349 				     || prs->request->outcome & RS_STOPPED
350 				     || Shutdown
351 				)
352 					killed = 0;
353 			}
354 
355 			/*
356 			 * If there was standard error output from the
357 			 * slow filter, or if the interface program exited
358 			 * with a non-zero exit code, the user should
359 			 * be notified.
360 			 */
361 			errbuf = geterrbuf(prs);
362 			if (prs->request->outcome
363 			    & (RS_REFILTER | RS_STOPPED)) {
364 				if (errbuf) {
365 					Free(errbuf);
366 					errbuf = NULL;
367 				}
368 			}
369 			if (
370 				errbuf
371 			     || 0 < exited && exited <= EXEC_EXIT_USER
372 			     || killed
373 			) {
374 				prs->request->outcome |= RS_FAILED;
375 				prs->request->outcome |= RS_NOTIFY;
376 				notify (prs, errbuf, killed, exited, 1);
377 				if (errbuf)
378 					Free (errbuf);
379 
380 
381 			/*
382 			 * If the request was canceled, call "notify()"
383 			 * in case we're to notify the user.
384 			 */
385 			} else if (canned) {
386 				if (SHOULD_NOTIFY(prs))
387 					prs->request->outcome |= RS_NOTIFY;
388 				notify (prs, (char *)0, 0, 0, 1);
389 
390 			/*
391 			 * If the slow filter exited normally, mark
392 			 * the request as finished slow filtering.
393 			 */
394 			} else if (exited == 0) {
395 				prs->request->outcome |= RS_FILTERED;
396 
397 			} else if (exited == -1) {
398 				/*EMPTY*/;
399 
400 			} else if (exited == EXEC_EXIT_NOPEN) {
401 				errno = ep->Errno;
402 				note (
403 					"Failed to open a print service file (%s).\n",
404 					PERROR
405 				);
406 
407 			} else if (exited == EXEC_EXIT_NEXEC) {
408 				errno = ep->Errno;
409 				note (
410 					"Failed to exec child process (%s).\n",
411 					PERROR
412 				);
413 
414 			} else if (exited == EXEC_EXIT_NOMEM) {
415 				mallocfail ();
416 
417 			}
418 
419 			prs->request->outcome &= ~RS_STOPPED;
420 
421 			schedule (EV_INTERF, prs->printer);
422 			if (
423 				prs->request->outcome & RS_REFILTER
424 			)
425 				schedule (EV_SLOWF, prs);
426 			else
427 				schedule (EV_SLOWF, (RSTATUS *)0);
428 
429 			check_request (prs);
430 			break;
431 
432 		case EX_NOTIFY:
433 			prs = ep->ex.request;
434 			ep->ex.request = 0;
435 			prs->exec = 0;
436 
437 			prs->request->outcome &= ~RS_NOTIFYING;
438 			    if (!Shutdown || !killed)
439 				prs->request->outcome &= ~RS_NOTIFY;
440 
441 			/*
442 			 * Now that this notification process slot
443 			 * has opened up, schedule the next notification
444 			 * (if any).
445 			 */
446 			schedule (EV_NOTIFY, (RSTATUS *)0);
447 
448 			check_request (prs);
449 			break;
450 
451 		case EX_ALERT:
452 			pas = ep->ex.printer->alert;
453 			goto CleanUpAlert;
454 
455 		case EX_FALERT:
456 			pas = ep->ex.form->alert;
457 			goto CleanUpAlert;
458 
459 		case EX_PALERT:
460 			pas = ep->ex.pwheel->alert;
461 			/*
462 			 * CAUTION: It may well be that we've removed
463 			 * the print wheel by the time we get here.
464 			 * Only the alert structure (and exec structure)
465 			 * can be considered okay.
466 			 */
467 
468 CleanUpAlert:
469 			if (Shutdown)
470 				break;
471 
472 			if (ep->flags & EXF_RESTART) {
473 				ep->flags &= ~(EXF_RESTART);
474 				if (exec(ep->type, ep->ex.form) == 0) {
475 					pas->active = 1;
476 					break;
477 				}
478 			}
479 			(void)Unlink (pas->msgfile);
480 			break;
481 
482 		}
483 	}
484 
485 	return;
486 }
487 
488 
489 /**
490  ** geterrbuf() - READ NON-BLANK STANDARD ERROR OUTPUT
491  **/
492 
493 static char *
geterrbuf(RSTATUS * prs)494 geterrbuf(RSTATUS *prs)
495 {
496 	register char		*cp;
497 	int                     fd,
498 				n;
499 	char                    *buf    = 0,
500 				*file;
501 	struct stat             statbuf;
502 
503 	if (!prs) return(NULL);
504 
505 	file = makereqerr(prs);
506 	if (
507 		Stat(file, &statbuf) == 0
508 	     && statbuf.st_size
509 	     && (fd = Open(file, O_RDONLY)) != -1
510 	) {
511 		/*
512 		 * Don't die if we can't allocate space for this
513 		 * file--the file may be huge!
514 		 */
515 		lp_alloc_fail_handler = 0;
516 		if ((buf = Malloc(statbuf.st_size + 1)))
517 			if ((n = Read(fd, buf, statbuf.st_size)) > 0) {
518 				buf[n] = 0;
519 
520 				/*
521 				 * NOTE: Ignore error output with no
522 				 * printable text. This hides problems we
523 				 * have with some shell scripts that
524 				 * occasionally cause spurious newlines
525 				 * when stopped via SIGTERM. Without this
526 				 * check for non-blank output, stopping
527 				 * a request sometimes causes a request
528 				 * failure.
529 				 */
530 				for (cp = buf; *cp && isspace(*cp); cp++)
531 					;
532 				if (!*cp) {
533 					Free (buf);
534 					buf = 0;
535 				}
536 			} else {
537 				Free (buf);
538 				buf = 0;
539 			}
540 		lp_alloc_fail_handler = mallocfail;
541 		Close(fd);
542 	}
543 	if (file)
544 		Free (file);
545 
546 	return (buf);
547 }
548 
549 /**
550  ** check_request() - CLEAN UP AFTER REQUEST
551  **/
552 
553 void
check_request(RSTATUS * prs)554 check_request(RSTATUS *prs)
555 {
556 	/*
557 	 * If the request is done, decrement the count of requests
558 	 * needing the form or print wheel. Update the disk copy of
559 	 * the request. If we're finished with the request, get rid of it.
560 	 */
561 	if (prs->request->outcome & RS_DONE) {
562 		unqueue_form (prs);
563 		unqueue_pwheel (prs);
564 		putrequest (prs->req_file, prs->request);
565 		if (!(prs->request->outcome & (RS_ACTIVE | RS_NOTIFY))) {
566 			rmfiles (prs, 1);
567 			free_rstatus (prs);
568 		}
569 	}
570 	return;
571 }
572 
573 /**
574  ** check_children()
575  **/
576 
577 void
check_children(void)578 check_children(void)
579 {
580 	register int		i;
581 
582 	for (i = 0; Exec_Table[i] != NULL; i++)
583 		if (Exec_Table[i]->pid > 0)
584 			break;
585 
586 	if (Exec_Table[i] == NULL)
587 		Shutdown = 2;
588 }
589