2 * Subscription routines for the CUPS scheduler.
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
11 * Include necessary headers...
16 # include <dbus/dbus.h>
17 # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
18 # define dbus_message_append_iter_init dbus_message_iter_init_append
19 # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
20 # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
21 # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
22 #endif /* HAVE_DBUS */
29 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
30 cupsd_subscription_t
*second
,
32 static void cupsd_delete_event(cupsd_event_t
*event
);
34 static void cupsd_send_dbus(cupsd_eventmask_t event
, cupsd_printer_t
*dest
,
36 #endif /* HAVE_DBUS */
37 static void cupsd_send_notification(cupsd_subscription_t
*sub
,
38 cupsd_event_t
*event
);
39 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
40 static void cupsd_update_notifier(void);
44 * 'cupsdAddEvent()' - Add an event to the global event cache.
49 cupsd_eventmask_t event
, /* I - Event */
50 cupsd_printer_t
*dest
, /* I - Printer associated with event */
51 cupsd_job_t
*job
, /* I - Job associated with event */
52 const char *text
, /* I - Notification text */
53 ...) /* I - Additional arguments as needed */
55 va_list ap
; /* Pointer to additional arguments */
56 char ftext
[1024]; /* Formatted text buffer */
57 ipp_attribute_t
*attr
; /* Printer/job attribute */
58 cupsd_event_t
*temp
; /* New event pointer */
59 cupsd_subscription_t
*sub
; /* Current subscription */
62 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
63 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
64 cupsdEventName(event
), dest
, dest
? dest
->name
: "",
65 job
, job
? job
->id
: 0, text
);
68 * Keep track of events with any OS-supplied notification mechanisms...
74 cupsd_send_dbus(event
, dest
, job
);
75 #endif /* HAVE_DBUS */
78 * Return if we aren't keeping events...
83 cupsdLogMessage(CUPSD_LOG_WARN
,
84 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
85 cupsdEventName(event
), MaxEvents
);
90 * Then loop through the subscriptions and add the event to the corresponding
94 for (temp
= NULL
, sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
96 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
99 * Check if this subscription requires this event...
102 if ((sub
->mask
& event
) != 0 && (sub
->dest
== dest
|| !sub
->dest
|| sub
->job
== job
))
105 * Need this event, so create a new event record...
108 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
110 cupsdLogMessage(CUPSD_LOG_CRIT
,
111 "Unable to allocate memory for event - %s",
117 temp
->time
= time(NULL
);
118 temp
->attrs
= ippNew();
124 temp
->dest
= dest
= cupsdFindPrinter(job
->dest
);
127 * Add common event notification attributes...
130 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
,
131 "notify-charset", NULL
, "utf-8");
133 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
,
134 "notify-natural-language", NULL
, "en-US");
136 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
137 "notify-subscription-id", sub
->id
);
139 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
140 "notify-sequence-number", sub
->next_event_id
);
142 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
143 "notify-subscribed-event", NULL
, cupsdEventName(event
));
145 if (sub
->user_data_len
> 0)
146 ippAddOctetString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
147 "notify-user-data", sub
->user_data
,
150 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
151 "printer-up-time", time(NULL
));
154 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
157 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
158 "notify-text", NULL
, ftext
);
163 * Add printer attributes...
166 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
,
167 "notify-printer-uri", NULL
, dest
->uri
);
169 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
170 "printer-name", NULL
, dest
->name
);
172 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
173 "printer-state", dest
->state
);
175 if (dest
->num_reasons
== 0)
176 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
177 IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
,
178 dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
180 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
181 IPP_TAG_KEYWORD
, "printer-state-reasons",
182 dest
->num_reasons
, NULL
,
183 (const char * const *)dest
->reasons
);
185 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
186 "printer-is-accepting-jobs", (char)dest
->accepting
);
192 * Add job attributes...
195 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
196 "notify-job-id", job
->id
);
197 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
198 "job-state", job
->state_value
);
200 if ((attr
= ippFindAttribute(job
->attrs
, "job-name",
201 IPP_TAG_NAME
)) != NULL
)
202 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
203 "job-name", NULL
, attr
->values
[0].string
.text
);
205 switch (job
->state_value
)
207 case IPP_JOB_PENDING
:
208 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
209 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
210 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
213 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
214 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
219 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
220 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
221 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
222 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
223 "job-hold-until-specified");
225 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
226 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
230 case IPP_JOB_PROCESSING
:
231 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
232 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
236 case IPP_JOB_STOPPED
:
237 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
238 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
242 case IPP_JOB_CANCELED
:
243 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
244 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
245 "job-canceled-by-user");
248 case IPP_JOB_ABORTED
:
249 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
250 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
251 "aborted-by-system");
254 case IPP_JOB_COMPLETED
:
255 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
256 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
257 "job-completed-successfully");
261 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
262 "job-impressions-completed",
263 job
->sheets
? job
->sheets
->values
[0].integer
: 0);
267 * Send the notification for this subscription...
270 cupsd_send_notification(sub
, temp
);
275 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
277 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...",
278 cupsdEventName(event
));
283 * 'cupsdAddSubscription()' - Add a new subscription object.
286 cupsd_subscription_t
* /* O - New subscription object */
287 cupsdAddSubscription(
288 unsigned mask
, /* I - Event mask */
289 cupsd_printer_t
*dest
, /* I - Printer, if any */
290 cupsd_job_t
*job
, /* I - Job, if any */
291 const char *uri
, /* I - notify-recipient-uri, if any */
292 int sub_id
) /* I - notify-subscription-id or 0 */
294 cupsd_subscription_t
*temp
; /* New subscription object */
297 cupsdLogMessage(CUPSD_LOG_DEBUG
,
298 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
300 mask
, dest
, dest
? dest
->name
: "", job
, job
? job
->id
: 0,
301 uri
? uri
: "(null)");
304 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
309 cupsdLogMessage(CUPSD_LOG_CRIT
,
310 "Unable to allocate memory for subscriptions - %s",
316 * Limit the number of subscriptions...
319 if (MaxSubscriptions
> 0 && cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
321 cupsdLogMessage(CUPSD_LOG_DEBUG
,
322 "cupsdAddSubscription: Reached MaxSubscriptions %d "
323 "(count=%d)", MaxSubscriptions
,
324 cupsArrayCount(Subscriptions
));
328 if (MaxSubscriptionsPerJob
> 0 && job
)
330 int count
; /* Number of job subscriptions */
332 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
335 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
336 if (temp
->job
== job
)
339 if (count
>= MaxSubscriptionsPerJob
)
341 cupsdLogMessage(CUPSD_LOG_DEBUG
,
342 "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
343 "for job #%d (count=%d)", MaxSubscriptionsPerJob
,
349 if (MaxSubscriptionsPerPrinter
> 0 && dest
)
351 int count
; /* Number of printer subscriptions */
353 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
356 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
357 if (temp
->dest
== dest
)
360 if (count
>= MaxSubscriptionsPerPrinter
)
362 cupsdLogMessage(CUPSD_LOG_DEBUG
,
363 "cupsdAddSubscription: Reached "
364 "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
365 MaxSubscriptionsPerPrinter
, dest
->name
, count
);
371 * Allocate memory for this subscription...
374 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
376 cupsdLogMessage(CUPSD_LOG_CRIT
,
377 "Unable to allocate memory for subscription object - %s",
383 * Fill in common data...
390 if (sub_id
>= NextSubscriptionId
)
391 NextSubscriptionId
= sub_id
+ 1;
395 temp
->id
= NextSubscriptionId
;
397 NextSubscriptionId
++;
404 temp
->first_event_id
= 1;
405 temp
->next_event_id
= 1;
407 cupsdSetString(&(temp
->recipient
), uri
);
410 * Add the subscription to the array...
413 cupsArrayAdd(Subscriptions
, temp
);
416 * For RSS subscriptions, run the notifier immediately...
419 if (uri
&& !strncmp(uri
, "rss:", 4))
420 cupsd_start_notifier(temp
);
427 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
431 cupsdDeleteAllSubscriptions(void)
433 cupsd_subscription_t
*sub
; /* Subscription */
439 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
441 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
442 cupsdDeleteSubscription(sub
, 0);
444 cupsArrayDelete(Subscriptions
);
445 Subscriptions
= NULL
;
450 * 'cupsdDeleteSubscription()' - Delete a subscription object.
454 cupsdDeleteSubscription(
455 cupsd_subscription_t
*sub
, /* I - Subscription object */
456 int update
) /* I - 1 = update subscriptions.conf */
459 * Close the pipe to the notifier as needed...
466 * Remove subscription from array...
469 cupsArrayRemove(Subscriptions
, sub
);
475 cupsdClearString(&(sub
->owner
));
476 cupsdClearString(&(sub
->recipient
));
478 cupsArrayDelete(sub
->events
);
483 * Update the subscriptions as needed...
487 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
492 * 'cupsdEventName()' - Return a single event name.
495 const char * /* O - Event name */
497 cupsd_eventmask_t event
) /* I - Event value */
504 case CUPSD_EVENT_PRINTER_RESTARTED
:
505 return ("printer-restarted");
507 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
508 return ("printer-shutdown");
510 case CUPSD_EVENT_PRINTER_STOPPED
:
511 return ("printer-stopped");
513 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
514 return ("printer-finishings-changed");
516 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
517 return ("printer-media-changed");
519 case CUPSD_EVENT_PRINTER_ADDED
:
520 return ("printer-added");
522 case CUPSD_EVENT_PRINTER_DELETED
:
523 return ("printer-deleted");
525 case CUPSD_EVENT_PRINTER_MODIFIED
:
526 return ("printer-modified");
528 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
:
529 return ("printer-queue-order-changed");
531 case CUPSD_EVENT_PRINTER_STATE
:
532 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
533 return ("printer-state-changed");
535 case CUPSD_EVENT_PRINTER_CONFIG
:
536 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
537 return ("printer-config-changed");
539 case CUPSD_EVENT_PRINTER_CHANGED
:
540 return ("printer-changed");
542 case CUPSD_EVENT_JOB_CREATED
:
543 return ("job-created");
545 case CUPSD_EVENT_JOB_COMPLETED
:
546 return ("job-completed");
548 case CUPSD_EVENT_JOB_STOPPED
:
549 return ("job-stopped");
551 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
552 return ("job-config-changed");
554 case CUPSD_EVENT_JOB_PROGRESS
:
555 return ("job-progress");
557 case CUPSD_EVENT_JOB_STATE
:
558 case CUPSD_EVENT_JOB_STATE_CHANGED
:
559 return ("job-state-changed");
561 case CUPSD_EVENT_SERVER_RESTARTED
:
562 return ("server-restarted");
564 case CUPSD_EVENT_SERVER_STARTED
:
565 return ("server-started");
567 case CUPSD_EVENT_SERVER_STOPPED
:
568 return ("server-stopped");
570 case CUPSD_EVENT_SERVER_AUDIT
:
571 return ("server-audit");
573 case CUPSD_EVENT_ALL
:
580 * 'cupsdEventValue()' - Return the event mask value for a name.
583 cupsd_eventmask_t
/* O - Event mask value */
584 cupsdEventValue(const char *name
) /* I - Name of event */
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-queue-order-changed"))
605 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
);
606 else if (!strcmp(name
, "printer-state-changed"))
607 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
608 else if (!strcmp(name
, "printer-config-changed"))
609 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
610 else if (!strcmp(name
, "printer-changed"))
611 return (CUPSD_EVENT_PRINTER_CHANGED
);
612 else if (!strcmp(name
, "job-created"))
613 return (CUPSD_EVENT_JOB_CREATED
);
614 else if (!strcmp(name
, "job-completed"))
615 return (CUPSD_EVENT_JOB_COMPLETED
);
616 else if (!strcmp(name
, "job-stopped"))
617 return (CUPSD_EVENT_JOB_STOPPED
);
618 else if (!strcmp(name
, "job-config-changed"))
619 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
620 else if (!strcmp(name
, "job-progress"))
621 return (CUPSD_EVENT_JOB_PROGRESS
);
622 else if (!strcmp(name
, "job-state-changed"))
623 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
624 else if (!strcmp(name
, "server-restarted"))
625 return (CUPSD_EVENT_SERVER_RESTARTED
);
626 else if (!strcmp(name
, "server-started"))
627 return (CUPSD_EVENT_SERVER_STARTED
);
628 else if (!strcmp(name
, "server-stopped"))
629 return (CUPSD_EVENT_SERVER_STOPPED
);
630 else if (!strcmp(name
, "server-audit"))
631 return (CUPSD_EVENT_SERVER_AUDIT
);
633 return (CUPSD_EVENT_NONE
);
638 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
642 cupsdExpireSubscriptions(
643 cupsd_printer_t
*dest
, /* I - Printer, if any */
644 cupsd_job_t
*job
) /* I - Job, if any */
646 cupsd_subscription_t
*sub
; /* Current subscription */
647 int update
; /* Update subscriptions.conf? */
648 time_t curtime
; /* Current time */
651 curtime
= time(NULL
);
654 cupsdLogMessage(CUPSD_LOG_INFO
, "Expiring subscriptions...");
656 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
658 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
659 if ((!sub
->job
&& !dest
&& sub
->expire
&& sub
->expire
<= curtime
) ||
660 (dest
&& sub
->dest
== dest
) ||
661 (job
&& sub
->job
== job
))
663 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...",
666 cupsdDeleteSubscription(sub
, 0);
672 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
677 * 'cupsdFindSubscription()' - Find a subscription by ID.
680 cupsd_subscription_t
* /* O - Subscription object */
681 cupsdFindSubscription(int id
) /* I - Subscription ID */
683 cupsd_subscription_t sub
; /* Subscription template */
688 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
693 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
697 cupsdLoadAllSubscriptions(void)
699 int i
; /* Looping var */
700 cups_file_t
*fp
; /* subscriptions.conf file */
701 int linenum
; /* Current line number */
702 char line
[1024], /* Line from file */
703 *value
, /* Pointer to value */
704 *valueptr
; /* Pointer into value */
705 cupsd_subscription_t
*sub
; /* Current subscription */
706 int hex
; /* Non-zero if reading hex data */
707 int delete_sub
; /* Delete subscription? */
711 * Open the subscriptions.conf file...
714 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
715 if ((fp
= cupsdOpenConfFile(line
)) == NULL
)
719 * Read all of the lines from the file...
726 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
728 if (!_cups_strcasecmp(line
, "NextSubscriptionId") && value
)
731 * NextSubscriptionId NNN
735 if (i
>= NextSubscriptionId
&& i
> 0)
736 NextSubscriptionId
= i
;
738 else if (!_cups_strcasecmp(line
, "<Subscription"))
744 if (!sub
&& value
&& isdigit(value
[0] & 255))
746 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
751 cupsdLogMessage(CUPSD_LOG_ERROR
,
752 "Syntax error on line %d of subscriptions.conf.",
757 else if (!_cups_strcasecmp(line
, "</Subscription>"))
761 cupsdLogMessage(CUPSD_LOG_ERROR
,
762 "Syntax error on line %d of subscriptions.conf.",
768 cupsdDeleteSubscription(sub
, 0);
775 cupsdLogMessage(CUPSD_LOG_ERROR
,
776 "Syntax error on line %d of subscriptions.conf.",
779 else if (!_cups_strcasecmp(line
, "Events"))
783 * Events name name name ...
788 cupsdLogMessage(CUPSD_LOG_ERROR
,
789 "Syntax error on line %d of subscriptions.conf.",
797 * Separate event names...
800 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
802 while (isspace(*valueptr
& 255))
806 * See if the name exists...
809 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
811 cupsdLogMessage(CUPSD_LOG_ERROR
,
812 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
820 else if (!_cups_strcasecmp(line
, "Owner"))
827 cupsdSetString(&sub
->owner
, value
);
830 cupsdLogMessage(CUPSD_LOG_ERROR
,
831 "Syntax error on line %d of subscriptions.conf.",
836 else if (!_cups_strcasecmp(line
, "Recipient"))
843 cupsdSetString(&sub
->recipient
, value
);
846 cupsdLogMessage(CUPSD_LOG_ERROR
,
847 "Syntax error on line %d of subscriptions.conf.",
852 else if (!_cups_strcasecmp(line
, "JobId"))
858 if (value
&& isdigit(*value
& 255))
860 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
862 cupsdLogMessage(CUPSD_LOG_ERROR
,
863 "Job %s not found on line %d of subscriptions.conf.",
870 cupsdLogMessage(CUPSD_LOG_ERROR
,
871 "Syntax error on line %d of subscriptions.conf.",
876 else if (!_cups_strcasecmp(line
, "PrinterName"))
884 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
886 cupsdLogMessage(CUPSD_LOG_ERROR
,
887 "Printer \'%s\' not found on line %d of subscriptions.conf.",
894 cupsdLogMessage(CUPSD_LOG_ERROR
,
895 "Syntax error on line %d of subscriptions.conf.",
900 else if (!_cups_strcasecmp(line
, "UserData"))
903 * UserData encoded-string
908 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
910 if (*valueptr
== '<' && !hex
)
918 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
920 if (isdigit(valueptr
[0]))
921 sub
->user_data
[i
] = (unsigned char)((valueptr
[0] - '0') << 4);
923 sub
->user_data
[i
] = (unsigned char)((tolower(valueptr
[0]) - 'a' + 10) << 4);
925 if (isdigit(valueptr
[1]))
926 sub
->user_data
[i
] |= valueptr
[1] - '0';
928 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
932 if (*valueptr
== '>')
942 sub
->user_data
[i
] = (unsigned char)*valueptr
++;
947 cupsdLogMessage(CUPSD_LOG_ERROR
,
948 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
952 sub
->user_data_len
= i
;
956 cupsdLogMessage(CUPSD_LOG_ERROR
,
957 "Syntax error on line %d of subscriptions.conf.",
962 else if (!_cups_strcasecmp(line
, "LeaseDuration"))
968 if (value
&& isdigit(*value
& 255))
970 sub
->lease
= atoi(value
);
971 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
975 cupsdLogMessage(CUPSD_LOG_ERROR
,
976 "Syntax error on line %d of subscriptions.conf.",
981 else if (!_cups_strcasecmp(line
, "Interval"))
987 if (value
&& isdigit(*value
& 255))
988 sub
->interval
= atoi(value
);
991 cupsdLogMessage(CUPSD_LOG_ERROR
,
992 "Syntax error on line %d of subscriptions.conf.",
997 else if (!_cups_strcasecmp(line
, "ExpirationTime"))
1003 if (value
&& isdigit(*value
& 255))
1004 sub
->expire
= atoi(value
);
1007 cupsdLogMessage(CUPSD_LOG_ERROR
,
1008 "Syntax error on line %d of subscriptions.conf.",
1013 else if (!_cups_strcasecmp(line
, "NextEventId"))
1019 if (value
&& isdigit(*value
& 255))
1020 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
1023 cupsdLogMessage(CUPSD_LOG_ERROR
,
1024 "Syntax error on line %d of subscriptions.conf.",
1032 * Something else we don't understand...
1035 cupsdLogMessage(CUPSD_LOG_ERROR
,
1036 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1046 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1050 cupsdSaveAllSubscriptions(void)
1052 int i
; /* Looping var */
1053 cups_file_t
*fp
; /* subscriptions.conf file */
1054 char filename
[1024], /* subscriptions.conf filename */
1055 temp
[1024]; /* Temporary string */
1056 cupsd_subscription_t
*sub
; /* Current subscription */
1057 time_t curtime
; /* Current time */
1058 struct tm
*curdate
; /* Current date */
1059 unsigned mask
; /* Current event mask */
1060 const char *name
; /* Current event name */
1061 int hex
; /* Non-zero if we are writing hex data */
1065 * Create the subscriptions.conf file...
1068 snprintf(filename
, sizeof(filename
), "%s/subscriptions.conf", ServerRoot
);
1070 if ((fp
= cupsdCreateConfFile(filename
, ConfigFilePerm
)) == NULL
)
1073 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1076 * Write a small header to the file...
1079 curtime
= time(NULL
);
1080 curdate
= localtime(&curtime
);
1081 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1083 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1084 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1086 cupsFilePrintf(fp
, "NextSubscriptionId %d\n", NextSubscriptionId
);
1089 * Write every subscription known to the system...
1092 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1094 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1096 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1098 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1101 * Simple event list...
1104 cupsFilePrintf(fp
, "Events %s\n", name
);
1109 * Complex event list...
1112 cupsFilePuts(fp
, "Events");
1114 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1115 if (sub
->mask
& mask
)
1116 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1118 cupsFilePuts(fp
, "\n");
1122 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1124 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1126 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1128 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1130 if (sub
->user_data_len
> 0)
1132 cupsFilePuts(fp
, "UserData ");
1134 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1136 if (sub
->user_data
[i
] < ' ' ||
1137 sub
->user_data
[i
] > 0x7f ||
1138 sub
->user_data
[i
] == '<')
1142 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1146 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1152 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1156 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1161 cupsFilePuts(fp
, ">\n");
1163 cupsFilePutChar(fp
, '\n');
1166 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1167 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1168 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1169 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1171 cupsFilePuts(fp
, "</Subscription>\n");
1174 cupsdCloseCreatedConfFile(fp
, filename
);
1179 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1183 cupsdStopAllNotifiers(void)
1185 cupsd_subscription_t
*sub
; /* Current subscription */
1189 * See if we have started any notifiers...
1192 if (!NotifierStatusBuffer
)
1196 * Yes, kill any processes that are left...
1199 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1201 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1204 cupsdEndProcess(sub
->pid
, 0);
1211 * Close the status pipes...
1214 if (NotifierPipes
[0] >= 0)
1216 cupsdRemoveSelect(NotifierPipes
[0]);
1218 cupsdStatBufDelete(NotifierStatusBuffer
);
1220 close(NotifierPipes
[0]);
1221 close(NotifierPipes
[1]);
1223 NotifierPipes
[0] = -1;
1224 NotifierPipes
[1] = -1;
1225 NotifierStatusBuffer
= NULL
;
1231 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1234 static int /* O - Result of comparison */
1235 cupsd_compare_subscriptions(
1236 cupsd_subscription_t
*first
, /* I - First subscription object */
1237 cupsd_subscription_t
*second
, /* I - Second subscription object */
1238 void *unused
) /* I - Unused user data pointer */
1242 return (first
->id
- second
->id
);
1247 * 'cupsd_delete_event()' - Delete a single event...
1249 * Oldest events must be deleted first, otherwise the subscription cache
1250 * flushing code will not work properly.
1254 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1260 ippDelete(event
->attrs
);
1267 * 'cupsd_send_dbus()' - Send a DBUS notification...
1271 cupsd_send_dbus(cupsd_eventmask_t event
,/* I - Event to send */
1272 cupsd_printer_t
*dest
,/* I - Destination, if any */
1273 cupsd_job_t
*job
) /* I - Job, if any */
1275 DBusError error
; /* Error, if any */
1276 DBusMessage
*message
; /* Message to send */
1277 DBusMessageIter iter
; /* Iterator for message data */
1278 const char *what
; /* What to send */
1279 static DBusConnection
*con
= NULL
; /* Connection to DBUS server */
1283 * Figure out what to send, if anything...
1286 if (event
& CUPSD_EVENT_PRINTER_ADDED
)
1287 what
= "PrinterAdded";
1288 else if (event
& CUPSD_EVENT_PRINTER_DELETED
)
1289 what
= "PrinterRemoved";
1290 else if (event
& CUPSD_EVENT_PRINTER_CHANGED
)
1291 what
= "QueueChanged";
1292 else if (event
& CUPSD_EVENT_JOB_CREATED
)
1293 what
= "JobQueuedLocal";
1294 else if ((event
& CUPSD_EVENT_JOB_STATE
) && job
&&
1295 job
->state_value
== IPP_JOB_PROCESSING
)
1296 what
= "JobStartedLocal";
1301 * Verify connection to DBUS server...
1304 if (con
&& !dbus_connection_get_is_connected(con
))
1306 dbus_connection_unref(con
);
1312 dbus_error_init(&error
);
1314 con
= dbus_bus_get(getuid() ? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &error
);
1317 dbus_error_free(&error
);
1323 * Create and send the new message...
1326 message
= dbus_message_new_signal("/com/redhat/PrinterSpooler",
1327 "com.redhat.PrinterSpooler", what
);
1329 dbus_message_append_iter_init(message
, &iter
);
1331 dbus_message_iter_append_string(&iter
, dest
->name
);
1334 dbus_message_iter_append_uint32(&iter
, job
->id
);
1335 dbus_message_iter_append_string(&iter
, job
->username
);
1338 dbus_connection_send(con
, message
, NULL
);
1339 dbus_connection_flush(con
);
1340 dbus_message_unref(message
);
1342 #endif /* HAVE_DBUS */
1346 * 'cupsd_send_notification()' - Send a notification for the specified event.
1350 cupsd_send_notification(
1351 cupsd_subscription_t
*sub
, /* I - Subscription object */
1352 cupsd_event_t
*event
) /* I - Event to send */
1354 ipp_state_t state
; /* IPP event state */
1357 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1358 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1359 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1362 * Allocate the events array as needed...
1367 sub
->events
= cupsArrayNew3((cups_array_func_t
)NULL
, NULL
,
1368 (cups_ahash_func_t
)NULL
, 0,
1369 (cups_acopy_func_t
)NULL
,
1370 (cups_afree_func_t
)cupsd_delete_event
);
1374 cupsdLogMessage(CUPSD_LOG_CRIT
,
1375 "Unable to allocate memory for subscription #%d!",
1382 * Purge an old event as needed...
1385 if (cupsArrayCount(sub
->events
) >= MaxEvents
)
1388 * Purge the oldest event in the cache...
1391 cupsArrayRemove(sub
->events
, cupsArrayFirst(sub
->events
));
1393 sub
->first_event_id
++;
1397 * Add the event to the subscription. Since the events array is
1398 * always MaxEvents in length, and since we will have already
1399 * removed an event from the subscription cache if we hit the
1400 * event cache limit, we don't need to check for overflow here...
1403 cupsArrayAdd(sub
->events
, event
);
1406 * Deliver the event...
1414 cupsd_start_notifier(sub
);
1416 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "sub->pipe=%d", sub
->pipe
);
1421 event
->attrs
->state
= IPP_IDLE
;
1423 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1424 if (state
== IPP_ERROR
)
1427 if (state
== IPP_ERROR
)
1432 * Notifier died, try restarting it...
1435 cupsdLogMessage(CUPSD_LOG_WARN
,
1436 "Notifier for subscription %d (%s) went away, "
1438 sub
->id
, sub
->recipient
);
1439 cupsdEndProcess(sub
->pid
, 0);
1446 cupsdLogMessage(CUPSD_LOG_ERROR
,
1447 "Unable to send event for subscription %d (%s)!",
1448 sub
->id
, sub
->recipient
);
1452 * If we get this far, break out of the loop...
1460 * Bump the event sequence number...
1463 sub
->next_event_id
++;
1468 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1472 cupsd_start_notifier(
1473 cupsd_subscription_t
*sub
) /* I - Subscription object */
1475 int pid
; /* Notifier process ID */
1476 int fds
[2]; /* Pipe file descriptors */
1477 char *argv
[4], /* Command-line arguments */
1478 *envp
[MAX_ENV
], /* Environment variables */
1479 user_data
[128], /* Base-64 encoded user data */
1480 scheme
[256], /* notify-recipient-uri scheme */
1481 *ptr
, /* Pointer into scheme */
1482 command
[1024]; /* Notifier command */
1486 * Extract the scheme name from the recipient URI and point to the
1487 * notifier program...
1490 strlcpy(scheme
, sub
->recipient
, sizeof(scheme
));
1491 if ((ptr
= strchr(scheme
, ':')) != NULL
)
1494 snprintf(command
, sizeof(command
), "%s/notifier/%s", ServerBin
, scheme
);
1497 * Base-64 encode the user data...
1500 httpEncode64_2(user_data
, sizeof(user_data
), (char *)sub
->user_data
,
1501 sub
->user_data_len
);
1504 * Setup the argument array...
1508 argv
[1] = sub
->recipient
;
1509 argv
[2] = user_data
;
1513 * Setup the environment...
1516 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1519 * Create pipes as needed...
1522 if (!NotifierStatusBuffer
)
1525 * Create the status pipe...
1528 if (cupsdOpenPipe(NotifierPipes
))
1530 cupsdLogMessage(CUPSD_LOG_ERROR
,
1531 "Unable to create pipes for notifier status - %s",
1536 NotifierStatusBuffer
= cupsdStatBufNew(NotifierPipes
[0], "[Notifier]");
1538 cupsdAddSelect(NotifierPipes
[0], (cupsd_selfunc_t
)cupsd_update_notifier
,
1542 if (cupsdOpenPipe(fds
))
1544 cupsdLogMessage(CUPSD_LOG_ERROR
,
1545 "Unable to create pipes for notifier %s - %s",
1546 scheme
, strerror(errno
));
1551 * Make sure the delivery pipe is non-blocking...
1554 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1557 * Create the notifier process...
1560 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1561 -1, -1, 0, DefaultProfile
, NULL
, &pid
) < 0)
1564 * Error - can't fork!
1567 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1568 scheme
, strerror(errno
));
1570 cupsdClosePipe(fds
);
1575 * Fork successful - return the PID...
1578 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1591 * 'cupsd_update_notifier()' - Read messages from notifiers.
1595 cupsd_update_notifier(void)
1597 char message
[1024]; /* Pointer to message text */
1598 int loglevel
; /* Log level for message */
1601 while (cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1602 message
, sizeof(message
)))
1604 if (loglevel
== CUPSD_LOG_INFO
)
1605 cupsdLogMessage(CUPSD_LOG_INFO
, "%s", message
);
1607 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))