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