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 /* 23 * Copyright 1998 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 31 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.15.1.7 */ 32 33 #include "stdarg.h" 34 #include "lpsched.h" 35 #include <syslog.h> 36 37 extern int isStartingForms; 38 39 typedef struct later { 40 struct later * next; 41 int event, 42 ticks; 43 union arg { 44 PSTATUS * printer; 45 RSTATUS * request; 46 FSTATUS * form; 47 } arg; 48 } LATER; 49 50 static LATER LaterHead = { 0 }, 51 TempHead; 52 53 static void ev_interf(PSTATUS *); 54 static void ev_message(PSTATUS *); 55 static void ev_form_message(FSTATUS *); 56 static int ev_slowf(RSTATUS *); 57 static int ev_notify(RSTATUS *); 58 59 static EXEC *find_exec_slot(EXEC *, int); 60 61 static char *_event_name(int event) 62 { 63 static char *_names[] = { 64 "", "EV_SLOWF", "EV_INTERF", "EV_NOTIFY", "EV_LATER", "EV_ALARM", 65 "EV_MESSAGE", "EV_CHECKCHILD"," EV_SYSTEM", "EV_ENABLE", 66 "EV_POLLBSDSYSTEMS", "EV_FORM_MESSAGE", "EV_STATUS", 67 NULL }; 68 69 if ((event < 0) || (event > EV_STATUS)) 70 return ("BAD_EVENT"); 71 else 72 return (_names[event]); 73 } 74 75 /* 76 * schedule() - SCHEDULE BY EVENT 77 */ 78 79 /*VARARGS1*/ 80 void 81 schedule(int event, ...) 82 { 83 va_list ap; 84 85 LATER * plprev; 86 LATER * pl; 87 LATER * plnext = 0; 88 89 register PSTATUS * pps; 90 register RSTATUS * prs; 91 register FSTATUS * pfs; 92 93 /* 94 * If we're in the process of shutting down, don't 95 * schedule anything. 96 */ 97 syslog(LOG_DEBUG, "schedule(%s)", _event_name(event)); 98 99 if (Shutdown) 100 return; 101 102 va_start (ap, event); 103 104 /* 105 * If we're still in the process of starting up, don't start 106 * anything! Schedule it for one tick later. While we're starting 107 * ticks aren't counted, so the events won't be started. 108 * HOWEVER, with a count of 1, a single EV_ALARM after we're 109 * finished starting will be enough to clear all things scheduled 110 * for later. 111 */ 112 if (Starting) { 113 switch (event) { 114 115 case EV_INTERF: 116 case EV_ENABLE: 117 pps = va_arg(ap, PSTATUS *); 118 schedule (EV_LATER, 1, event, pps); 119 goto Return; 120 121 case EV_SLOWF: 122 case EV_NOTIFY: 123 prs = va_arg(ap, RSTATUS *); 124 schedule (EV_LATER, 1, event, prs); 125 goto Return; 126 127 case EV_MESSAGE: 128 pps = va_arg(ap, PSTATUS *); 129 schedule (EV_LATER, 1, event, pps); 130 goto Return; 131 132 case EV_FORM_MESSAGE: 133 pfs = va_arg(ap, FSTATUS *); 134 schedule (EV_LATER, 1, event, pfs); 135 goto Return; 136 137 case EV_LATER: 138 /* 139 * This is okay--in fact it may be us! 140 */ 141 break; 142 143 case EV_ALARM: 144 /* 145 * The alarm will go off again, hold off for now. 146 */ 147 goto Return; 148 149 } 150 } 151 152 /* 153 * Schedule something: 154 */ 155 switch (event) { 156 157 case EV_INTERF: 158 if ((pps = va_arg(ap, PSTATUS *)) != NULL) 159 ev_interf (pps); 160 161 else 162 for (pps = walk_ptable(1); pps; pps = walk_ptable(0)) 163 ev_interf (pps); 164 165 break; 166 167 /* 168 * The EV_ENABLE event is used to get a printer going again 169 * after waiting for a fault to be cleared. We used to use 170 * just the EV_INTERF event, but this wasn't enough: For 171 * requests that can go on several different printers (e.g. 172 * queued for class, queued for ``any''), a printer is 173 * arbitrarily assigned. The EV_INTERF event just checks 174 * assignments, not possibilities, so a printer with no 175 * assigned requests but still eligible to handle one or 176 * more requests would never automatically start up again after 177 * a fault. The EV_ENABLE event calls "enable()" which eventually 178 * gets around to invoking the EV_INTERF event. However, it first 179 * calls "queue_attract()" to get an eligible request assigned 180 * so that things proceed. This also makes sense from the 181 * following standpoint: The documented method of getting a 182 * printer going, while it is waiting for auto-retry, is to 183 * manually issue the enable command! 184 * 185 * Note: "enable()" will destroy the current record of the fault, 186 * so if the fault is still with us any new alert will not include 187 * the history of each repeated fault. This is a plus and a minus, 188 * usually a minus: While a repeated fault may occasionally show 189 * a varied record, usually the same reason is given each time; 190 * before switching to EV_ENABLE we typically saw a boring, long 191 * list of identical reasons. 192 */ 193 case EV_ENABLE: 194 if ((pps = va_arg(ap, PSTATUS *)) != NULL) 195 enable (pps); 196 else 197 for (pps = walk_ptable(1); pps; pps = walk_ptable(0)) 198 enable (pps); 199 break; 200 201 case EV_SLOWF: 202 if ((prs = va_arg(ap, RSTATUS *)) != NULL) 203 (void) ev_slowf (prs); 204 else 205 for (prs = Request_List; prs && ev_slowf(prs) != -1; 206 prs = prs->next); 207 break; 208 209 case EV_NOTIFY: 210 if ((prs = va_arg(ap, RSTATUS *)) != NULL) 211 (void) ev_notify (prs); 212 else 213 for (prs = Request_List; prs && ev_notify(prs) != -1; 214 prs = prs->next); 215 break; 216 217 case EV_MESSAGE: 218 pps = va_arg(ap, PSTATUS *); 219 ev_message(pps); 220 break; 221 222 case EV_FORM_MESSAGE: 223 pfs = va_arg(ap, FSTATUS *); 224 ev_form_message(pfs); 225 break; 226 227 case EV_LATER: 228 pl = (LATER *)Malloc(sizeof (LATER)); 229 230 if (!LaterHead.next) 231 alarm (CLOCK_TICK); 232 233 pl->next = LaterHead.next; 234 LaterHead.next = pl; 235 236 pl->ticks = va_arg(ap, int); 237 pl->event = va_arg(ap, int); 238 switch (pl->event) { 239 240 case EV_MESSAGE: 241 case EV_INTERF: 242 case EV_ENABLE: 243 pl->arg.printer = va_arg(ap, PSTATUS *); 244 if (pl->arg.printer) 245 pl->arg.printer->status |= PS_LATER; 246 break; 247 248 case EV_FORM_MESSAGE: 249 pl->arg.form = va_arg(ap, FSTATUS *); 250 break; 251 252 case EV_SLOWF: 253 case EV_NOTIFY: 254 pl->arg.request = va_arg(ap, RSTATUS *); 255 break; 256 257 } 258 break; 259 260 case EV_ALARM: 261 Sig_Alrm = 0; 262 263 /* 264 * The act of scheduling some of the ``laters'' may 265 * cause new ``laters'' to be added to the list. 266 * To ease the handling of the linked list, we first 267 * run through the list and move all events ready to 268 * be scheduled to another list. Then we schedule the 269 * events off the new list. This leaves the main ``later'' 270 * list ready for new events. 271 */ 272 TempHead.next = 0; 273 for (pl = (plprev = &LaterHead)->next; pl; pl = plnext) { 274 plnext = pl->next; 275 if (--pl->ticks) 276 plprev = pl; 277 else { 278 plprev->next = plnext; 279 280 pl->next = TempHead.next; 281 TempHead.next = pl; 282 } 283 } 284 285 for (pl = TempHead.next; pl; pl = plnext) { 286 plnext = pl->next; 287 switch (pl->event) { 288 289 case EV_MESSAGE: 290 case EV_INTERF: 291 case EV_ENABLE: 292 pl->arg.printer->status &= ~PS_LATER; 293 schedule (pl->event, pl->arg.printer); 294 break; 295 296 case EV_FORM_MESSAGE: 297 schedule (pl->event, pl->arg.form); 298 break; 299 300 case EV_SLOWF: 301 case EV_NOTIFY: 302 schedule (pl->event, pl->arg.request); 303 break; 304 305 } 306 Free ((char *)pl); 307 } 308 309 if (LaterHead.next) 310 alarm (CLOCK_TICK); 311 break; 312 313 } 314 315 Return: va_end (ap); 316 317 return; 318 } 319 320 /* 321 * maybe_schedule() - MAYBE SCHEDULE SOMETHING FOR A REQUEST 322 */ 323 324 void 325 maybe_schedule(RSTATUS *prs) 326 { 327 /* 328 * Use this routine if a request has been changed by some 329 * means so that it is ready for filtering or printing, 330 * but a previous filtering or printing process for this 331 * request MAY NOT have finished yet. If a process is still 332 * running, then the cleanup of that process will cause 333 * "schedule()" to be called. Calling "schedule()" regardless 334 * might make another request slip ahead of this request. 335 */ 336 337 /* 338 * "schedule()" will refuse if this request is filtering. 339 * It will also refuse if the request ``was'' filtering 340 * but the filter was terminated in "validate_request()", 341 * because we can not have heard from the filter process 342 * yet. Also, when called with a particular request, 343 * "schedule()" won't slip another request ahead. 344 */ 345 if (NEEDS_FILTERING(prs)) 346 schedule (EV_SLOWF, prs); 347 348 else if (!(prs->request->outcome & RS_STOPPED)) 349 schedule (EV_INTERF, prs->printer); 350 351 return; 352 } 353 354 static void 355 ev_message(PSTATUS *pps) 356 { 357 register RSTATUS *prs; 358 char *systemName; 359 char toSelf; 360 361 syslog(LOG_DEBUG, "ev_message(%s)", 362 (pps && pps->request && pps->request->req_file ? 363 pps->request->req_file : "NULL")); 364 365 toSelf = 0; 366 BEGIN_WALK_BY_PRINTER_LOOP (prs, pps) 367 note("prs (%d) pps (%d)\n", prs, pps); 368 systemName = (prs->secure ? prs->secure->system : NULL); 369 if ((!systemName) || STREQU(systemName, Local_System)) 370 if (!toSelf) { 371 toSelf = 1; 372 exec(EX_FAULT_MESSAGE, pps, prs); 373 } 374 END_WALK_LOOP 375 } 376 377 static void 378 ev_form_message_body(FSTATUS *pfs, RSTATUS *prs, char *toSelf, char ***sysList) 379 { 380 char *systemName; 381 systemName = (prs && prs->secure ? prs->secure->system : NULL); 382 383 syslog(LOG_DEBUG, "ev_form_message_body(%s, %s, %d, 0x%x)", 384 (pfs && pfs->form && pfs->form->name ? pfs->form->name : "NULL"), 385 (systemName ? systemName : "NULL"), (toSelf ? *toSelf : 0), 386 sysList); 387 388 if ((!systemName) || STREQU(systemName, Local_System)) 389 if (!*toSelf) { 390 *toSelf = 1; 391 exec(EX_FORM_MESSAGE, pfs); 392 } 393 } 394 395 static void 396 ev_form_message(FSTATUS *pfs) 397 { 398 register RSTATUS *prs; 399 char **sysList; 400 char toSelf; 401 402 syslog(LOG_DEBUG, "ev_form_message(%s)", 403 (pfs && pfs->form && pfs->form->name ? 404 pfs->form->name : "NULL")); 405 406 toSelf = 0; 407 sysList = NULL; 408 BEGIN_WALK_BY_FORM_LOOP (prs, pfs) 409 ev_form_message_body(pfs, prs, &toSelf, &sysList); 410 END_WALK_LOOP 411 if (NewRequest && (NewRequest->form == pfs)) 412 ev_form_message_body(pfs, NewRequest, &toSelf, &sysList); 413 414 freelist(sysList); 415 } 416 417 /* 418 * ev_interf() - CHECK AND EXEC INTERFACE PROGRAM 419 */ 420 421 /* 422 * Macro to check if the request needs a print wheel or character set (S) 423 * and the printer (P) has it mounted or can select it. Since the request 424 * has already been approved for the printer, we don't have to check the 425 * character set, just the mount. If the printer has selectable character 426 * sets, there's nothing to check so the request is ready to print. 427 */ 428 #define MATCH(PRS, PPS) (\ 429 !(PPS)->printer->daisy || \ 430 !(PRS)->pwheel_name || \ 431 !((PRS)->status & RSS_PWMAND) || \ 432 STREQU((PRS)->pwheel_name, NAME_ANY) || \ 433 ((PPS)->pwheel_name && \ 434 STREQU((PPS)->pwheel_name, (PRS)->pwheel_name))) 435 436 437 static void 438 ev_interf(PSTATUS *pps) 439 { 440 register RSTATUS *prs; 441 442 syslog(LOG_DEBUG, "ev_interf(%s)", 443 (pps && pps->request && pps->request->req_file ? 444 pps->request->req_file : "NULL")); 445 446 447 /* 448 * If the printer isn't tied up doing something 449 * else, and isn't disabled, see if there is a request 450 * waiting to print on it. Note: We don't include 451 * PS_FAULTED here, because simply having a printer 452 * fault (without also being disabled) isn't sufficient 453 * to keep us from trying again. (In fact, we HAVE TO 454 * try again, to see if the fault has gone away.) 455 * 456 * NOTE: If the printer is faulted but the filter controlling 457 * the printer is waiting for the fault to clear, a 458 * request will still be attached to the printer, as 459 * evidenced by "pps->request", so we won't try to 460 * schedule another request! 461 */ 462 if (pps->request || pps->status & (PS_DISABLED|PS_LATER|PS_BUSY)) 463 return; 464 465 BEGIN_WALK_BY_PRINTER_LOOP (prs, pps) 466 /* 467 * Just because the printer isn't busy and the 468 * request is assigned to this printer, don't get the 469 * idea that the request can't be printing (RS_ACTIVE), 470 * because another printer may still have the request 471 * attached but we've not yet heard from the child 472 * process controlling that printer. 473 */ 474 if (qchk_waiting(prs)) 475 476 if (isFormUsableOnPrinter(pps, prs->form) && MATCH(prs, pps)) { 477 /* 478 * We have the waiting request, we have 479 * the ready (local) printer. If the exec fails 480 * because the fork failed, schedule a 481 * try later and claim we succeeded. The 482 * later attempt will sort things out, 483 * e.g. will re-schedule if the fork fails 484 * again. 485 */ 486 pps->request = prs; 487 if (exec(EX_INTERF, pps) == 0) { 488 pps->status |= PS_BUSY; 489 return; 490 } 491 pps->request = 0; 492 if (errno == EAGAIN) { 493 load_str (&pps->dis_reason, CUZ_NOFORK); 494 schedule (EV_LATER, WHEN_FORK, EV_ENABLE, pps); 495 return; 496 } 497 } 498 END_WALK_LOOP 499 500 return; 501 } 502 503 /* 504 * ev_slowf() - CHECK AND EXEC SLOW FILTER 505 */ 506 507 static int 508 ev_slowf(RSTATUS *prs) 509 { 510 register EXEC *ep; 511 512 syslog(LOG_DEBUG, "ev_slowf(%s)", 513 (prs && prs->req_file ? prs->req_file : "NULL")); 514 515 /* 516 * Return -1 if no more can be executed (no more exec slots) 517 * or if it's unwise to execute any more (fork failed). 518 */ 519 520 if (!(ep = find_exec_slot(Exec_Slow, ET_SlowSize))) 521 return (-1); 522 523 if (!(prs->request->outcome & (RS_DONE|RS_HELD|RS_ACTIVE)) && 524 NEEDS_FILTERING(prs)) { 525 (prs->exec = ep)->ex.request = prs; 526 if (exec(EX_SLOWF, prs) != 0) { 527 ep->ex.request = 0; 528 prs->exec = 0; 529 if (errno == EAGAIN) { 530 schedule (EV_LATER, WHEN_FORK, EV_SLOWF, prs); 531 return (-1); 532 } 533 } 534 } 535 return (0); 536 } 537 538 /* 539 * ev_notify() - CHECK AND EXEC NOTIFICATION 540 */ 541 542 static int 543 ev_notify(RSTATUS *prs) 544 { 545 register EXEC *ep; 546 547 syslog(LOG_DEBUG, "ev_notify(%s)", 548 (prs && prs->req_file ? prs->req_file : "NULL")); 549 550 /* 551 * Return -1 if no more can be executed (no more exec slots) 552 * or if it's unwise to execute any more (fork failed, already 553 * sent one to remote side). 554 */ 555 556 /* 557 * If the job came from a remote machine, we forward the 558 * outcome of the request to the network manager for sending 559 * to the remote side. 560 */ 561 if (prs->request->actions & ACT_NOTIFY) { 562 if (prs->request->outcome & RS_NOTIFY) { 563 prs->request->actions &= ~ACT_NOTIFY; 564 return (0); /* but try another request */ 565 } 566 /* 567 * If the job didn't come from a remote system, 568 * we'll try to start a process to send the notification 569 * to the user. But we only allow so many notifications 570 * to run at the same time, so we may not be able to 571 * do it. 572 */ 573 } else if (!(ep = find_exec_slot(Exec_Notify, ET_NotifySize))) 574 return (-1); 575 576 else if (prs->request->outcome & RS_NOTIFY && 577 !(prs->request->outcome & RS_NOTIFYING)) { 578 579 (prs->exec = ep)->ex.request = prs; 580 if (exec(EX_NOTIFY, prs) != 0) { 581 ep->ex.request = 0; 582 prs->exec = 0; 583 if (errno == EAGAIN) { 584 schedule (EV_LATER, WHEN_FORK, EV_NOTIFY, prs); 585 return (-1); 586 } 587 } 588 } 589 return (0); 590 } 591 592 593 /* 594 * find_exec_slot() - FIND AVAILABLE EXEC SLOT 595 */ 596 597 static EXEC * 598 find_exec_slot(EXEC *exec_table, int size) 599 { 600 register EXEC * ep; 601 register EXEC * last_ep = exec_table + size - 1; 602 603 for (ep = exec_table; ep <= last_ep; ep++) 604 if (ep->pid == 0) 605 return (ep); 606 607 return (0); 608 } 609