2 * "$Id: subscriptions.c 7824 2008-08-01 21:11:55Z mike $"
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * cupsdAddEvent() - Add an event to the global event cache.
18 * cupsdAddSubscription() - Add a new subscription object.
19 * cupsdDeleteAllSubscriptions() - Delete all subscriptions.
20 * cupsdDeleteSubscription() - Delete a subscription object.
21 * cupsdEventName() - Return a single event name.
22 * cupsdEventValue() - Return the event mask value for a name.
23 * cupsdExpireSubscriptions() - Expire old subscription objects.
24 * cupsdFindSubscription() - Find a subscription by ID.
25 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
26 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
27 * cupsdStopAllNotifiers() - Stop all notifier processes.
28 * cupsd_compare_subscriptions() - Compare two subscriptions.
29 * cupsd_delete_event() - Delete a single event...
30 * cupsd_send_dbus() - Send a DBUS notification...
31 * cupsd_send_notification() - Send a notification for the specified
33 * cupsd_start_notifier() - Start a notifier subprocess...
34 * cupsd_update_notifier() - Read messages from notifiers.
38 * Include necessary headers...
43 # include <dbus/dbus.h>
44 # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
45 # define dbus_message_append_iter_init dbus_message_iter_init_append
46 # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
47 # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
48 # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
49 #endif /* HAVE_DBUS */
56 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
57 cupsd_subscription_t
*second
,
59 static void cupsd_delete_event(cupsd_event_t
*event
);
61 static void cupsd_send_dbus(cupsd_eventmask_t event
, cupsd_printer_t
*dest
,
63 #endif /* HAVE_DBUS */
64 static void cupsd_send_notification(cupsd_subscription_t
*sub
,
65 cupsd_event_t
*event
);
66 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
67 static void cupsd_update_notifier(void);
71 * 'cupsdAddEvent()' - Add an event to the global event cache.
76 cupsd_eventmask_t event
, /* I - Event */
77 cupsd_printer_t
*dest
, /* I - Printer associated with event */
78 cupsd_job_t
*job
, /* I - Job associated with event */
79 const char *text
, /* I - Notification text */
80 ...) /* I - Additional arguments as needed */
82 va_list ap
; /* Pointer to additional arguments */
83 char ftext
[1024]; /* Formatted text buffer */
84 ipp_attribute_t
*attr
; /* Printer/job attribute */
85 cupsd_event_t
*temp
; /* New event pointer */
86 cupsd_subscription_t
*sub
; /* Current subscription */
89 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
90 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
91 cupsdEventName(event
), dest
, dest
? dest
->name
: "",
92 job
, job
? job
->id
: 0, text
);
95 * Keep track of events with any OS-supplied notification mechanisms...
101 cupsd_send_dbus(event
, dest
, job
);
102 #endif /* HAVE_DBUS */
105 * Return if we aren't keeping events...
110 cupsdLogMessage(CUPSD_LOG_WARN
,
111 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
112 cupsdEventName(event
), MaxEvents
);
117 * Then loop through the subscriptions and add the event to the corresponding
121 for (temp
= NULL
, sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
123 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
126 * Check if this subscription requires this event...
129 if ((sub
->mask
& event
) != 0 &&
130 (sub
->dest
== dest
|| !sub
->dest
) &&
131 (sub
->job
== job
|| !sub
->job
))
134 * Need this event, so create a new event record...
137 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
139 cupsdLogMessage(CUPSD_LOG_CRIT
,
140 "Unable to allocate memory for event - %s",
146 temp
->time
= time(NULL
);
147 temp
->attrs
= ippNew();
152 * Add common event notification attributes...
155 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
,
156 "notify-charset", NULL
, "utf-8");
158 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
,
159 "notify-natural-langugage", NULL
, "en-US");
161 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
162 "notify-subscription-id", sub
->id
);
164 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
165 "notify-sequence-number", sub
->next_event_id
);
167 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
168 "notify-subscribed-event", NULL
, cupsdEventName(event
));
170 if (sub
->user_data_len
> 0)
171 ippAddOctetString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
172 "notify-user-data", sub
->user_data
,
175 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
176 "printer-up-time", time(NULL
));
179 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
182 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
183 "notify-text", NULL
, ftext
);
188 * Add printer attributes...
191 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
,
192 "notify-printer-uri", NULL
, dest
->uri
);
194 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
195 "printer-name", NULL
, dest
->name
);
197 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
198 "printer-state", dest
->state
);
200 if (dest
->num_reasons
== 0)
201 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
202 IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
,
203 dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
205 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
206 IPP_TAG_KEYWORD
, "printer-state-reasons",
207 dest
->num_reasons
, NULL
,
208 (const char * const *)dest
->reasons
);
210 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
211 "printer-is-accepting-jobs", dest
->accepting
);
217 * Add job attributes...
220 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
221 "notify-job-id", job
->id
);
222 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
223 "job-state", job
->state_value
);
225 if ((attr
= ippFindAttribute(job
->attrs
, "job-name",
226 IPP_TAG_NAME
)) != NULL
)
227 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
228 "job-name", NULL
, attr
->values
[0].string
.text
);
230 switch (job
->state_value
)
232 case IPP_JOB_PENDING
:
233 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
234 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
235 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
238 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
239 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
244 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
245 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
246 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
247 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
248 "job-hold-until-specified");
250 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
251 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
255 case IPP_JOB_PROCESSING
:
256 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
257 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
261 case IPP_JOB_STOPPED
:
262 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
263 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
267 case IPP_JOB_CANCELED
:
268 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
269 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
270 "job-canceled-by-user");
273 case IPP_JOB_ABORTED
:
274 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
275 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
276 "aborted-by-system");
279 case IPP_JOB_COMPLETED
:
280 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
281 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
282 "job-completed-successfully");
286 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
287 "job-impressions-completed",
288 job
->sheets
? job
->sheets
->values
[0].integer
: 0);
292 * Send the notification for this subscription...
295 cupsd_send_notification(sub
, temp
);
300 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
302 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...",
303 cupsdEventName(event
));
308 * 'cupsdAddSubscription()' - Add a new subscription object.
311 cupsd_subscription_t
* /* O - New subscription object */
312 cupsdAddSubscription(
313 unsigned mask
, /* I - Event mask */
314 cupsd_printer_t
*dest
, /* I - Printer, if any */
315 cupsd_job_t
*job
, /* I - Job, if any */
316 const char *uri
, /* I - notify-recipient-uri, if any */
317 int sub_id
) /* I - notify-subscription-id or 0 */
319 cupsd_subscription_t
*temp
; /* New subscription object */
322 cupsdLogMessage(CUPSD_LOG_DEBUG
,
323 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
325 mask
, dest
, dest
? dest
->name
: "", job
, job
? job
->id
: 0,
326 uri
? uri
: "(null)");
329 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
334 cupsdLogMessage(CUPSD_LOG_CRIT
,
335 "Unable to allocate memory for subscriptions - %s",
341 * Limit the number of subscriptions...
344 if (MaxSubscriptions
> 0 && cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
346 cupsdLogMessage(CUPSD_LOG_DEBUG
,
347 "cupsdAddSubscription: Reached MaxSubscriptions %d "
348 "(count=%d)", MaxSubscriptions
,
349 cupsArrayCount(Subscriptions
));
353 if (MaxSubscriptionsPerJob
> 0 && job
)
355 int count
; /* Number of job subscriptions */
357 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
360 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
361 if (temp
->job
== job
)
364 if (count
>= MaxSubscriptionsPerJob
)
366 cupsdLogMessage(CUPSD_LOG_DEBUG
,
367 "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
368 "for job #%d (count=%d)", MaxSubscriptionsPerJob
,
374 if (MaxSubscriptionsPerPrinter
> 0 && dest
)
376 int count
; /* Number of printer subscriptions */
378 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
381 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
382 if (temp
->dest
== dest
)
385 if (count
>= MaxSubscriptionsPerPrinter
)
387 cupsdLogMessage(CUPSD_LOG_DEBUG
,
388 "cupsdAddSubscription: Reached "
389 "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
390 MaxSubscriptionsPerPrinter
, dest
->name
, count
);
396 * Allocate memory for this subscription...
399 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
401 cupsdLogMessage(CUPSD_LOG_CRIT
,
402 "Unable to allocate memory for subscription object - %s",
408 * Fill in common data...
415 if (sub_id
>= NextSubscriptionId
)
416 NextSubscriptionId
= sub_id
+ 1;
420 temp
->id
= NextSubscriptionId
;
422 NextSubscriptionId
++;
429 temp
->first_event_id
= 1;
430 temp
->next_event_id
= 1;
432 cupsdSetString(&(temp
->recipient
), uri
);
435 * Add the subscription to the array...
438 cupsArrayAdd(Subscriptions
, temp
);
441 * For RSS subscriptions, run the notifier immediately...
444 if (uri
&& !strncmp(uri
, "rss:", 4))
445 cupsd_start_notifier(temp
);
452 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
456 cupsdDeleteAllSubscriptions(void)
458 cupsd_subscription_t
*sub
; /* Subscription */
464 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
466 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
467 cupsdDeleteSubscription(sub
, 0);
469 cupsArrayDelete(Subscriptions
);
470 Subscriptions
= NULL
;
475 * 'cupsdDeleteSubscription()' - Delete a subscription object.
479 cupsdDeleteSubscription(
480 cupsd_subscription_t
*sub
, /* I - Subscription object */
481 int update
) /* I - 1 = update subscriptions.conf */
483 int i
; /* Looping var */
487 * Close the pipe to the notifier as needed...
494 * Remove subscription from array...
497 cupsArrayRemove(Subscriptions
, sub
);
503 cupsdClearString(&(sub
->owner
));
504 cupsdClearString(&(sub
->recipient
));
508 for (i
= 0; i
< sub
->num_events
; i
++)
509 cupsd_delete_event(sub
->events
[i
]);
517 * Update the subscriptions as needed...
521 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
526 * 'cupsdEventName()' - Return a single event name.
529 const char * /* O - Event name */
531 cupsd_eventmask_t event
) /* I - Event value */
538 case CUPSD_EVENT_PRINTER_RESTARTED
:
539 return ("printer-restarted");
541 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
542 return ("printer-shutdown");
544 case CUPSD_EVENT_PRINTER_STOPPED
:
545 return ("printer-stopped");
547 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
548 return ("printer-finishings-changed");
550 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
551 return ("printer-media-changed");
553 case CUPSD_EVENT_PRINTER_ADDED
:
554 return ("printer-added");
556 case CUPSD_EVENT_PRINTER_DELETED
:
557 return ("printer-deleted");
559 case CUPSD_EVENT_PRINTER_MODIFIED
:
560 return ("printer-modified");
562 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
:
563 return ("printer-queue-order-changed");
565 case CUPSD_EVENT_PRINTER_STATE
:
566 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
567 return ("printer-state-changed");
569 case CUPSD_EVENT_PRINTER_CONFIG
:
570 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
571 return ("printer-config-changed");
573 case CUPSD_EVENT_PRINTER_CHANGED
:
574 return ("printer-changed");
576 case CUPSD_EVENT_JOB_CREATED
:
577 return ("job-created");
579 case CUPSD_EVENT_JOB_COMPLETED
:
580 return ("job-completed");
582 case CUPSD_EVENT_JOB_STOPPED
:
583 return ("job-stopped");
585 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
586 return ("job-config-changed");
588 case CUPSD_EVENT_JOB_PROGRESS
:
589 return ("job-progress");
591 case CUPSD_EVENT_JOB_STATE
:
592 case CUPSD_EVENT_JOB_STATE_CHANGED
:
593 return ("job-state-changed");
595 case CUPSD_EVENT_SERVER_RESTARTED
:
596 return ("server-restarted");
598 case CUPSD_EVENT_SERVER_STARTED
:
599 return ("server-started");
601 case CUPSD_EVENT_SERVER_STOPPED
:
602 return ("server-stopped");
604 case CUPSD_EVENT_SERVER_AUDIT
:
605 return ("server-audit");
607 case CUPSD_EVENT_ALL
:
614 * 'cupsdEventValue()' - Return the event mask value for a name.
617 cupsd_eventmask_t
/* O - Event mask value */
618 cupsdEventValue(const char *name
) /* I - Name of event */
620 if (!strcmp(name
, "all"))
621 return (CUPSD_EVENT_ALL
);
622 else if (!strcmp(name
, "printer-restarted"))
623 return (CUPSD_EVENT_PRINTER_RESTARTED
);
624 else if (!strcmp(name
, "printer-shutdown"))
625 return (CUPSD_EVENT_PRINTER_SHUTDOWN
);
626 else if (!strcmp(name
, "printer-stopped"))
627 return (CUPSD_EVENT_PRINTER_STOPPED
);
628 else if (!strcmp(name
, "printer-finishings-changed"))
629 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
);
630 else if (!strcmp(name
, "printer-media-changed"))
631 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED
);
632 else if (!strcmp(name
, "printer-added"))
633 return (CUPSD_EVENT_PRINTER_ADDED
);
634 else if (!strcmp(name
, "printer-deleted"))
635 return (CUPSD_EVENT_PRINTER_DELETED
);
636 else if (!strcmp(name
, "printer-modified"))
637 return (CUPSD_EVENT_PRINTER_MODIFIED
);
638 else if (!strcmp(name
, "printer-queue-order-changed"))
639 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
);
640 else if (!strcmp(name
, "printer-state-changed"))
641 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
642 else if (!strcmp(name
, "printer-config-changed"))
643 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
644 else if (!strcmp(name
, "printer-changed"))
645 return (CUPSD_EVENT_PRINTER_CHANGED
);
646 else if (!strcmp(name
, "job-created"))
647 return (CUPSD_EVENT_JOB_CREATED
);
648 else if (!strcmp(name
, "job-completed"))
649 return (CUPSD_EVENT_JOB_COMPLETED
);
650 else if (!strcmp(name
, "job-stopped"))
651 return (CUPSD_EVENT_JOB_STOPPED
);
652 else if (!strcmp(name
, "job-config-changed"))
653 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
654 else if (!strcmp(name
, "job-progress"))
655 return (CUPSD_EVENT_JOB_PROGRESS
);
656 else if (!strcmp(name
, "job-state-changed"))
657 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
658 else if (!strcmp(name
, "server-restarted"))
659 return (CUPSD_EVENT_SERVER_RESTARTED
);
660 else if (!strcmp(name
, "server-started"))
661 return (CUPSD_EVENT_SERVER_STARTED
);
662 else if (!strcmp(name
, "server-stopped"))
663 return (CUPSD_EVENT_SERVER_STOPPED
);
664 else if (!strcmp(name
, "server-audit"))
665 return (CUPSD_EVENT_SERVER_AUDIT
);
667 return (CUPSD_EVENT_NONE
);
672 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
676 cupsdExpireSubscriptions(
677 cupsd_printer_t
*dest
, /* I - Printer, if any */
678 cupsd_job_t
*job
) /* I - Job, if any */
680 cupsd_subscription_t
*sub
; /* Current subscription */
681 int update
; /* Update subscriptions.conf? */
682 time_t curtime
; /* Current time */
685 curtime
= time(NULL
);
688 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
690 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
691 if ((!sub
->job
&& !dest
&& sub
->expire
&& sub
->expire
<= curtime
) ||
692 (dest
&& sub
->dest
== dest
) ||
693 (job
&& sub
->job
== job
))
695 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...",
698 cupsdDeleteSubscription(sub
, 0);
704 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
709 * 'cupsdFindSubscription()' - Find a subscription by ID.
712 cupsd_subscription_t
* /* O - Subscription object */
713 cupsdFindSubscription(int id
) /* I - Subscription ID */
715 cupsd_subscription_t sub
; /* Subscription template */
720 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
725 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
729 cupsdLoadAllSubscriptions(void)
731 int i
; /* Looping var */
732 cups_file_t
*fp
; /* subscriptions.conf file */
733 int linenum
; /* Current line number */
734 char line
[1024], /* Line from file */
735 *value
, /* Pointer to value */
736 *valueptr
; /* Pointer into value */
737 cupsd_subscription_t
*sub
; /* Current subscription */
738 int hex
; /* Non-zero if reading hex data */
739 int delete_sub
; /* Delete subscription? */
743 * Open the subscriptions.conf file...
746 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
747 if ((fp
= cupsFileOpen(line
, "r")) == NULL
)
750 cupsdLogMessage(CUPSD_LOG_ERROR
,
751 "LoadAllSubscriptions: Unable to open %s - %s", line
,
757 * Read all of the lines from the file...
764 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
766 if (!strcasecmp(line
, "NextSubscriptionId") && value
)
769 * NextSubscriptionId NNN
773 if (i
>= NextSubscriptionId
&& i
> 0)
774 NextSubscriptionId
= i
;
776 else if (!strcasecmp(line
, "<Subscription"))
782 if (!sub
&& value
&& isdigit(value
[0] & 255))
784 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
789 cupsdLogMessage(CUPSD_LOG_ERROR
,
790 "Syntax error on line %d of subscriptions.conf.",
795 else if (!strcasecmp(line
, "</Subscription>"))
799 cupsdLogMessage(CUPSD_LOG_ERROR
,
800 "Syntax error on line %d of subscriptions.conf.",
806 cupsdDeleteSubscription(sub
, 0);
813 cupsdLogMessage(CUPSD_LOG_ERROR
,
814 "Syntax error on line %d of subscriptions.conf.",
817 else if (!strcasecmp(line
, "Events"))
821 * Events name name name ...
826 cupsdLogMessage(CUPSD_LOG_ERROR
,
827 "Syntax error on line %d of subscriptions.conf.",
835 * Separate event names...
838 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
840 while (isspace(*valueptr
& 255))
844 * See if the name exists...
847 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
849 cupsdLogMessage(CUPSD_LOG_ERROR
,
850 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
858 else if (!strcasecmp(line
, "Owner"))
865 cupsdSetString(&sub
->owner
, value
);
868 cupsdLogMessage(CUPSD_LOG_ERROR
,
869 "Syntax error on line %d of subscriptions.conf.",
874 else if (!strcasecmp(line
, "Recipient"))
881 cupsdSetString(&sub
->recipient
, value
);
884 cupsdLogMessage(CUPSD_LOG_ERROR
,
885 "Syntax error on line %d of subscriptions.conf.",
890 else if (!strcasecmp(line
, "JobId"))
896 if (value
&& isdigit(*value
& 255))
898 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
900 cupsdLogMessage(CUPSD_LOG_ERROR
,
901 "Job %s not found on line %d of subscriptions.conf.",
908 cupsdLogMessage(CUPSD_LOG_ERROR
,
909 "Syntax error on line %d of subscriptions.conf.",
914 else if (!strcasecmp(line
, "PrinterName"))
922 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
924 cupsdLogMessage(CUPSD_LOG_ERROR
,
925 "Printer \'%s\' not found on line %d of subscriptions.conf.",
932 cupsdLogMessage(CUPSD_LOG_ERROR
,
933 "Syntax error on line %d of subscriptions.conf.",
938 else if (!strcasecmp(line
, "UserData"))
941 * UserData encoded-string
946 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
948 if (*valueptr
== '<' && !hex
)
956 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
958 if (isdigit(valueptr
[0]))
959 sub
->user_data
[i
] = (valueptr
[0] - '0') << 4;
961 sub
->user_data
[i
] = (tolower(valueptr
[0]) - 'a' + 10) << 4;
963 if (isdigit(valueptr
[1]))
964 sub
->user_data
[i
] |= valueptr
[1] - '0';
966 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
970 if (*valueptr
== '>')
980 sub
->user_data
[i
] = *valueptr
++;
985 cupsdLogMessage(CUPSD_LOG_ERROR
,
986 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
990 sub
->user_data_len
= i
;
994 cupsdLogMessage(CUPSD_LOG_ERROR
,
995 "Syntax error on line %d of subscriptions.conf.",
1000 else if (!strcasecmp(line
, "LeaseDuration"))
1006 if (value
&& isdigit(*value
& 255))
1008 sub
->lease
= atoi(value
);
1009 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
1013 cupsdLogMessage(CUPSD_LOG_ERROR
,
1014 "Syntax error on line %d of subscriptions.conf.",
1019 else if (!strcasecmp(line
, "Interval"))
1025 if (value
&& isdigit(*value
& 255))
1026 sub
->interval
= atoi(value
);
1029 cupsdLogMessage(CUPSD_LOG_ERROR
,
1030 "Syntax error on line %d of subscriptions.conf.",
1035 else if (!strcasecmp(line
, "ExpirationTime"))
1041 if (value
&& isdigit(*value
& 255))
1042 sub
->expire
= atoi(value
);
1045 cupsdLogMessage(CUPSD_LOG_ERROR
,
1046 "Syntax error on line %d of subscriptions.conf.",
1051 else if (!strcasecmp(line
, "NextEventId"))
1057 if (value
&& isdigit(*value
& 255))
1058 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
1061 cupsdLogMessage(CUPSD_LOG_ERROR
,
1062 "Syntax error on line %d of subscriptions.conf.",
1070 * Something else we don't understand...
1073 cupsdLogMessage(CUPSD_LOG_ERROR
,
1074 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1084 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1088 cupsdSaveAllSubscriptions(void)
1090 int i
; /* Looping var */
1091 cups_file_t
*fp
; /* subscriptions.conf file */
1092 char temp
[1024]; /* Temporary string */
1093 char backup
[1024]; /* subscriptions.conf.O file */
1094 cupsd_subscription_t
*sub
; /* Current subscription */
1095 time_t curtime
; /* Current time */
1096 struct tm
*curdate
; /* Current date */
1097 unsigned mask
; /* Current event mask */
1098 const char *name
; /* Current event name */
1099 int hex
; /* Non-zero if we are writing hex data */
1103 * Create the subscriptions.conf file...
1106 snprintf(temp
, sizeof(temp
), "%s/subscriptions.conf", ServerRoot
);
1107 snprintf(backup
, sizeof(backup
), "%s/subscriptions.conf.O", ServerRoot
);
1109 if (rename(temp
, backup
))
1111 if (errno
!= ENOENT
)
1112 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to backup subscriptions.conf - %s",
1116 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1118 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to save subscriptions.conf - %s",
1121 if (rename(backup
, temp
))
1122 cupsdLogMessage(CUPSD_LOG_ERROR
,
1123 "Unable to restore subscriptions.conf - %s",
1128 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1131 * Restrict access to the file...
1134 fchown(cupsFileNumber(fp
), getuid(), Group
);
1135 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1138 * Write a small header to the file...
1141 curtime
= time(NULL
);
1142 curdate
= localtime(&curtime
);
1143 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1145 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1146 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1148 cupsFilePrintf(fp
, "NextSubscriptionId %d\n", NextSubscriptionId
);
1151 * Write every subscription known to the system...
1154 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1156 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1158 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1160 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1163 * Simple event list...
1166 cupsFilePrintf(fp
, "Events %s\n", name
);
1171 * Complex event list...
1174 cupsFilePuts(fp
, "Events");
1176 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1177 if (sub
->mask
& mask
)
1178 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1180 cupsFilePuts(fp
, "\n");
1184 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1186 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1188 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1190 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1192 if (sub
->user_data_len
> 0)
1194 cupsFilePuts(fp
, "UserData ");
1196 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1198 if (sub
->user_data
[i
] < ' ' ||
1199 sub
->user_data
[i
] > 0x7f ||
1200 sub
->user_data
[i
] == '<')
1204 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1208 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1214 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1218 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1223 cupsFilePuts(fp
, ">\n");
1225 cupsFilePutChar(fp
, '\n');
1228 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1229 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1230 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1231 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1233 cupsFilePuts(fp
, "</Subscription>\n");
1241 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1245 cupsdStopAllNotifiers(void)
1247 cupsd_subscription_t
*sub
; /* Current subscription */
1251 * See if we have started any notifiers...
1254 if (!NotifierStatusBuffer
)
1258 * Yes, kill any processes that are left...
1261 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1263 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1266 cupsdEndProcess(sub
->pid
, 0);
1273 * Close the status pipes...
1276 if (NotifierPipes
[0] >= 0)
1278 cupsdRemoveSelect(NotifierPipes
[0]);
1280 cupsdStatBufDelete(NotifierStatusBuffer
);
1282 close(NotifierPipes
[0]);
1283 close(NotifierPipes
[1]);
1285 NotifierPipes
[0] = -1;
1286 NotifierPipes
[1] = -1;
1287 NotifierStatusBuffer
= NULL
;
1293 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1296 static int /* O - Result of comparison */
1297 cupsd_compare_subscriptions(
1298 cupsd_subscription_t
*first
, /* I - First subscription object */
1299 cupsd_subscription_t
*second
, /* I - Second subscription object */
1300 void *unused
) /* I - Unused user data pointer */
1304 return (first
->id
- second
->id
);
1309 * 'cupsd_delete_event()' - Delete a single event...
1311 * Oldest events must be deleted first, otherwise the subscription cache
1312 * flushing code will not work properly.
1316 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1322 ippDelete(event
->attrs
);
1329 * 'cupsd_send_dbus()' - Send a DBUS notification...
1333 cupsd_send_dbus(cupsd_eventmask_t event
,/* I - Event to send */
1334 cupsd_printer_t
*dest
,/* I - Destination, if any */
1335 cupsd_job_t
*job
) /* I - Job, if any */
1337 DBusError error
; /* Error, if any */
1338 DBusMessage
*message
; /* Message to send */
1339 DBusMessageIter iter
; /* Iterator for message data */
1340 const char *what
; /* What to send */
1341 static DBusConnection
*con
= NULL
; /* Connection to DBUS server */
1345 * Figure out what to send, if anything...
1348 if (event
& CUPSD_EVENT_PRINTER_ADDED
)
1349 what
= "PrinterAdded";
1350 else if (event
& CUPSD_EVENT_PRINTER_DELETED
)
1351 what
= "PrinterRemoved";
1352 else if (event
& CUPSD_EVENT_PRINTER_CHANGED
)
1353 what
= "QueueChanged";
1354 else if (event
& CUPSD_EVENT_JOB_CREATED
)
1355 what
= "JobQueuedLocal";
1356 else if ((event
& CUPSD_EVENT_JOB_STATE
) && job
&&
1357 job
->state_value
== IPP_JOB_PROCESSING
)
1358 what
= "JobStartedLocal";
1363 * Verify connection to DBUS server...
1366 if (con
&& !dbus_connection_get_is_connected(con
))
1368 dbus_connection_unref(con
);
1374 dbus_error_init(&error
);
1376 con
= dbus_bus_get(getuid() ? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &error
);
1379 dbus_error_free(&error
);
1385 * Create and send the new message...
1388 message
= dbus_message_new_signal("/com/redhat/PrinterSpooler",
1389 "com.redhat.PrinterSpooler", what
);
1391 dbus_message_append_iter_init(message
, &iter
);
1393 dbus_message_iter_append_string(&iter
, dest
->name
);
1396 dbus_message_iter_append_uint32(&iter
, job
->id
);
1397 dbus_message_iter_append_string(&iter
, job
->username
);
1400 dbus_connection_send(con
, message
, NULL
);
1401 dbus_connection_flush(con
);
1402 dbus_message_unref(message
);
1404 #endif /* HAVE_DBUS */
1408 * 'cupsd_send_notification()' - Send a notification for the specified event.
1412 cupsd_send_notification(
1413 cupsd_subscription_t
*sub
, /* I - Subscription object */
1414 cupsd_event_t
*event
) /* I - Event to send */
1416 ipp_state_t state
; /* IPP event state */
1419 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1420 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1421 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1424 * Allocate the events array as needed...
1429 sub
->events
= calloc(MaxEvents
, sizeof(cupsd_event_t
*));
1433 cupsdLogMessage(CUPSD_LOG_CRIT
,
1434 "Unable to allocate memory for subscription #%d!",
1441 * Purge an old event as needed...
1444 if (sub
->num_events
>= MaxEvents
)
1447 * Purge the oldest event in the cache...
1450 cupsd_delete_event(sub
->events
[0]);
1453 sub
->first_event_id
++;
1455 memmove(sub
->events
, sub
->events
+ 1,
1456 sub
->num_events
* sizeof(cupsd_event_t
*));
1460 * Add the event to the subscription. Since the events array is
1461 * always MaxEvents in length, and since we will have already
1462 * removed an event from the subscription cache if we hit the
1463 * event cache limit, we don't need to check for overflow here...
1466 sub
->events
[sub
->num_events
] = event
;
1470 * Deliver the event...
1478 cupsd_start_notifier(sub
);
1480 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "sub->pipe=%d", sub
->pipe
);
1485 event
->attrs
->state
= IPP_IDLE
;
1487 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1488 if (state
== IPP_ERROR
)
1491 if (state
== IPP_ERROR
)
1496 * Notifier died, try restarting it...
1499 cupsdLogMessage(CUPSD_LOG_WARN
,
1500 "Notifier for subscription %d (%s) went away, "
1502 sub
->id
, sub
->recipient
);
1503 cupsdEndProcess(sub
->pid
, 0);
1510 cupsdLogMessage(CUPSD_LOG_ERROR
,
1511 "Unable to send event for subscription %d (%s)!",
1512 sub
->id
, sub
->recipient
);
1516 * If we get this far, break out of the loop...
1524 * Bump the event sequence number...
1527 sub
->next_event_id
++;
1532 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1536 cupsd_start_notifier(
1537 cupsd_subscription_t
*sub
) /* I - Subscription object */
1539 int pid
; /* Notifier process ID */
1540 int fds
[2]; /* Pipe file descriptors */
1541 char *argv
[4], /* Command-line arguments */
1542 *envp
[MAX_ENV
], /* Environment variables */
1543 user_data
[128], /* Base-64 encoded user data */
1544 scheme
[256], /* notify-recipient-uri scheme */
1545 *ptr
, /* Pointer into scheme */
1546 command
[1024]; /* Notifier command */
1550 * Extract the scheme name from the recipient URI and point to the
1551 * notifier program...
1554 strlcpy(scheme
, sub
->recipient
, sizeof(scheme
));
1555 if ((ptr
= strchr(scheme
, ':')) != NULL
)
1558 snprintf(command
, sizeof(command
), "%s/notifier/%s", ServerBin
, scheme
);
1561 * Base-64 encode the user data...
1564 httpEncode64_2(user_data
, sizeof(user_data
), (char *)sub
->user_data
,
1565 sub
->user_data_len
);
1568 * Setup the argument array...
1572 argv
[1] = sub
->recipient
;
1573 argv
[2] = user_data
;
1577 * Setup the environment...
1580 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1583 * Create pipes as needed...
1586 if (!NotifierStatusBuffer
)
1589 * Create the status pipe...
1592 if (cupsdOpenPipe(NotifierPipes
))
1594 cupsdLogMessage(CUPSD_LOG_ERROR
,
1595 "Unable to create pipes for notifier status - %s",
1600 NotifierStatusBuffer
= cupsdStatBufNew(NotifierPipes
[0], "[Notifier]");
1602 cupsdAddSelect(NotifierPipes
[0], (cupsd_selfunc_t
)cupsd_update_notifier
,
1606 if (cupsdOpenPipe(fds
))
1608 cupsdLogMessage(CUPSD_LOG_ERROR
,
1609 "Unable to create pipes for notifier %s - %s",
1610 scheme
, strerror(errno
));
1615 * Make sure the delivery pipe is non-blocking...
1618 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1621 * Create the notifier process...
1624 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1625 -1, -1, 0, DefaultProfile
, NULL
, &pid
) < 0)
1628 * Error - can't fork!
1631 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1632 scheme
, strerror(errno
));
1634 cupsdClosePipe(fds
);
1639 * Fork successful - return the PID...
1642 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1655 * 'cupsd_update_notifier()' - Read messages from notifiers.
1659 cupsd_update_notifier(void)
1661 char message
[1024]; /* Pointer to message text */
1662 int loglevel
; /* Log level for message */
1665 while (cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1666 message
, sizeof(message
)))
1668 if (loglevel
== CUPSD_LOG_INFO
)
1669 cupsdLogMessage(CUPSD_LOG_INFO
, "%s", message
);
1671 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))
1678 * End of "$Id: subscriptions.c 7824 2008-08-01 21:11:55Z mike $".