]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/subscriptions.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / subscriptions.c
1 /*
2 * "$Id: subscriptions.c 4993 2006-01-26 19:27:40Z mike $"
3 *
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * cupsdAddEvent() - Add an event to the global event cache.
27 * cupsdAddSubscription() - Add a new subscription object.
28 * cupsdDeleteAllEvents() - Delete all cached events.
29 * cupsdDeleteAllSubscriptions() - Delete all subscriptions.
30 * cupsdDeleteSubscription() - Delete a subscription object.
31 * cupsdEventName() - Return a single event name.
32 * cupsdEventValue() - Return the event mask value for a name.
33 * cupsdExpireSubscriptions() - Expire old subscription objects.
34 * cupsdFindSubscription() - Find a subscription by ID.
35 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
36 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
37 * cupsdSendNotification() - Send a notification for the specified event.
38 * cupsdStopAllNotifiers() - Stop all notifier processes.
39 * cupsdUpdateNotifierStatus() - Read messages from notifiers.
40 * cupsd_compare_subscriptions() - Compare two subscriptions.
41 * cupsd_delete_event() - Delete a single event...
42 * cupsd_start_notifier() - Start a notifier subprocess...
43 */
44
45 /*
46 * Include necessary headers...
47 */
48
49 #include "cupsd.h"
50
51
52 /*
53 * Local functions...
54 */
55
56 static int cupsd_compare_subscriptions(cupsd_subscription_t *first,
57 cupsd_subscription_t *second,
58 void *unused);
59 static void cupsd_delete_event(cupsd_event_t *event);
60 static void cupsd_start_notifier(cupsd_subscription_t *sub);
61
62
63 /*
64 * 'cupsdAddEvent()' - Add an event to the global event cache.
65 */
66
67 void
68 cupsdAddEvent(
69 cupsd_eventmask_t event, /* I - Event */
70 cupsd_printer_t *dest, /* I - Printer associated with event */
71 cupsd_job_t *job, /* I - Job associated with event */
72 const char *text, /* I - Notification text */
73 ...) /* I - Additional arguments as needed */
74 {
75 va_list ap; /* Pointer to additional arguments */
76 char ftext[1024]; /* Formatted text buffer */
77 ipp_attribute_t *attr; /* Printer/job attribute */
78 cupsd_event_t *temp; /* New event pointer */
79 cupsd_subscription_t *sub; /* Current subscription */
80
81
82 LastEvent |= event;
83
84 /*
85 * Return if we aren't keeping events...
86 */
87
88 if (MaxEvents <= 0)
89 {
90 cupsdLogMessage(CUPSD_LOG_WARN,
91 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
92 cupsdEventName(event), MaxEvents);
93 return;
94 }
95
96 /*
97 * Allocate memory for the event cache as needed...
98 */
99
100 if (!Events)
101 {
102 Events = calloc(MaxEvents, sizeof(cupsd_event_t *));
103 NumEvents = 0;
104
105 if (!Events)
106 {
107 cupsdLogMessage(CUPSD_LOG_CRIT,
108 "Unable to allocate memory for event cache - %s",
109 strerror(errno));
110 return;
111 }
112 }
113
114 /*
115 * Then loop through the subscriptions and add the event to the corresponding
116 * caches...
117 */
118
119 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), temp = NULL;
120 sub;
121 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
122 {
123 /*
124 * Check if this subscription requires this event...
125 */
126
127 if ((sub->mask & event) != 0 &&
128 (sub->dest == dest || !sub->dest) &&
129 (sub->job == job || !sub->job))
130 {
131 /*
132 * Need this event...
133 */
134
135 if (!temp)
136 {
137 /*
138 * Create the new event record...
139 */
140
141 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
142 {
143 cupsdLogMessage(CUPSD_LOG_CRIT,
144 "Unable to allocate memory for event - %s",
145 strerror(errno));
146 return;
147 }
148
149 temp->event = event;
150 temp->time = time(NULL);
151 temp->attrs = ippNew();
152 temp->job = job;
153 temp->dest = dest;
154
155 /*
156 * Add common event notification attributes...
157 */
158
159 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
160 "notify-subscription-id", sub->id);
161
162 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
163 "notify-subscribed-event", NULL, cupsdEventName(event));
164
165 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
166 "printer-up-time", time(NULL));
167
168 va_start(ap, text);
169 vsnprintf(ftext, sizeof(ftext), text, ap);
170 va_end(ap);
171
172 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
173 "notify-text", NULL, ftext);
174
175 if (dest)
176 {
177 /*
178 * Add printer attributes...
179 */
180
181 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
182 "notify-printer-uri", NULL, dest->uri);
183
184 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
185 "printer-name", NULL, dest->name);
186
187 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
188 "printer-state", dest->state);
189
190 if (dest->num_reasons == 0)
191 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
192 IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
193 dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
194 else
195 ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
196 IPP_TAG_KEYWORD, "printer-state-reasons",
197 dest->num_reasons, NULL,
198 (const char * const *)dest->reasons);
199
200 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
201 "printer-is-accepting-jobs", dest->accepting);
202 }
203
204 if (job)
205 {
206 /*
207 * Add job attributes...
208 */
209
210 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
211 "notify-job-id", job->id);
212 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
213 "job-state", (int)job->state);
214
215 if ((attr = ippFindAttribute(job->attrs, "job-name",
216 IPP_TAG_NAME)) != NULL)
217 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
218 "job-name", NULL, attr->values[0].string.text);
219
220 switch (job->state->values[0].integer)
221 {
222 case IPP_JOB_PENDING :
223 if (dest && dest->state == IPP_PRINTER_STOPPED)
224 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
225 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
226 "printer-stopped");
227 else
228 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
229 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
230 "none");
231 break;
232
233 case IPP_JOB_HELD :
234 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
235 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
236 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
237 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
238 "job-hold-until-specified");
239 else
240 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
241 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
242 "job-incoming");
243 break;
244
245 case IPP_JOB_PROCESSING :
246 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
247 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
248 "job-printing");
249 break;
250
251 case IPP_JOB_STOPPED :
252 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
253 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
254 "job-stopped");
255 break;
256
257 case IPP_JOB_CANCELLED :
258 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
259 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
260 "job-canceled-by-user");
261 break;
262
263 case IPP_JOB_ABORTED :
264 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
265 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
266 "aborted-by-system");
267 break;
268
269 case IPP_JOB_COMPLETED :
270 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
271 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
272 "job-completed-successfully");
273 break;
274 }
275
276 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
277 "job-impressions-completed",
278 job->sheets ? job->sheets->values[0].integer : 0);
279 }
280
281 /*
282 * Purge an old event as needed...
283 */
284
285 if (NumEvents >= MaxEvents)
286 {
287 /*
288 * Purge the oldest event in the cache...
289 */
290
291 cupsd_delete_event(Events[0]);
292
293 NumEvents --;
294
295 memmove(Events, Events + 1, NumEvents * sizeof(cupsd_event_t *));
296 }
297
298 /*
299 * Add the new event to the main cache...
300 */
301
302 Events[NumEvents] = temp;
303 NumEvents ++;
304 }
305
306 /*
307 * Send the notification for this subscription...
308 */
309
310 cupsdSendNotification(sub, temp);
311 }
312 }
313
314 if (temp)
315 cupsdSaveAllSubscriptions();
316 else
317 cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...",
318 cupsdEventName(event));
319 }
320
321
322 /*
323 * 'cupsdAddSubscription()' - Add a new subscription object.
324 */
325
326 cupsd_subscription_t * /* O - New subscription object */
327 cupsdAddSubscription(
328 unsigned mask, /* I - Event mask */
329 cupsd_printer_t *dest, /* I - Printer, if any */
330 cupsd_job_t *job, /* I - Job, if any */
331 const char *uri, /* I - notify-recipient-uri, if any */
332 int sub_id) /* I - notify-subscription-id or 0 */
333 {
334 cupsd_subscription_t *temp; /* New subscription object */
335
336
337 cupsdLogMessage(CUPSD_LOG_DEBUG,
338 "cupsdAddSubscription(mask=%x(%s), dest=%p(%s), job=%p(%d), uri=\"%s\")",
339 mask, cupsdEventName(mask), dest, dest ? dest->name : "",
340 job, job ? job->id : 0, uri);
341
342 if (!Subscriptions)
343 Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
344 NULL);
345
346 if (!Subscriptions)
347 {
348 cupsdLogMessage(CUPSD_LOG_CRIT,
349 "Unable to allocate memory for subscriptions - %s",
350 strerror(errno));
351 return (NULL);
352 }
353
354 /*
355 * Limit the number of subscriptions...
356 */
357
358 if (cupsArrayCount(Subscriptions) >= MaxSubscriptions)
359 return (NULL);
360
361 /*
362 * Allocate memory for this subscription...
363 */
364
365 if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
366 {
367 cupsdLogMessage(CUPSD_LOG_CRIT,
368 "Unable to allocate memory for subscription object - %s",
369 strerror(errno));
370 return (NULL);
371 }
372
373 /*
374 * Fill in common data...
375 */
376
377 if (sub_id)
378 {
379 temp->id = sub_id;
380
381 if (sub_id >= NextSubscriptionId)
382 NextSubscriptionId = sub_id + 1;
383 }
384 else
385 {
386 temp->id = NextSubscriptionId;
387
388 NextSubscriptionId ++;
389 }
390
391 temp->mask = mask;
392 temp->dest = dest;
393 temp->job = job;
394 temp->pipe = -1;
395 temp->first_event_id = 1;
396 temp->next_event_id = 1;
397
398 cupsdSetString(&(temp->recipient), uri);
399
400 /*
401 * Add the subscription to the array...
402 */
403
404 cupsArrayAdd(Subscriptions, temp);
405
406 return (temp);
407 }
408
409
410 /*
411 * 'cupsdDeleteAllEvents()' - Delete all cached events.
412 */
413
414 void
415 cupsdDeleteAllEvents(void)
416 {
417 int i; /* Looping var */
418
419
420 if (MaxEvents <= 0 || !Events)
421 return;
422
423 for (i = 0; i < NumEvents; i ++)
424 cupsd_delete_event(Events[i]);
425
426 free(Events);
427 Events = NULL;
428 }
429
430
431 /*
432 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
433 */
434
435 void
436 cupsdDeleteAllSubscriptions(void)
437 {
438 cupsd_subscription_t *sub; /* Subscription */
439
440
441 if (!Subscriptions)
442 return;
443
444 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
445 sub;
446 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
447 cupsdDeleteSubscription(sub, 0);
448
449 cupsArrayDelete(Subscriptions);
450 Subscriptions = NULL;
451 }
452
453
454 /*
455 * 'cupsdDeleteSubscription()' - Delete a subscription object.
456 */
457
458 void
459 cupsdDeleteSubscription(
460 cupsd_subscription_t *sub, /* I - Subscription object */
461 int update) /* I - 1 = update subscriptions.conf */
462 {
463 /*
464 * Close the pipe to the notifier as needed...
465 */
466
467 if (sub->pipe >= 0)
468 close(sub->pipe);
469
470 /*
471 * Remove subscription from array...
472 */
473
474 cupsArrayRemove(Subscriptions, sub);
475
476 /*
477 * Free memory...
478 */
479
480 cupsdClearString(&(sub->owner));
481 cupsdClearString(&(sub->recipient));
482
483 if (sub->events)
484 free(sub->events);
485
486 free(sub);
487
488 /*
489 * Update the subscriptions as needed...
490 */
491
492 if (update)
493 cupsdSaveAllSubscriptions();
494 }
495
496
497 /*
498 * 'cupsdEventName()' - Return a single event name.
499 */
500
501 const char * /* O - Event name */
502 cupsdEventName(
503 cupsd_eventmask_t event) /* I - Event value */
504 {
505 switch (event)
506 {
507 default :
508 return (NULL);
509
510 case CUPSD_EVENT_PRINTER_RESTARTED :
511 return ("printer-restarted");
512
513 case CUPSD_EVENT_PRINTER_SHUTDOWN :
514 return ("printer-shutdown");
515
516 case CUPSD_EVENT_PRINTER_STOPPED :
517 return ("printer-stopped");
518
519 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
520 return ("printer-finishings-changed");
521
522 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
523 return ("printer-media-changed");
524
525 case CUPSD_EVENT_PRINTER_ADDED :
526 return ("printer-added");
527
528 case CUPSD_EVENT_PRINTER_DELETED :
529 return ("printer-deleted");
530
531 case CUPSD_EVENT_PRINTER_MODIFIED :
532 return ("printer-modified");
533
534 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
535 return ("printer-state-changed");
536
537 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
538 return ("printer-config-changed");
539
540 case CUPSD_EVENT_PRINTER_CHANGED :
541 return ("printer-changed");
542
543 case CUPSD_EVENT_JOB_CREATED :
544 return ("job-created");
545
546 case CUPSD_EVENT_JOB_COMPLETED :
547 return ("job-completed");
548
549 case CUPSD_EVENT_JOB_STOPPED :
550 return ("job-stopped");
551
552 case CUPSD_EVENT_JOB_CONFIG_CHANGED :
553 return ("job-config-changed");
554
555 case CUPSD_EVENT_JOB_PROGRESS :
556 return ("job-progress");
557
558 case CUPSD_EVENT_JOB_STATE_CHANGED :
559 return ("job-state-changed");
560
561 case CUPSD_EVENT_SERVER_RESTARTED :
562 return ("server-restarted");
563
564 case CUPSD_EVENT_SERVER_STARTED :
565 return ("server-started");
566
567 case CUPSD_EVENT_SERVER_STOPPED :
568 return ("server-stopped");
569
570 case CUPSD_EVENT_SERVER_AUDIT :
571 return ("server-audit");
572
573 case CUPSD_EVENT_ALL :
574 return ("all");
575 }
576 }
577
578
579 /*
580 * 'cupsdEventValue()' - Return the event mask value for a name.
581 */
582
583 cupsd_eventmask_t /* O - Event mask value */
584 cupsdEventValue(const char *name) /* I - Name of event */
585 {
586 if (!strcmp(name, "all"))
587 return (CUPSD_EVENT_ALL);
588 else if (!strcmp(name, "printer-restarted"))
589 return (CUPSD_EVENT_PRINTER_RESTARTED);
590 else if (!strcmp(name, "printer-shutdown"))
591 return (CUPSD_EVENT_PRINTER_SHUTDOWN);
592 else if (!strcmp(name, "printer-stopped"))
593 return (CUPSD_EVENT_PRINTER_STOPPED);
594 else if (!strcmp(name, "printer-finishings-changed"))
595 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
596 else if (!strcmp(name, "printer-media-changed"))
597 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
598 else if (!strcmp(name, "printer-added"))
599 return (CUPSD_EVENT_PRINTER_ADDED);
600 else if (!strcmp(name, "printer-deleted"))
601 return (CUPSD_EVENT_PRINTER_DELETED);
602 else if (!strcmp(name, "printer-modified"))
603 return (CUPSD_EVENT_PRINTER_MODIFIED);
604 else if (!strcmp(name, "printer-state-changed"))
605 return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
606 else if (!strcmp(name, "printer-config-changed"))
607 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
608 else if (!strcmp(name, "printer-changed"))
609 return (CUPSD_EVENT_PRINTER_CHANGED);
610 else if (!strcmp(name, "job-created"))
611 return (CUPSD_EVENT_JOB_CREATED);
612 else if (!strcmp(name, "job-completed"))
613 return (CUPSD_EVENT_JOB_COMPLETED);
614 else if (!strcmp(name, "job-stopped"))
615 return (CUPSD_EVENT_JOB_STOPPED);
616 else if (!strcmp(name, "job-config-changed"))
617 return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
618 else if (!strcmp(name, "job-progress"))
619 return (CUPSD_EVENT_JOB_PROGRESS);
620 else if (!strcmp(name, "job-state-changed"))
621 return (CUPSD_EVENT_JOB_STATE_CHANGED);
622 else if (!strcmp(name, "server-restarted"))
623 return (CUPSD_EVENT_SERVER_RESTARTED);
624 else if (!strcmp(name, "server-started"))
625 return (CUPSD_EVENT_SERVER_STARTED);
626 else if (!strcmp(name, "server-stopped"))
627 return (CUPSD_EVENT_SERVER_STOPPED);
628 else if (!strcmp(name, "server-audit"))
629 return (CUPSD_EVENT_SERVER_AUDIT);
630 else
631 return (CUPSD_EVENT_NONE);
632 }
633
634
635 /*
636 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
637 */
638
639 void
640 cupsdExpireSubscriptions(
641 cupsd_printer_t *dest, /* I - Printer, if any */
642 cupsd_job_t *job) /* I - Job, if any */
643 {
644 cupsd_subscription_t *sub; /* Current subscription */
645 int update; /* Update subscriptions.conf? */
646 time_t curtime; /* Current time */
647
648
649 curtime = time(NULL);
650 update = 0;
651
652 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
653 sub;
654 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
655 if ((sub->expire <= curtime && dest && sub->dest == dest) ||
656 (job && sub->job == job))
657 {
658 cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...", sub->id);
659
660 cupsdDeleteSubscription(sub, 0);
661
662 update = 1;
663 }
664
665 if (update)
666 cupsdSaveAllSubscriptions();
667 }
668
669
670 /*
671 * 'cupsdFindSubscription()' - Find a subscription by ID.
672 */
673
674 cupsd_subscription_t * /* O - Subscription object */
675 cupsdFindSubscription(int id) /* I - Subscription ID */
676 {
677 cupsd_subscription_t sub; /* Subscription template */
678
679
680 sub.id = id;
681
682 return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
683 }
684
685
686 /*
687 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
688 */
689
690 void
691 cupsdLoadAllSubscriptions(void)
692 {
693 int i; /* Looping var */
694 cups_file_t *fp; /* subscriptions.conf file */
695 int linenum; /* Current line number */
696 char line[1024], /* Line from file */
697 *value, /* Pointer to value */
698 *valueptr; /* Pointer into value */
699 cupsd_subscription_t *sub; /* Current subscription */
700 int hex; /* Non-zero if reading hex data */
701 int delete_sub; /* Delete subscription? */
702
703
704 /*
705 * Open the subscriptions.conf file...
706 */
707
708 snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
709 if ((fp = cupsFileOpen(line, "r")) == NULL)
710 {
711 if (errno != ENOENT)
712 cupsdLogMessage(CUPSD_LOG_ERROR,
713 "LoadAllSubscriptions: Unable to open %s - %s", line,
714 strerror(errno));
715 return;
716 }
717
718 /*
719 * Read all of the lines from the file...
720 */
721
722 linenum = 0;
723 sub = NULL;
724 delete_sub = 0;
725
726 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
727 {
728 if (!strcasecmp(line, "<Subscription"))
729 {
730 /*
731 * <Subscription #>
732 */
733
734 if (!sub && value && isdigit(value[0] & 255))
735 {
736 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
737 atoi(value));
738 }
739 else
740 {
741 cupsdLogMessage(CUPSD_LOG_ERROR,
742 "Syntax error on line %d of subscriptions.conf.",
743 linenum);
744 return;
745 }
746 }
747 else if (!strcasecmp(line, "</Subscription>"))
748 {
749 if (!sub)
750 {
751 cupsdLogMessage(CUPSD_LOG_ERROR,
752 "Syntax error on line %d of subscriptions.conf.",
753 linenum);
754 return;
755 }
756
757 if (delete_sub)
758 cupsdDeleteSubscription(sub, 0);
759
760 sub = NULL;
761 delete_sub = 0;
762 }
763 else if (!sub)
764 {
765 cupsdLogMessage(CUPSD_LOG_ERROR,
766 "Syntax error on line %d of subscriptions.conf.",
767 linenum);
768 return;
769 }
770 else if (!strcasecmp(line, "Events"))
771 {
772 /*
773 * Events name
774 * Events name name name ...
775 */
776
777 if (!value)
778 {
779 cupsdLogMessage(CUPSD_LOG_ERROR,
780 "Syntax error on line %d of subscriptions.conf.",
781 linenum);
782 return;
783 }
784
785 while (*value)
786 {
787 /*
788 * Separate event names...
789 */
790
791 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
792
793 while (isspace(*valueptr & 255))
794 *valueptr++ = '\0';
795
796 /*
797 * See if the name exists...
798 */
799
800 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
801 {
802 cupsdLogMessage(CUPSD_LOG_ERROR,
803 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
804 value, linenum);
805 return;
806 }
807
808 value = valueptr;
809 }
810 }
811 else if (!strcasecmp(line, "Owner"))
812 {
813 /*
814 * Owner
815 */
816
817 if (value)
818 cupsdSetString(&sub->owner, value);
819 else
820 {
821 cupsdLogMessage(CUPSD_LOG_ERROR,
822 "Syntax error on line %d of subscriptions.conf.",
823 linenum);
824 return;
825 }
826 }
827 else if (!strcasecmp(line, "Recipient"))
828 {
829 /*
830 * Recipient uri
831 */
832
833 if (value)
834 cupsdSetString(&sub->recipient, value);
835 else
836 {
837 cupsdLogMessage(CUPSD_LOG_ERROR,
838 "Syntax error on line %d of subscriptions.conf.",
839 linenum);
840 return;
841 }
842 }
843 else if (!strcasecmp(line, "JobId"))
844 {
845 /*
846 * JobId #
847 */
848
849 if (value && isdigit(*value & 255))
850 {
851 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
852 {
853 cupsdLogMessage(CUPSD_LOG_ERROR,
854 "Job %s not found on line %d of subscriptions.conf.",
855 value, linenum);
856 delete_sub = 1;
857 }
858 }
859 else
860 {
861 cupsdLogMessage(CUPSD_LOG_ERROR,
862 "Syntax error on line %d of subscriptions.conf.",
863 linenum);
864 return;
865 }
866 }
867 else if (!strcasecmp(line, "PrinterName"))
868 {
869 /*
870 * PrinterName name
871 */
872
873 if (value)
874 {
875 if ((sub->dest = cupsdFindDest(value)) == NULL)
876 {
877 cupsdLogMessage(CUPSD_LOG_ERROR,
878 "Printer \'%s\' not found on line %d of subscriptions.conf.",
879 value, linenum);
880 delete_sub = 1;
881 }
882 }
883 else
884 {
885 cupsdLogMessage(CUPSD_LOG_ERROR,
886 "Syntax error on line %d of subscriptions.conf.",
887 linenum);
888 return;
889 }
890 }
891 else if (!strcasecmp(line, "UserData"))
892 {
893 /*
894 * UserData encoded-string
895 */
896
897 if (value)
898 {
899 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
900 {
901 if (*valueptr == '<' && !hex)
902 {
903 hex = 1;
904 valueptr ++;
905 }
906
907 if (hex)
908 {
909 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
910 {
911 if (isdigit(valueptr[0]))
912 sub->user_data[i] = (valueptr[0] - '0') << 4;
913 else
914 sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
915
916 if (isdigit(valueptr[1]))
917 sub->user_data[i] |= valueptr[1] - '0';
918 else
919 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
920
921 valueptr += 2;
922
923 if (*valueptr == '>')
924 {
925 hex = 0;
926 valueptr ++;
927 }
928 }
929 else
930 break;
931 }
932 else
933 sub->user_data[i] = *valueptr++;
934 }
935
936 if (*valueptr)
937 {
938 cupsdLogMessage(CUPSD_LOG_ERROR,
939 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
940 value, linenum);
941 }
942 else
943 sub->user_data_len = i;
944 }
945 else
946 {
947 cupsdLogMessage(CUPSD_LOG_ERROR,
948 "Syntax error on line %d of subscriptions.conf.",
949 linenum);
950 return;
951 }
952 }
953 else if (!strcasecmp(line, "LeaseDuration"))
954 {
955 /*
956 * LeaseDuration #
957 */
958
959 if (value && isdigit(*value & 255))
960 sub->lease = atoi(value);
961 else
962 {
963 cupsdLogMessage(CUPSD_LOG_ERROR,
964 "Syntax error on line %d of subscriptions.conf.",
965 linenum);
966 return;
967 }
968 }
969 else if (!strcasecmp(line, "Interval"))
970 {
971 /*
972 * Interval #
973 */
974
975 if (value && isdigit(*value & 255))
976 sub->interval = atoi(value);
977 else
978 {
979 cupsdLogMessage(CUPSD_LOG_ERROR,
980 "Syntax error on line %d of subscriptions.conf.",
981 linenum);
982 return;
983 }
984 }
985 else if (!strcasecmp(line, "ExpirationTime"))
986 {
987 /*
988 * ExpirationTime #
989 */
990
991 if (value && isdigit(*value & 255))
992 sub->expire = atoi(value);
993 else
994 {
995 cupsdLogMessage(CUPSD_LOG_ERROR,
996 "Syntax error on line %d of subscriptions.conf.",
997 linenum);
998 return;
999 }
1000 }
1001 else if (!strcasecmp(line, "NextEventId"))
1002 {
1003 /*
1004 * NextEventId #
1005 */
1006
1007 if (value && isdigit(*value & 255))
1008 sub->next_event_id = sub->first_event_id = atoi(value);
1009 else
1010 {
1011 cupsdLogMessage(CUPSD_LOG_ERROR,
1012 "Syntax error on line %d of subscriptions.conf.",
1013 linenum);
1014 return;
1015 }
1016 }
1017 else
1018 {
1019 /*
1020 * Something else we don't understand...
1021 */
1022
1023 cupsdLogMessage(CUPSD_LOG_ERROR,
1024 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1025 line, linenum);
1026 }
1027 }
1028
1029 cupsFileClose(fp);
1030 }
1031
1032
1033 /*
1034 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1035 */
1036
1037 void
1038 cupsdSaveAllSubscriptions(void)
1039 {
1040 int i; /* Looping var */
1041 cups_file_t *fp; /* subscriptions.conf file */
1042 char temp[1024]; /* Temporary string */
1043 char backup[1024]; /* subscriptions.conf.O file */
1044 cupsd_subscription_t *sub; /* Current subscription */
1045 time_t curtime; /* Current time */
1046 struct tm *curdate; /* Current date */
1047 unsigned mask; /* Current event mask */
1048 const char *name; /* Current event name */
1049 int hex; /* Non-zero if we are writing hex data */
1050
1051
1052 /*
1053 * Create the subscriptions.conf file...
1054 */
1055
1056 snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
1057 snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
1058
1059 if (rename(temp, backup))
1060 {
1061 if (errno != ENOENT)
1062 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
1063 strerror(errno));
1064 }
1065
1066 if ((fp = cupsFileOpen(temp, "w")) == NULL)
1067 {
1068 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
1069 strerror(errno));
1070
1071 if (rename(backup, temp))
1072 cupsdLogMessage(CUPSD_LOG_ERROR,
1073 "Unable to restore subscriptions.conf - %s",
1074 strerror(errno));
1075 return;
1076 }
1077 else
1078 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
1079
1080 /*
1081 * Restrict access to the file...
1082 */
1083
1084 fchown(cupsFileNumber(fp), getuid(), Group);
1085 fchmod(cupsFileNumber(fp), ConfigFilePerm);
1086
1087 /*
1088 * Write a small header to the file...
1089 */
1090
1091 curtime = time(NULL);
1092 curdate = localtime(&curtime);
1093 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1094
1095 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1096 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1097
1098 /*
1099 * Write every subscription known to the system...
1100 */
1101
1102 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1103 sub;
1104 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1105 {
1106 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1107
1108 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1109 {
1110 /*
1111 * Simple event list...
1112 */
1113
1114 cupsFilePrintf(fp, "Events %s\n", name);
1115 }
1116 else
1117 {
1118 /*
1119 * Complex event list...
1120 */
1121
1122 cupsFilePuts(fp, "Events");
1123
1124 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1125 if (sub->mask & mask)
1126 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1127
1128 cupsFilePuts(fp, "\n");
1129 }
1130
1131 if (sub->owner)
1132 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1133 if (sub->recipient)
1134 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1135 if (sub->job)
1136 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1137 if (sub->dest)
1138 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1139
1140 if (sub->user_data_len > 0)
1141 {
1142 cupsFilePuts(fp, "UserData ");
1143
1144 for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1145 {
1146 if (sub->user_data[i] < ' ' ||
1147 sub->user_data[i] > 0x7f ||
1148 sub->user_data[i] == '<')
1149 {
1150 if (!hex)
1151 {
1152 cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1153 hex = 1;
1154 }
1155 else
1156 cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1157 }
1158 else
1159 {
1160 if (hex)
1161 {
1162 cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1163 hex = 0;
1164 }
1165 else
1166 cupsFilePutChar(fp, sub->user_data[i]);
1167 }
1168 }
1169
1170 if (hex)
1171 cupsFilePuts(fp, ">\n");
1172 else
1173 cupsFilePutChar(fp, '\n');
1174 }
1175
1176 cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1177 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1178 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1179 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1180
1181 cupsFilePuts(fp, "</Subscription>\n");
1182 }
1183
1184 cupsFileClose(fp);
1185 }
1186
1187
1188 /*
1189 * 'cupsdSendNotification()' - Send a notification for the specified event.
1190 */
1191
1192 void
1193 cupsdSendNotification(
1194 cupsd_subscription_t *sub, /* I - Subscription object */
1195 cupsd_event_t *event) /* I - Event to send */
1196 {
1197 ipp_state_t state; /* IPP event state */
1198
1199
1200 cupsdLogMessage(CUPSD_LOG_DEBUG,
1201 "cupsdSendNotification(sub=%p(%d), event=%p(%s))\n",
1202 sub, sub->id, event, cupsdEventName(event->event));
1203
1204 /*
1205 * Allocate the events array as needed...
1206 */
1207
1208 if (!sub->events)
1209 {
1210 sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
1211
1212 if (!sub->events)
1213 {
1214 cupsdLogMessage(CUPSD_LOG_CRIT,
1215 "Unable to allocate memory for subscription #%d!",
1216 sub->id);
1217 return;
1218 }
1219 }
1220
1221 /*
1222 * Add the event to the subscription. Since the events array is
1223 * always MaxEvents in length, and since we will have already
1224 * removed an event from the subscription cache if we hit the
1225 * event cache limit, we don't need to check for overflow here...
1226 */
1227
1228 sub->events[sub->num_events] = event;
1229 sub->num_events ++;
1230
1231 /*
1232 * Deliver the event...
1233 */
1234
1235 if (sub->recipient)
1236 {
1237 if (sub->pipe < 0)
1238 cupsd_start_notifier(sub);
1239
1240 cupsdLogMessage(CUPSD_LOG_DEBUG, "sub->pipe=%d", sub->pipe);
1241
1242 if (sub->pipe >= 0)
1243 {
1244 event->attrs->state = IPP_IDLE;
1245
1246 while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
1247 if (state == IPP_ERROR)
1248 break;
1249
1250 if (state == IPP_ERROR)
1251 cupsdLogMessage(CUPSD_LOG_ERROR,
1252 "Unable to send event for subscription %d (%s)!",
1253 sub->id, sub->recipient);
1254 }
1255 }
1256
1257 /*
1258 * Bump the event sequence number...
1259 */
1260
1261 sub->next_event_id ++;
1262 }
1263
1264
1265 /*
1266 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1267 */
1268
1269 void
1270 cupsdStopAllNotifiers(void)
1271 {
1272 cupsd_subscription_t *sub; /* Current subscription */
1273
1274
1275 /*
1276 * See if we have started any notifiers...
1277 */
1278
1279 if (!NotifierStatusBuffer)
1280 return;
1281
1282 /*
1283 * Yes, kill any processes that are left...
1284 */
1285
1286 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1287 sub;
1288 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1289 if (sub->pid)
1290 {
1291 cupsdEndProcess(sub->pid, 0);
1292
1293 close(sub->pipe);
1294 sub->pipe = -1;
1295 }
1296
1297 /*
1298 * Close the status pipes...
1299 */
1300
1301 if (NotifierPipes[0] >= 0)
1302 {
1303 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1304 "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
1305 NotifierPipes[0]);
1306 FD_CLR(NotifierPipes[0], InputSet);
1307
1308 cupsdStatBufDelete(NotifierStatusBuffer);
1309
1310 close(NotifierPipes[0]);
1311 close(NotifierPipes[1]);
1312
1313 NotifierPipes[0] = -1;
1314 NotifierPipes[1] = -1;
1315 NotifierStatusBuffer = NULL;
1316 }
1317 }
1318
1319
1320 /*
1321 * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
1322 */
1323
1324 void
1325 cupsdUpdateNotifierStatus(void)
1326 {
1327 char *ptr, /* Pointer to end of line in buffer */
1328 message[1024]; /* Pointer to message text */
1329 int loglevel; /* Log level for message */
1330
1331
1332 while ((ptr = cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1333 message, sizeof(message))) != NULL)
1334 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1335 break;
1336 }
1337
1338
1339 /*
1340 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1341 */
1342
1343 static int /* O - Result of comparison */
1344 cupsd_compare_subscriptions(
1345 cupsd_subscription_t *first, /* I - First subscription object */
1346 cupsd_subscription_t *second, /* I - Second subscription object */
1347 void *unused) /* I - Unused user data pointer */
1348 {
1349 (void)unused;
1350
1351 return (first->id - second->id);
1352 }
1353
1354
1355 /*
1356 * 'cupsd_delete_event()' - Delete a single event...
1357 *
1358 * Oldest events must be deleted first, otherwise the subscription cache
1359 * flushing code will not work properly.
1360 */
1361
1362 static void
1363 cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1364 {
1365 cupsd_subscription_t *sub; /* Current subscription */
1366
1367
1368 /*
1369 * Loop through the subscriptions and look for the event in the cache...
1370 */
1371
1372 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1373 sub;
1374 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1375 {
1376 /*
1377 * Only check the first event in the subscription cache, since the
1378 * caller will only delete the oldest event in the cache...
1379 */
1380
1381 if (sub->num_events > 0 && sub->events[0] == event)
1382 {
1383 /*
1384 * Remove this event...
1385 */
1386
1387 sub->num_events --;
1388 sub->first_event_id ++;
1389
1390 if (sub->num_events > 0)
1391 {
1392 /*
1393 * Shift other events upward in cache...
1394 */
1395
1396 memmove(sub->events, sub->events + 1,
1397 sub->num_events * sizeof(cupsd_event_t *));
1398 }
1399 }
1400 }
1401
1402 /*
1403 * Free memory...
1404 */
1405
1406 ippDelete(event->attrs);
1407 free(event);
1408 }
1409
1410
1411 /*
1412 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1413 */
1414
1415 static void
1416 cupsd_start_notifier(
1417 cupsd_subscription_t *sub) /* I - Subscription object */
1418 {
1419 int pid; /* Notifier process ID */
1420 int fds[2]; /* Pipe file descriptors */
1421 int envc; /* Number of environment variables */
1422 char *argv[4], /* Command-line arguments */
1423 *envp[100], /* Environment variables */
1424 user_data[128], /* Base-64 encoded user data */
1425 scheme[256], /* notify-recipient-uri scheme */
1426 *ptr, /* Pointer into scheme */
1427 command[1024]; /* Notifier command */
1428
1429
1430 /*
1431 * Extract the scheme name from the recipient URI and point to the
1432 * notifier program...
1433 */
1434
1435 strlcpy(scheme, sub->recipient, sizeof(scheme));
1436 if ((ptr = strchr(scheme, ':')) != NULL)
1437 *ptr = '\0';
1438
1439 snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1440
1441 /*
1442 * Base-64 encode the user data...
1443 */
1444
1445 httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
1446 sub->user_data_len);
1447
1448 /*
1449 * Setup the argument array...
1450 */
1451
1452 argv[0] = command;
1453 argv[1] = sub->recipient;
1454 argv[2] = user_data;
1455 argv[3] = NULL;
1456
1457 /*
1458 * Setup the environment...
1459 */
1460
1461 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1462
1463 /*
1464 * Create pipes as needed...
1465 */
1466
1467 if (!NotifierStatusBuffer)
1468 {
1469 /*
1470 * Create the status pipe...
1471 */
1472
1473 if (cupsdOpenPipe(NotifierPipes))
1474 {
1475 cupsdLogMessage(CUPSD_LOG_ERROR,
1476 "Unable to create pipes for notifier status - %s",
1477 strerror(errno));
1478 return;
1479 }
1480
1481 NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1482
1483 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1484 "start_notifier: Adding fd %d to InputSet...",
1485 NotifierPipes[0]);
1486
1487 FD_SET(NotifierPipes[0], InputSet);
1488 }
1489
1490 if (cupsdOpenPipe(fds))
1491 {
1492 cupsdLogMessage(CUPSD_LOG_ERROR,
1493 "Unable to create pipes for notifier %s - %s",
1494 scheme, strerror(errno));
1495 return;
1496 }
1497
1498 /*
1499 * Make sure the delivery pipe is non-blocking...
1500 */
1501
1502 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1503
1504 /*
1505 * Create the notifier process...
1506 */
1507
1508 if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
1509 -1, 0, &pid) < 0)
1510 {
1511 /*
1512 * Error - can't fork!
1513 */
1514
1515 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
1516 scheme, strerror(errno));
1517
1518 cupsdClosePipe(fds);
1519 }
1520 else
1521 {
1522 /*
1523 * Fork successful - return the PID...
1524 */
1525
1526 cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
1527 scheme, pid);
1528
1529 sub->pid = pid;
1530 sub->pipe = fds[1];
1531 sub->status = 0;
1532
1533 close(fds[0]);
1534 }
1535 }
1536
1537
1538 /*
1539 * End of "$Id: subscriptions.c 4993 2006-01-26 19:27:40Z mike $".
1540 */