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
dowait(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 *
geterrbuf(RSTATUS * prs)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
check_request(RSTATUS * prs)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
check_children(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