2 * Subscription routines for the CUPS scheduler.
4 * Copyright © 2007-2019 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
12 * Include necessary headers...
17 # include <dbus/dbus.h>
18 # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
19 # define dbus_message_append_iter_init dbus_message_iter_init_append
20 # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
21 # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
22 # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
23 #endif /* HAVE_DBUS */
30 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
31 cupsd_subscription_t
*second
,
33 static void cupsd_delete_event(cupsd_event_t
*event
);
35 static void cupsd_send_dbus(cupsd_eventmask_t event
, cupsd_printer_t
*dest
,
37 #endif /* HAVE_DBUS */
38 static void cupsd_send_notification(cupsd_subscription_t
*sub
,
39 cupsd_event_t
*event
);
40 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
41 static void cupsd_update_notifier(void);
45 * 'cupsdAddEvent()' - Add an event to the global event cache.
50 cupsd_eventmask_t event
, /* I - Event */
51 cupsd_printer_t
*dest
, /* I - Printer associated with event */
52 cupsd_job_t
*job
, /* I - Job associated with event */
53 const char *text
, /* I - Notification text */
54 ...) /* I - Additional arguments as needed */
56 va_list ap
; /* Pointer to additional arguments */
57 char ftext
[1024]; /* Formatted text buffer */
58 ipp_attribute_t
*attr
; /* Printer/job attribute */
59 cupsd_event_t
*temp
; /* New event pointer */
60 cupsd_subscription_t
*sub
; /* Current subscription */
63 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
64 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
65 cupsdEventName(event
), dest
, dest
? dest
->name
: "",
66 job
, job
? job
->id
: 0, text
);
69 * Keep track of events with any OS-supplied notification mechanisms...
75 cupsd_send_dbus(event
, dest
, job
);
76 #endif /* HAVE_DBUS */
79 * Return if we aren't keeping events...
84 cupsdLogMessage(CUPSD_LOG_WARN
,
85 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
86 cupsdEventName(event
), MaxEvents
);
91 * Then loop through the subscriptions and add the event to the corresponding
95 for (temp
= NULL
, sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
97 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
100 * Check if this subscription requires this event...
103 if ((sub
->mask
& event
) != 0 && (sub
->dest
== dest
|| !sub
->dest
|| sub
->job
== job
))
106 * Need this event, so create a new event record...
109 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
111 cupsdLogMessage(CUPSD_LOG_CRIT
,
112 "Unable to allocate memory for event - %s",
118 temp
->time
= time(NULL
);
119 temp
->attrs
= ippNew();
125 temp
->dest
= dest
= cupsdFindPrinter(job
->dest
);
128 * Add common event notification attributes...
131 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
,
132 "notify-charset", NULL
, "utf-8");
134 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
,
135 "notify-natural-language", NULL
, "en-US");
137 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
138 "notify-subscription-id", sub
->id
);
140 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
141 "notify-sequence-number", sub
->next_event_id
);
143 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
144 "notify-subscribed-event", NULL
, cupsdEventName(event
));
146 if (sub
->user_data_len
> 0)
147 ippAddOctetString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
148 "notify-user-data", sub
->user_data
,
151 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
152 "printer-up-time", time(NULL
));
155 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
158 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
159 "notify-text", NULL
, ftext
);
164 * Add printer attributes...
167 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
, "notify-printer-uri", NULL
, dest
->uri
);
169 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "printer-name", NULL
, dest
->name
);
171 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "printer-state", (int)dest
->state
);
173 if (dest
->num_reasons
== 0)
174 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
, dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
176 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "printer-state-reasons", dest
->num_reasons
, NULL
, (const char * const *)dest
->reasons
);
178 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, "printer-is-accepting-jobs", (char)dest
->accepting
);
184 * Add job attributes...
187 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "notify-job-id", job
->id
);
188 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
, "job-state", (int)job
->state_value
);
190 if ((attr
= ippFindAttribute(job
->attrs
, "job-name", IPP_TAG_NAME
)) != NULL
)
191 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
, "job-name", NULL
, attr
->values
[0].string
.text
);
193 switch (job
->state_value
)
195 case IPP_JOB_PENDING
:
196 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
197 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "printer-stopped");
199 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "none");
203 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
204 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
205 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-hold-until-specified");
207 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-incoming");
210 case IPP_JOB_PROCESSING
:
211 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-printing");
214 case IPP_JOB_STOPPED
:
215 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-stopped");
218 case IPP_JOB_CANCELED
:
219 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-canceled-by-user");
222 case IPP_JOB_ABORTED
:
223 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "aborted-by-system");
226 case IPP_JOB_COMPLETED
:
227 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
, "job-state-reasons", NULL
, "job-completed-successfully");
231 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
, "job-impressions-completed", job
->sheets
? job
->sheets
->values
[0].integer
: 0);
235 * Send the notification for this subscription...
238 cupsd_send_notification(sub
, temp
);
243 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
245 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...", cupsdEventName(event
));
250 * 'cupsdAddSubscription()' - Add a new subscription object.
253 cupsd_subscription_t
* /* O - New subscription object */
254 cupsdAddSubscription(
255 unsigned mask
, /* I - Event mask */
256 cupsd_printer_t
*dest
, /* I - Printer, if any */
257 cupsd_job_t
*job
, /* I - Job, if any */
258 const char *uri
, /* I - notify-recipient-uri, if any */
259 int sub_id
) /* I - notify-subscription-id or 0 */
261 cupsd_subscription_t
*temp
; /* New subscription object */
264 cupsdLogMessage(CUPSD_LOG_DEBUG
,
265 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
267 mask
, dest
, dest
? dest
->name
: "", job
, job
? job
->id
: 0,
268 uri
? uri
: "(null)");
271 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
276 cupsdLogMessage(CUPSD_LOG_CRIT
,
277 "Unable to allocate memory for subscriptions - %s",
283 * Limit the number of subscriptions...
286 if (MaxSubscriptions
> 0 && cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
288 cupsdLogMessage(CUPSD_LOG_DEBUG
,
289 "cupsdAddSubscription: Reached MaxSubscriptions %d "
290 "(count=%d)", MaxSubscriptions
,
291 cupsArrayCount(Subscriptions
));
295 if (MaxSubscriptionsPerJob
> 0 && job
)
297 int count
; /* Number of job subscriptions */
299 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
302 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
303 if (temp
->job
== job
)
306 if (count
>= MaxSubscriptionsPerJob
)
308 cupsdLogMessage(CUPSD_LOG_DEBUG
,
309 "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
310 "for job #%d (count=%d)", MaxSubscriptionsPerJob
,
316 if (MaxSubscriptionsPerPrinter
> 0 && dest
)
318 int count
; /* Number of printer subscriptions */
320 for (temp
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
),
323 temp
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
324 if (temp
->dest
== dest
)
327 if (count
>= MaxSubscriptionsPerPrinter
)
329 cupsdLogMessage(CUPSD_LOG_DEBUG
,
330 "cupsdAddSubscription: Reached "
331 "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
332 MaxSubscriptionsPerPrinter
, dest
->name
, count
);
338 * Allocate memory for this subscription...
341 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
343 cupsdLogMessage(CUPSD_LOG_CRIT
,
344 "Unable to allocate memory for subscription object - %s",
350 * Fill in common data...
357 if (sub_id
>= NextSubscriptionId
)
358 NextSubscriptionId
= sub_id
+ 1;
362 temp
->id
= NextSubscriptionId
;
364 NextSubscriptionId
++;
371 temp
->first_event_id
= 1;
372 temp
->next_event_id
= 1;
374 cupsdSetString(&(temp
->recipient
), uri
);
377 * Add the subscription to the array...
380 cupsArrayAdd(Subscriptions
, temp
);
383 * For RSS subscriptions, run the notifier immediately...
386 if (uri
&& !strncmp(uri
, "rss:", 4))
387 cupsd_start_notifier(temp
);
394 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
398 cupsdDeleteAllSubscriptions(void)
400 cupsd_subscription_t
*sub
; /* Subscription */
406 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
408 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
409 cupsdDeleteSubscription(sub
, 0);
411 cupsArrayDelete(Subscriptions
);
412 Subscriptions
= NULL
;
417 * 'cupsdDeleteSubscription()' - Delete a subscription object.
421 cupsdDeleteSubscription(
422 cupsd_subscription_t
*sub
, /* I - Subscription object */
423 int update
) /* I - 1 = update subscriptions.conf */
426 * Close the pipe to the notifier as needed...
433 * Remove subscription from array...
436 cupsArrayRemove(Subscriptions
, sub
);
442 cupsdClearString(&(sub
->owner
));
443 cupsdClearString(&(sub
->recipient
));
445 cupsArrayDelete(sub
->events
);
450 * Update the subscriptions as needed...
454 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
459 * 'cupsdEventName()' - Return a single event name.
462 const char * /* O - Event name */
464 cupsd_eventmask_t event
) /* I - Event value */
471 case CUPSD_EVENT_PRINTER_RESTARTED
:
472 return ("printer-restarted");
474 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
475 return ("printer-shutdown");
477 case CUPSD_EVENT_PRINTER_STOPPED
:
478 return ("printer-stopped");
480 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
481 return ("printer-finishings-changed");
483 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
484 return ("printer-media-changed");
486 case CUPSD_EVENT_PRINTER_ADDED
:
487 return ("printer-added");
489 case CUPSD_EVENT_PRINTER_DELETED
:
490 return ("printer-deleted");
492 case CUPSD_EVENT_PRINTER_MODIFIED
:
493 return ("printer-modified");
495 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
:
496 return ("printer-queue-order-changed");
498 case CUPSD_EVENT_PRINTER_STATE
:
499 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
500 return ("printer-state-changed");
502 case CUPSD_EVENT_PRINTER_CONFIG
:
503 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
504 return ("printer-config-changed");
506 case CUPSD_EVENT_PRINTER_CHANGED
:
507 return ("printer-changed");
509 case CUPSD_EVENT_JOB_CREATED
:
510 return ("job-created");
512 case CUPSD_EVENT_JOB_COMPLETED
:
513 return ("job-completed");
515 case CUPSD_EVENT_JOB_STOPPED
:
516 return ("job-stopped");
518 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
519 return ("job-config-changed");
521 case CUPSD_EVENT_JOB_PROGRESS
:
522 return ("job-progress");
524 case CUPSD_EVENT_JOB_STATE
:
525 case CUPSD_EVENT_JOB_STATE_CHANGED
:
526 return ("job-state-changed");
528 case CUPSD_EVENT_SERVER_RESTARTED
:
529 return ("server-restarted");
531 case CUPSD_EVENT_SERVER_STARTED
:
532 return ("server-started");
534 case CUPSD_EVENT_SERVER_STOPPED
:
535 return ("server-stopped");
537 case CUPSD_EVENT_SERVER_AUDIT
:
538 return ("server-audit");
540 case CUPSD_EVENT_ALL
:
547 * 'cupsdEventValue()' - Return the event mask value for a name.
550 cupsd_eventmask_t
/* O - Event mask value */
551 cupsdEventValue(const char *name
) /* I - Name of event */
553 if (!strcmp(name
, "all"))
554 return (CUPSD_EVENT_ALL
);
555 else if (!strcmp(name
, "printer-restarted"))
556 return (CUPSD_EVENT_PRINTER_RESTARTED
);
557 else if (!strcmp(name
, "printer-shutdown"))
558 return (CUPSD_EVENT_PRINTER_SHUTDOWN
);
559 else if (!strcmp(name
, "printer-stopped"))
560 return (CUPSD_EVENT_PRINTER_STOPPED
);
561 else if (!strcmp(name
, "printer-finishings-changed"))
562 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
);
563 else if (!strcmp(name
, "printer-media-changed"))
564 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED
);
565 else if (!strcmp(name
, "printer-added"))
566 return (CUPSD_EVENT_PRINTER_ADDED
);
567 else if (!strcmp(name
, "printer-deleted"))
568 return (CUPSD_EVENT_PRINTER_DELETED
);
569 else if (!strcmp(name
, "printer-modified"))
570 return (CUPSD_EVENT_PRINTER_MODIFIED
);
571 else if (!strcmp(name
, "printer-queue-order-changed"))
572 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED
);
573 else if (!strcmp(name
, "printer-state-changed"))
574 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
575 else if (!strcmp(name
, "printer-config-changed"))
576 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
577 else if (!strcmp(name
, "printer-changed"))
578 return (CUPSD_EVENT_PRINTER_CHANGED
);
579 else if (!strcmp(name
, "job-created"))
580 return (CUPSD_EVENT_JOB_CREATED
);
581 else if (!strcmp(name
, "job-completed"))
582 return (CUPSD_EVENT_JOB_COMPLETED
);
583 else if (!strcmp(name
, "job-stopped"))
584 return (CUPSD_EVENT_JOB_STOPPED
);
585 else if (!strcmp(name
, "job-config-changed"))
586 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
587 else if (!strcmp(name
, "job-progress"))
588 return (CUPSD_EVENT_JOB_PROGRESS
);
589 else if (!strcmp(name
, "job-state-changed"))
590 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
591 else if (!strcmp(name
, "server-restarted"))
592 return (CUPSD_EVENT_SERVER_RESTARTED
);
593 else if (!strcmp(name
, "server-started"))
594 return (CUPSD_EVENT_SERVER_STARTED
);
595 else if (!strcmp(name
, "server-stopped"))
596 return (CUPSD_EVENT_SERVER_STOPPED
);
597 else if (!strcmp(name
, "server-audit"))
598 return (CUPSD_EVENT_SERVER_AUDIT
);
600 return (CUPSD_EVENT_NONE
);
605 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
609 cupsdExpireSubscriptions(
610 cupsd_printer_t
*dest
, /* I - Printer, if any */
611 cupsd_job_t
*job
) /* I - Job, if any */
613 cupsd_subscription_t
*sub
; /* Current subscription */
614 int update
; /* Update subscriptions.conf? */
615 time_t curtime
; /* Current time */
618 if (cupsArrayCount(Subscriptions
) == 0)
621 curtime
= time(NULL
);
624 cupsdLogMessage(CUPSD_LOG_INFO
, "Expiring subscriptions...");
626 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
628 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
629 if ((!sub
->job
&& !dest
&& sub
->expire
&& sub
->expire
<= curtime
) ||
630 (dest
&& sub
->dest
== dest
) ||
631 (job
&& sub
->job
== job
))
633 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...",
636 cupsdDeleteSubscription(sub
, 0);
642 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS
);
647 * 'cupsdFindSubscription()' - Find a subscription by ID.
650 cupsd_subscription_t
* /* O - Subscription object */
651 cupsdFindSubscription(int id
) /* I - Subscription ID */
653 cupsd_subscription_t sub
; /* Subscription template */
658 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
663 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
667 cupsdLoadAllSubscriptions(void)
669 int i
; /* Looping var */
670 cups_file_t
*fp
; /* subscriptions.conf file */
671 int linenum
; /* Current line number */
672 char line
[1024], /* Line from file */
673 *value
, /* Pointer to value */
674 *valueptr
; /* Pointer into value */
675 cupsd_subscription_t
*sub
; /* Current subscription */
676 int hex
; /* Non-zero if reading hex data */
677 int delete_sub
; /* Delete subscription? */
681 * Open the subscriptions.conf file...
684 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
685 if ((fp
= cupsdOpenConfFile(line
)) == NULL
)
689 * Read all of the lines from the file...
696 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
698 if (!_cups_strcasecmp(line
, "NextSubscriptionId") && value
)
701 * NextSubscriptionId NNN
705 if (i
>= NextSubscriptionId
&& i
> 0)
706 NextSubscriptionId
= i
;
708 else if (!_cups_strcasecmp(line
, "<Subscription"))
714 if (!sub
&& value
&& isdigit(value
[0] & 255))
716 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
721 cupsdLogMessage(CUPSD_LOG_ERROR
,
722 "Syntax error on line %d of subscriptions.conf.",
727 else if (!_cups_strcasecmp(line
, "</Subscription>"))
731 cupsdLogMessage(CUPSD_LOG_ERROR
,
732 "Syntax error on line %d of subscriptions.conf.",
738 cupsdDeleteSubscription(sub
, 0);
745 cupsdLogMessage(CUPSD_LOG_ERROR
,
746 "Syntax error on line %d of subscriptions.conf.",
749 else if (!_cups_strcasecmp(line
, "Events"))
753 * Events name name name ...
758 cupsdLogMessage(CUPSD_LOG_ERROR
,
759 "Syntax error on line %d of subscriptions.conf.",
767 * Separate event names...
770 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
772 while (isspace(*valueptr
& 255))
776 * See if the name exists...
779 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
781 cupsdLogMessage(CUPSD_LOG_ERROR
,
782 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
790 else if (!_cups_strcasecmp(line
, "Owner"))
797 cupsdSetString(&sub
->owner
, value
);
800 cupsdLogMessage(CUPSD_LOG_ERROR
,
801 "Syntax error on line %d of subscriptions.conf.",
806 else if (!_cups_strcasecmp(line
, "Recipient"))
813 cupsdSetString(&sub
->recipient
, value
);
816 cupsdLogMessage(CUPSD_LOG_ERROR
,
817 "Syntax error on line %d of subscriptions.conf.",
822 else if (!_cups_strcasecmp(line
, "JobId"))
828 if (value
&& isdigit(*value
& 255))
830 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
832 cupsdLogMessage(CUPSD_LOG_ERROR
,
833 "Job %s not found on line %d of subscriptions.conf.",
840 cupsdLogMessage(CUPSD_LOG_ERROR
,
841 "Syntax error on line %d of subscriptions.conf.",
846 else if (!_cups_strcasecmp(line
, "PrinterName"))
854 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
856 cupsdLogMessage(CUPSD_LOG_ERROR
,
857 "Printer \'%s\' not found on line %d of subscriptions.conf.",
864 cupsdLogMessage(CUPSD_LOG_ERROR
,
865 "Syntax error on line %d of subscriptions.conf.",
870 else if (!_cups_strcasecmp(line
, "UserData"))
873 * UserData encoded-string
878 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
880 if (*valueptr
== '<' && !hex
)
888 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
890 if (isdigit(valueptr
[0]))
891 sub
->user_data
[i
] = (unsigned char)((valueptr
[0] - '0') << 4);
893 sub
->user_data
[i
] = (unsigned char)((tolower(valueptr
[0]) - 'a' + 10) << 4);
895 if (isdigit(valueptr
[1]))
896 sub
->user_data
[i
] |= valueptr
[1] - '0';
898 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
902 if (*valueptr
== '>')
912 sub
->user_data
[i
] = (unsigned char)*valueptr
++;
917 cupsdLogMessage(CUPSD_LOG_ERROR
,
918 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
922 sub
->user_data_len
= i
;
926 cupsdLogMessage(CUPSD_LOG_ERROR
,
927 "Syntax error on line %d of subscriptions.conf.",
932 else if (!_cups_strcasecmp(line
, "LeaseDuration"))
938 if (value
&& isdigit(*value
& 255))
940 sub
->lease
= atoi(value
);
941 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
945 cupsdLogMessage(CUPSD_LOG_ERROR
,
946 "Syntax error on line %d of subscriptions.conf.",
951 else if (!_cups_strcasecmp(line
, "Interval"))
957 if (value
&& isdigit(*value
& 255))
958 sub
->interval
= atoi(value
);
961 cupsdLogMessage(CUPSD_LOG_ERROR
,
962 "Syntax error on line %d of subscriptions.conf.",
967 else if (!_cups_strcasecmp(line
, "ExpirationTime"))
973 if (value
&& isdigit(*value
& 255))
974 sub
->expire
= atoi(value
);
977 cupsdLogMessage(CUPSD_LOG_ERROR
,
978 "Syntax error on line %d of subscriptions.conf.",
983 else if (!_cups_strcasecmp(line
, "NextEventId"))
989 if (value
&& isdigit(*value
& 255))
990 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
993 cupsdLogMessage(CUPSD_LOG_ERROR
,
994 "Syntax error on line %d of subscriptions.conf.",
1002 * Something else we don't understand...
1005 cupsdLogMessage(CUPSD_LOG_ERROR
,
1006 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1016 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1020 cupsdSaveAllSubscriptions(void)
1022 int i
; /* Looping var */
1023 cups_file_t
*fp
; /* subscriptions.conf file */
1024 char filename
[1024]; /* subscriptions.conf filename */
1025 cupsd_subscription_t
*sub
; /* Current subscription */
1026 unsigned mask
; /* Current event mask */
1027 const char *name
; /* Current event name */
1028 int hex
; /* Non-zero if we are writing hex data */
1032 * Create the subscriptions.conf file...
1035 snprintf(filename
, sizeof(filename
), "%s/subscriptions.conf", ServerRoot
);
1037 if ((fp
= cupsdCreateConfFile(filename
, ConfigFilePerm
)) == NULL
)
1040 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1043 * Write a small header to the file...
1046 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1047 cupsFilePrintf(fp
, "# Written by cupsd\n");
1049 cupsFilePrintf(fp
, "NextSubscriptionId %d\n", NextSubscriptionId
);
1052 * Write every subscription known to the system...
1055 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1057 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1059 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1061 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1064 * Simple event list...
1067 cupsFilePrintf(fp
, "Events %s\n", name
);
1072 * Complex event list...
1075 cupsFilePuts(fp
, "Events");
1077 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1078 if (sub
->mask
& mask
)
1079 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1081 cupsFilePuts(fp
, "\n");
1085 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1087 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1089 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1091 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1093 if (sub
->user_data_len
> 0)
1095 cupsFilePuts(fp
, "UserData ");
1097 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1099 if (sub
->user_data
[i
] < ' ' ||
1100 sub
->user_data
[i
] > 0x7f ||
1101 sub
->user_data
[i
] == '<')
1105 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1109 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1115 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1119 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1124 cupsFilePuts(fp
, ">\n");
1126 cupsFilePutChar(fp
, '\n');
1129 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1130 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1131 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1132 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1134 cupsFilePuts(fp
, "</Subscription>\n");
1137 cupsdCloseCreatedConfFile(fp
, filename
);
1142 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1146 cupsdStopAllNotifiers(void)
1148 cupsd_subscription_t
*sub
; /* Current subscription */
1152 * See if we have started any notifiers...
1155 if (!NotifierStatusBuffer
)
1159 * Yes, kill any processes that are left...
1162 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1164 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1167 cupsdEndProcess(sub
->pid
, 0);
1174 * Close the status pipes...
1177 if (NotifierPipes
[0] >= 0)
1179 cupsdRemoveSelect(NotifierPipes
[0]);
1181 cupsdStatBufDelete(NotifierStatusBuffer
);
1183 close(NotifierPipes
[0]);
1184 close(NotifierPipes
[1]);
1186 NotifierPipes
[0] = -1;
1187 NotifierPipes
[1] = -1;
1188 NotifierStatusBuffer
= NULL
;
1194 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1197 static int /* O - Result of comparison */
1198 cupsd_compare_subscriptions(
1199 cupsd_subscription_t
*first
, /* I - First subscription object */
1200 cupsd_subscription_t
*second
, /* I - Second subscription object */
1201 void *unused
) /* I - Unused user data pointer */
1205 return (first
->id
- second
->id
);
1210 * 'cupsd_delete_event()' - Delete a single event...
1212 * Oldest events must be deleted first, otherwise the subscription cache
1213 * flushing code will not work properly.
1217 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1223 ippDelete(event
->attrs
);
1230 * 'cupsd_send_dbus()' - Send a DBUS notification...
1234 cupsd_send_dbus(cupsd_eventmask_t event
,/* I - Event to send */
1235 cupsd_printer_t
*dest
,/* I - Destination, if any */
1236 cupsd_job_t
*job
) /* I - Job, if any */
1238 DBusError error
; /* Error, if any */
1239 DBusMessage
*message
; /* Message to send */
1240 DBusMessageIter iter
; /* Iterator for message data */
1241 const char *what
; /* What to send */
1242 static DBusConnection
*con
= NULL
; /* Connection to DBUS server */
1246 * Figure out what to send, if anything...
1249 if (event
& CUPSD_EVENT_PRINTER_ADDED
)
1250 what
= "PrinterAdded";
1251 else if (event
& CUPSD_EVENT_PRINTER_DELETED
)
1252 what
= "PrinterRemoved";
1253 else if (event
& CUPSD_EVENT_PRINTER_CHANGED
)
1254 what
= "QueueChanged";
1255 else if (event
& CUPSD_EVENT_JOB_CREATED
)
1256 what
= "JobQueuedLocal";
1257 else if ((event
& CUPSD_EVENT_JOB_STATE
) && job
&&
1258 job
->state_value
== IPP_JOB_PROCESSING
)
1259 what
= "JobStartedLocal";
1264 * Verify connection to DBUS server...
1267 if (con
&& !dbus_connection_get_is_connected(con
))
1269 dbus_connection_unref(con
);
1275 dbus_error_init(&error
);
1277 con
= dbus_bus_get(getuid() ? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &error
);
1280 dbus_error_free(&error
);
1286 * Create and send the new message...
1289 message
= dbus_message_new_signal("/com/redhat/PrinterSpooler",
1290 "com.redhat.PrinterSpooler", what
);
1292 dbus_message_append_iter_init(message
, &iter
);
1294 dbus_message_iter_append_string(&iter
, dest
->name
);
1297 dbus_message_iter_append_uint32(&iter
, job
->id
);
1298 dbus_message_iter_append_string(&iter
, job
->username
);
1301 dbus_connection_send(con
, message
, NULL
);
1302 dbus_connection_flush(con
);
1303 dbus_message_unref(message
);
1305 #endif /* HAVE_DBUS */
1309 * 'cupsd_send_notification()' - Send a notification for the specified event.
1313 cupsd_send_notification(
1314 cupsd_subscription_t
*sub
, /* I - Subscription object */
1315 cupsd_event_t
*event
) /* I - Event to send */
1317 ipp_state_t state
; /* IPP event state */
1320 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1321 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1322 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1325 * Allocate the events array as needed...
1330 sub
->events
= cupsArrayNew3((cups_array_func_t
)NULL
, NULL
,
1331 (cups_ahash_func_t
)NULL
, 0,
1332 (cups_acopy_func_t
)NULL
,
1333 (cups_afree_func_t
)cupsd_delete_event
);
1337 cupsdLogMessage(CUPSD_LOG_CRIT
,
1338 "Unable to allocate memory for subscription #%d!",
1345 * Purge an old event as needed...
1348 if (cupsArrayCount(sub
->events
) >= MaxEvents
)
1351 * Purge the oldest event in the cache...
1354 cupsArrayRemove(sub
->events
, cupsArrayFirst(sub
->events
));
1356 sub
->first_event_id
++;
1360 * Add the event to the subscription. Since the events array is
1361 * always MaxEvents in length, and since we will have already
1362 * removed an event from the subscription cache if we hit the
1363 * event cache limit, we don't need to check for overflow here...
1366 cupsArrayAdd(sub
->events
, event
);
1369 * Deliver the event...
1377 cupsd_start_notifier(sub
);
1379 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "sub->pipe=%d", sub
->pipe
);
1384 event
->attrs
->state
= IPP_IDLE
;
1386 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1387 if (state
== IPP_ERROR
)
1390 if (state
== IPP_ERROR
)
1395 * Notifier died, try restarting it...
1398 cupsdLogMessage(CUPSD_LOG_WARN
,
1399 "Notifier for subscription %d (%s) went away, "
1401 sub
->id
, sub
->recipient
);
1402 cupsdEndProcess(sub
->pid
, 0);
1409 cupsdLogMessage(CUPSD_LOG_ERROR
,
1410 "Unable to send event for subscription %d (%s)!",
1411 sub
->id
, sub
->recipient
);
1415 * If we get this far, break out of the loop...
1423 * Bump the event sequence number...
1426 sub
->next_event_id
++;
1431 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1435 cupsd_start_notifier(
1436 cupsd_subscription_t
*sub
) /* I - Subscription object */
1438 int pid
; /* Notifier process ID */
1439 int fds
[2]; /* Pipe file descriptors */
1440 char *argv
[4], /* Command-line arguments */
1441 *envp
[MAX_ENV
], /* Environment variables */
1442 user_data
[128], /* Base-64 encoded user data */
1443 scheme
[256], /* notify-recipient-uri scheme */
1444 *ptr
, /* Pointer into scheme */
1445 command
[1024]; /* Notifier command */
1449 * Extract the scheme name from the recipient URI and point to the
1450 * notifier program...
1453 strlcpy(scheme
, sub
->recipient
, sizeof(scheme
));
1454 if ((ptr
= strchr(scheme
, ':')) != NULL
)
1457 snprintf(command
, sizeof(command
), "%s/notifier/%s", ServerBin
, scheme
);
1460 * Base-64 encode the user data...
1463 httpEncode64_2(user_data
, sizeof(user_data
), (char *)sub
->user_data
,
1464 sub
->user_data_len
);
1467 * Setup the argument array...
1471 argv
[1] = sub
->recipient
;
1472 argv
[2] = user_data
;
1476 * Setup the environment...
1479 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1482 * Create pipes as needed...
1485 if (!NotifierStatusBuffer
)
1488 * Create the status pipe...
1491 if (cupsdOpenPipe(NotifierPipes
))
1493 cupsdLogMessage(CUPSD_LOG_ERROR
,
1494 "Unable to create pipes for notifier status - %s",
1499 NotifierStatusBuffer
= cupsdStatBufNew(NotifierPipes
[0], "[Notifier]");
1501 cupsdAddSelect(NotifierPipes
[0], (cupsd_selfunc_t
)cupsd_update_notifier
,
1505 if (cupsdOpenPipe(fds
))
1507 cupsdLogMessage(CUPSD_LOG_ERROR
,
1508 "Unable to create pipes for notifier %s - %s",
1509 scheme
, strerror(errno
));
1514 * Make sure the delivery pipe is non-blocking...
1517 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1520 * Create the notifier process...
1523 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1524 -1, -1, 0, DefaultProfile
, NULL
, &pid
) < 0)
1527 * Error - can't fork!
1530 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1531 scheme
, strerror(errno
));
1533 cupsdClosePipe(fds
);
1538 * Fork successful - return the PID...
1541 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1554 * 'cupsd_update_notifier()' - Read messages from notifiers.
1558 cupsd_update_notifier(void)
1560 char message
[1024]; /* Pointer to message text */
1561 int loglevel
; /* Log level for message */
1564 while (cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1565 message
, sizeof(message
)))
1567 if (loglevel
== CUPSD_LOG_INFO
)
1568 cupsdLogMessage(CUPSD_LOG_INFO
, "%s", message
);
1570 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))