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