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