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