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