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