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