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