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