2 * "$Id: subscriptions.c 6376 2007-03-21 06:39:10Z mike $"
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * cupsdAddEvent() - Add an event to the global event cache.
27 * cupsdAddSubscription() - Add a new subscription object.
28 * cupsdDeleteAllSubscriptions() - Delete all subscriptions.
29 * cupsdDeleteSubscription() - Delete a subscription object.
30 * cupsdEventName() - Return a single event name.
31 * cupsdEventValue() - Return the event mask value for a name.
32 * cupsdExpireSubscriptions() - Expire old subscription objects.
33 * cupsdFindSubscription() - Find a subscription by ID.
34 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
35 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
36 * cupsdStopAllNotifiers() - Stop all notifier processes.
37 * cupsd_compare_subscriptions() - Compare two subscriptions.
38 * cupsd_delete_event() - Delete a single event...
39 * cupsd_send_dbus() - Send a DBUS notification...
40 * cupsd_send_notification() - Send a notification for the specified
42 * cupsd_start_notifier() - Start a notifier subprocess...
43 * cupsd_update_notifier() - Read messages from notifiers.
47 * Include necessary headers...
52 # include <dbus/dbus.h>
53 # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
54 # define dbus_message_append_iter_init dbus_message_iter_init_append
55 # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
56 # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
57 # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
58 #endif /* HAVE_DBUS */
65 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
66 cupsd_subscription_t
*second
,
68 static void cupsd_delete_event(cupsd_event_t
*event
);
70 static void cupsd_send_dbus(cupsd_eventmask_t event
, cupsd_printer_t
*dest
,
72 #endif /* HAVE_DBUS */
73 static void cupsd_send_notification(cupsd_subscription_t
*sub
,
74 cupsd_event_t
*event
);
75 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
76 static void cupsd_update_notifier(void);
80 * 'cupsdAddEvent()' - Add an event to the global event cache.
85 cupsd_eventmask_t event
, /* I - Event */
86 cupsd_printer_t
*dest
, /* I - Printer associated with event */
87 cupsd_job_t
*job
, /* I - Job associated with event */
88 const char *text
, /* I - Notification text */
89 ...) /* I - Additional arguments as needed */
91 va_list ap
; /* Pointer to additional arguments */
92 char ftext
[1024]; /* Formatted text buffer */
93 ipp_attribute_t
*attr
; /* Printer/job attribute */
94 cupsd_event_t
*temp
; /* New event pointer */
95 cupsd_subscription_t
*sub
; /* Current subscription */
98 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
99 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
100 cupsdEventName(event
), dest
, dest
? dest
->name
: "",
101 job
, job
? job
->id
: 0, text
);
104 * Keep track of events with any OS-supplied notification mechanisms...
110 cupsd_send_dbus(event
, dest
, job
);
111 #endif /* HAVE_DBUS */
114 * Return if we aren't keeping events...
119 cupsdLogMessage(CUPSD_LOG_WARN
,
120 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
121 cupsdEventName(event
), MaxEvents
);
126 * Then loop through the subscriptions and add the event to the corresponding
130 for (temp
= NULL
, sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
132 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
135 * Check if this subscription requires this event...
138 if ((sub
->mask
& event
) != 0 &&
139 (sub
->dest
== dest
|| !sub
->dest
) &&
140 (sub
->job
== job
|| !sub
->job
))
143 * Need this event, so create a new event record...
146 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
148 cupsdLogMessage(CUPSD_LOG_CRIT
,
149 "Unable to allocate memory for event - %s",
155 temp
->time
= time(NULL
);
156 temp
->attrs
= ippNew();
161 * Add common event notification attributes...
164 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
,
165 "notify-charset", NULL
, "utf-8");
167 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
,
168 "notify-natural-langugage", NULL
, "en-US");
170 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
171 "notify-subscription-id", sub
->id
);
173 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
174 "notify-sequence-number", sub
->next_event_id
);
176 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
177 "notify-subscribed-event", NULL
, cupsdEventName(event
));
179 if (sub
->user_data_len
> 0)
180 ippAddOctetString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
181 "notify-user-data", sub
->user_data
,
184 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
185 "printer-up-time", time(NULL
));
188 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
191 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
192 "notify-text", NULL
, ftext
);
197 * Add printer attributes...
200 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
,
201 "notify-printer-uri", NULL
, dest
->uri
);
203 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
204 "printer-name", NULL
, dest
->name
);
206 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
207 "printer-state", dest
->state
);
209 if (dest
->num_reasons
== 0)
210 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
211 IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
,
212 dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
214 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
215 IPP_TAG_KEYWORD
, "printer-state-reasons",
216 dest
->num_reasons
, NULL
,
217 (const char * const *)dest
->reasons
);
219 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
220 "printer-is-accepting-jobs", dest
->accepting
);
226 * Add job attributes...
229 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
230 "notify-job-id", job
->id
);
231 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
232 "job-state", job
->state_value
);
234 if ((attr
= ippFindAttribute(job
->attrs
, "job-name",
235 IPP_TAG_NAME
)) != NULL
)
236 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
237 "job-name", NULL
, attr
->values
[0].string
.text
);
239 switch (job
->state_value
)
241 case IPP_JOB_PENDING
:
242 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
243 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
244 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
247 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
248 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
253 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
254 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
255 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
256 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
257 "job-hold-until-specified");
259 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
260 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
264 case IPP_JOB_PROCESSING
:
265 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
266 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
270 case IPP_JOB_STOPPED
:
271 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
272 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
276 case IPP_JOB_CANCELED
:
277 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
278 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
279 "job-canceled-by-user");
282 case IPP_JOB_ABORTED
:
283 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
284 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
285 "aborted-by-system");
288 case IPP_JOB_COMPLETED
:
289 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
290 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
291 "job-completed-successfully");
295 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
296 "job-impressions-completed",
297 job
->sheets
? job
->sheets
->values
[0].integer
: 0);
301 * Send the notification for this subscription...
304 cupsd_send_notification(sub
, temp
);
309 cupsdSaveAllSubscriptions();
311 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...",
312 cupsdEventName(event
));
317 * 'cupsdAddSubscription()' - Add a new subscription object.
320 cupsd_subscription_t
* /* O - New subscription object */
321 cupsdAddSubscription(
322 unsigned mask
, /* I - Event mask */
323 cupsd_printer_t
*dest
, /* I - Printer, if any */
324 cupsd_job_t
*job
, /* I - Job, if any */
325 const char *uri
, /* I - notify-recipient-uri, if any */
326 int sub_id
) /* I - notify-subscription-id or 0 */
328 cupsd_subscription_t
*temp
; /* New subscription object */
331 cupsdLogMessage(CUPSD_LOG_DEBUG
,
332 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
334 mask
, dest
, dest
? dest
->name
: "", job
, job
? job
->id
: 0,
338 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
343 cupsdLogMessage(CUPSD_LOG_CRIT
,
344 "Unable to allocate memory for subscriptions - %s",
350 * Limit the number of subscriptions...
353 if (cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
357 * Allocate memory for this subscription...
360 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
362 cupsdLogMessage(CUPSD_LOG_CRIT
,
363 "Unable to allocate memory for subscription object - %s",
369 * Fill in common data...
376 if (sub_id
>= NextSubscriptionId
)
377 NextSubscriptionId
= sub_id
+ 1;
381 temp
->id
= NextSubscriptionId
;
383 NextSubscriptionId
++;
390 temp
->first_event_id
= 1;
391 temp
->next_event_id
= 1;
393 cupsdSetString(&(temp
->recipient
), uri
);
396 * Add the subscription to the array...
399 cupsArrayAdd(Subscriptions
, temp
);
406 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
410 cupsdDeleteAllSubscriptions(void)
412 cupsd_subscription_t
*sub
; /* Subscription */
418 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
420 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
421 cupsdDeleteSubscription(sub
, 0);
423 cupsArrayDelete(Subscriptions
);
424 Subscriptions
= NULL
;
429 * 'cupsdDeleteSubscription()' - Delete a subscription object.
433 cupsdDeleteSubscription(
434 cupsd_subscription_t
*sub
, /* I - Subscription object */
435 int update
) /* I - 1 = update subscriptions.conf */
437 int i
; /* Looping var */
441 * Close the pipe to the notifier as needed...
448 * Remove subscription from array...
451 cupsArrayRemove(Subscriptions
, sub
);
457 cupsdClearString(&(sub
->owner
));
458 cupsdClearString(&(sub
->recipient
));
462 for (i
= 0; i
< sub
->num_events
; i
++)
463 cupsd_delete_event(sub
->events
[i
]);
471 * Update the subscriptions as needed...
475 cupsdSaveAllSubscriptions();
480 * 'cupsdEventName()' - Return a single event name.
483 const char * /* O - Event name */
485 cupsd_eventmask_t event
) /* I - Event value */
492 case CUPSD_EVENT_PRINTER_RESTARTED
:
493 return ("printer-restarted");
495 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
496 return ("printer-shutdown");
498 case CUPSD_EVENT_PRINTER_STOPPED
:
499 return ("printer-stopped");
501 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
502 return ("printer-finishings-changed");
504 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
505 return ("printer-media-changed");
507 case CUPSD_EVENT_PRINTER_ADDED
:
508 return ("printer-added");
510 case CUPSD_EVENT_PRINTER_DELETED
:
511 return ("printer-deleted");
513 case CUPSD_EVENT_PRINTER_MODIFIED
:
514 return ("printer-modified");
516 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
517 return ("printer-state-changed");
519 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
520 return ("printer-config-changed");
522 case CUPSD_EVENT_PRINTER_CHANGED
:
523 return ("printer-changed");
525 case CUPSD_EVENT_JOB_CREATED
:
526 return ("job-created");
528 case CUPSD_EVENT_JOB_COMPLETED
:
529 return ("job-completed");
531 case CUPSD_EVENT_JOB_STOPPED
:
532 return ("job-stopped");
534 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
535 return ("job-config-changed");
537 case CUPSD_EVENT_JOB_PROGRESS
:
538 return ("job-progress");
540 case CUPSD_EVENT_JOB_STATE
:
541 return ("job-state");
543 case CUPSD_EVENT_JOB_STATE_CHANGED
:
544 return ("job-state-changed");
546 case CUPSD_EVENT_SERVER_RESTARTED
:
547 return ("server-restarted");
549 case CUPSD_EVENT_SERVER_STARTED
:
550 return ("server-started");
552 case CUPSD_EVENT_SERVER_STOPPED
:
553 return ("server-stopped");
555 case CUPSD_EVENT_SERVER_AUDIT
:
556 return ("server-audit");
558 case CUPSD_EVENT_ALL
:
565 * 'cupsdEventValue()' - Return the event mask value for a name.
568 cupsd_eventmask_t
/* O - Event mask value */
569 cupsdEventValue(const char *name
) /* I - Name of event */
571 if (!strcmp(name
, "all"))
572 return (CUPSD_EVENT_ALL
);
573 else if (!strcmp(name
, "printer-restarted"))
574 return (CUPSD_EVENT_PRINTER_RESTARTED
);
575 else if (!strcmp(name
, "printer-shutdown"))
576 return (CUPSD_EVENT_PRINTER_SHUTDOWN
);
577 else if (!strcmp(name
, "printer-stopped"))
578 return (CUPSD_EVENT_PRINTER_STOPPED
);
579 else if (!strcmp(name
, "printer-finishings-changed"))
580 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
);
581 else if (!strcmp(name
, "printer-media-changed"))
582 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED
);
583 else if (!strcmp(name
, "printer-added"))
584 return (CUPSD_EVENT_PRINTER_ADDED
);
585 else if (!strcmp(name
, "printer-deleted"))
586 return (CUPSD_EVENT_PRINTER_DELETED
);
587 else if (!strcmp(name
, "printer-modified"))
588 return (CUPSD_EVENT_PRINTER_MODIFIED
);
589 else if (!strcmp(name
, "printer-state-changed"))
590 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
591 else if (!strcmp(name
, "printer-config-changed"))
592 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
593 else if (!strcmp(name
, "printer-changed"))
594 return (CUPSD_EVENT_PRINTER_CHANGED
);
595 else if (!strcmp(name
, "job-state"))
596 return (CUPSD_EVENT_JOB_STATE
);
597 else if (!strcmp(name
, "job-created"))
598 return (CUPSD_EVENT_JOB_CREATED
);
599 else if (!strcmp(name
, "job-completed"))
600 return (CUPSD_EVENT_JOB_COMPLETED
);
601 else if (!strcmp(name
, "job-stopped"))
602 return (CUPSD_EVENT_JOB_STOPPED
);
603 else if (!strcmp(name
, "job-config-changed"))
604 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
605 else if (!strcmp(name
, "job-progress"))
606 return (CUPSD_EVENT_JOB_PROGRESS
);
607 else if (!strcmp(name
, "job-state-changed"))
608 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
609 else if (!strcmp(name
, "server-restarted"))
610 return (CUPSD_EVENT_SERVER_RESTARTED
);
611 else if (!strcmp(name
, "server-started"))
612 return (CUPSD_EVENT_SERVER_STARTED
);
613 else if (!strcmp(name
, "server-stopped"))
614 return (CUPSD_EVENT_SERVER_STOPPED
);
615 else if (!strcmp(name
, "server-audit"))
616 return (CUPSD_EVENT_SERVER_AUDIT
);
618 return (CUPSD_EVENT_NONE
);
623 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
627 cupsdExpireSubscriptions(
628 cupsd_printer_t
*dest
, /* I - Printer, if any */
629 cupsd_job_t
*job
) /* I - Job, if any */
631 cupsd_subscription_t
*sub
; /* Current subscription */
632 int update
; /* Update subscriptions.conf? */
633 time_t curtime
; /* Current time */
636 curtime
= time(NULL
);
639 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
641 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
642 if ((!sub
->job
&& !dest
&& sub
->expire
&& sub
->expire
<= curtime
) ||
643 (dest
&& sub
->dest
== dest
) ||
644 (job
&& sub
->job
== job
))
646 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...",
649 cupsdDeleteSubscription(sub
, 0);
655 cupsdSaveAllSubscriptions();
660 * 'cupsdFindSubscription()' - Find a subscription by ID.
663 cupsd_subscription_t
* /* O - Subscription object */
664 cupsdFindSubscription(int id
) /* I - Subscription ID */
666 cupsd_subscription_t sub
; /* Subscription template */
671 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
676 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
680 cupsdLoadAllSubscriptions(void)
682 int i
; /* Looping var */
683 cups_file_t
*fp
; /* subscriptions.conf file */
684 int linenum
; /* Current line number */
685 char line
[1024], /* Line from file */
686 *value
, /* Pointer to value */
687 *valueptr
; /* Pointer into value */
688 cupsd_subscription_t
*sub
; /* Current subscription */
689 int hex
; /* Non-zero if reading hex data */
690 int delete_sub
; /* Delete subscription? */
694 * Open the subscriptions.conf file...
697 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
698 if ((fp
= cupsFileOpen(line
, "r")) == NULL
)
701 cupsdLogMessage(CUPSD_LOG_ERROR
,
702 "LoadAllSubscriptions: Unable to open %s - %s", line
,
708 * Read all of the lines from the file...
715 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
717 if (!strcasecmp(line
, "NextSubscriptionId") && value
)
720 * NextSubscriptionId NNN
724 if (i
>= NextSubscriptionId
&& i
> 0)
725 NextSubscriptionId
= i
;
727 else if (!strcasecmp(line
, "<Subscription"))
733 if (!sub
&& value
&& isdigit(value
[0] & 255))
735 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
740 cupsdLogMessage(CUPSD_LOG_ERROR
,
741 "Syntax error on line %d of subscriptions.conf.",
746 else if (!strcasecmp(line
, "</Subscription>"))
750 cupsdLogMessage(CUPSD_LOG_ERROR
,
751 "Syntax error on line %d of subscriptions.conf.",
757 cupsdDeleteSubscription(sub
, 0);
764 cupsdLogMessage(CUPSD_LOG_ERROR
,
765 "Syntax error on line %d of subscriptions.conf.",
769 else if (!strcasecmp(line
, "Events"))
773 * Events name name name ...
778 cupsdLogMessage(CUPSD_LOG_ERROR
,
779 "Syntax error on line %d of subscriptions.conf.",
787 * Separate event names...
790 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
792 while (isspace(*valueptr
& 255))
796 * See if the name exists...
799 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
801 cupsdLogMessage(CUPSD_LOG_ERROR
,
802 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
810 else if (!strcasecmp(line
, "Owner"))
817 cupsdSetString(&sub
->owner
, value
);
820 cupsdLogMessage(CUPSD_LOG_ERROR
,
821 "Syntax error on line %d of subscriptions.conf.",
826 else if (!strcasecmp(line
, "Recipient"))
833 cupsdSetString(&sub
->recipient
, value
);
836 cupsdLogMessage(CUPSD_LOG_ERROR
,
837 "Syntax error on line %d of subscriptions.conf.",
842 else if (!strcasecmp(line
, "JobId"))
848 if (value
&& isdigit(*value
& 255))
850 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
852 cupsdLogMessage(CUPSD_LOG_ERROR
,
853 "Job %s not found on line %d of subscriptions.conf.",
860 cupsdLogMessage(CUPSD_LOG_ERROR
,
861 "Syntax error on line %d of subscriptions.conf.",
866 else if (!strcasecmp(line
, "PrinterName"))
874 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
876 cupsdLogMessage(CUPSD_LOG_ERROR
,
877 "Printer \'%s\' not found on line %d of subscriptions.conf.",
884 cupsdLogMessage(CUPSD_LOG_ERROR
,
885 "Syntax error on line %d of subscriptions.conf.",
890 else if (!strcasecmp(line
, "UserData"))
893 * UserData encoded-string
898 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
900 if (*valueptr
== '<' && !hex
)
908 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
910 if (isdigit(valueptr
[0]))
911 sub
->user_data
[i
] = (valueptr
[0] - '0') << 4;
913 sub
->user_data
[i
] = (tolower(valueptr
[0]) - 'a' + 10) << 4;
915 if (isdigit(valueptr
[1]))
916 sub
->user_data
[i
] |= valueptr
[1] - '0';
918 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
922 if (*valueptr
== '>')
932 sub
->user_data
[i
] = *valueptr
++;
937 cupsdLogMessage(CUPSD_LOG_ERROR
,
938 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
942 sub
->user_data_len
= i
;
946 cupsdLogMessage(CUPSD_LOG_ERROR
,
947 "Syntax error on line %d of subscriptions.conf.",
952 else if (!strcasecmp(line
, "LeaseDuration"))
958 if (value
&& isdigit(*value
& 255))
960 sub
->lease
= atoi(value
);
961 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
965 cupsdLogMessage(CUPSD_LOG_ERROR
,
966 "Syntax error on line %d of subscriptions.conf.",
971 else if (!strcasecmp(line
, "Interval"))
977 if (value
&& isdigit(*value
& 255))
978 sub
->interval
= atoi(value
);
981 cupsdLogMessage(CUPSD_LOG_ERROR
,
982 "Syntax error on line %d of subscriptions.conf.",
987 else if (!strcasecmp(line
, "ExpirationTime"))
993 if (value
&& isdigit(*value
& 255))
994 sub
->expire
= atoi(value
);
997 cupsdLogMessage(CUPSD_LOG_ERROR
,
998 "Syntax error on line %d of subscriptions.conf.",
1003 else if (!strcasecmp(line
, "NextEventId"))
1009 if (value
&& isdigit(*value
& 255))
1010 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
1013 cupsdLogMessage(CUPSD_LOG_ERROR
,
1014 "Syntax error on line %d of subscriptions.conf.",
1022 * Something else we don't understand...
1025 cupsdLogMessage(CUPSD_LOG_ERROR
,
1026 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1036 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1040 cupsdSaveAllSubscriptions(void)
1042 int i
; /* Looping var */
1043 cups_file_t
*fp
; /* subscriptions.conf file */
1044 char temp
[1024]; /* Temporary string */
1045 char backup
[1024]; /* subscriptions.conf.O file */
1046 cupsd_subscription_t
*sub
; /* Current subscription */
1047 time_t curtime
; /* Current time */
1048 struct tm
*curdate
; /* Current date */
1049 unsigned mask
; /* Current event mask */
1050 const char *name
; /* Current event name */
1051 int hex
; /* Non-zero if we are writing hex data */
1055 * Create the subscriptions.conf file...
1058 snprintf(temp
, sizeof(temp
), "%s/subscriptions.conf", ServerRoot
);
1059 snprintf(backup
, sizeof(backup
), "%s/subscriptions.conf.O", ServerRoot
);
1061 if (rename(temp
, backup
))
1063 if (errno
!= ENOENT
)
1064 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to backup subscriptions.conf - %s",
1068 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1070 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to save subscriptions.conf - %s",
1073 if (rename(backup
, temp
))
1074 cupsdLogMessage(CUPSD_LOG_ERROR
,
1075 "Unable to restore subscriptions.conf - %s",
1080 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1083 * Restrict access to the file...
1086 fchown(cupsFileNumber(fp
), getuid(), Group
);
1087 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1090 * Write a small header to the file...
1093 curtime
= time(NULL
);
1094 curdate
= localtime(&curtime
);
1095 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1097 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1098 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1100 cupsFilePrintf(fp
, "NextSubscriptionId %d\n", NextSubscriptionId
);
1103 * Write every subscription known to the system...
1106 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1108 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1110 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1112 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1115 * Simple event list...
1118 cupsFilePrintf(fp
, "Events %s\n", name
);
1123 * Complex event list...
1126 cupsFilePuts(fp
, "Events");
1128 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1129 if (sub
->mask
& mask
)
1130 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1132 cupsFilePuts(fp
, "\n");
1136 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1138 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1140 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1142 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1144 if (sub
->user_data_len
> 0)
1146 cupsFilePuts(fp
, "UserData ");
1148 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1150 if (sub
->user_data
[i
] < ' ' ||
1151 sub
->user_data
[i
] > 0x7f ||
1152 sub
->user_data
[i
] == '<')
1156 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1160 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1166 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1170 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1175 cupsFilePuts(fp
, ">\n");
1177 cupsFilePutChar(fp
, '\n');
1180 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1181 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1182 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1183 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1185 cupsFilePuts(fp
, "</Subscription>\n");
1193 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1197 cupsdStopAllNotifiers(void)
1199 cupsd_subscription_t
*sub
; /* Current subscription */
1203 * See if we have started any notifiers...
1206 if (!NotifierStatusBuffer
)
1210 * Yes, kill any processes that are left...
1213 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1215 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1218 cupsdEndProcess(sub
->pid
, 0);
1225 * Close the status pipes...
1228 if (NotifierPipes
[0] >= 0)
1230 cupsdRemoveSelect(NotifierPipes
[0]);
1232 cupsdStatBufDelete(NotifierStatusBuffer
);
1234 close(NotifierPipes
[0]);
1235 close(NotifierPipes
[1]);
1237 NotifierPipes
[0] = -1;
1238 NotifierPipes
[1] = -1;
1239 NotifierStatusBuffer
= NULL
;
1245 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1248 static int /* O - Result of comparison */
1249 cupsd_compare_subscriptions(
1250 cupsd_subscription_t
*first
, /* I - First subscription object */
1251 cupsd_subscription_t
*second
, /* I - Second subscription object */
1252 void *unused
) /* I - Unused user data pointer */
1256 return (first
->id
- second
->id
);
1261 * 'cupsd_delete_event()' - Delete a single event...
1263 * Oldest events must be deleted first, otherwise the subscription cache
1264 * flushing code will not work properly.
1268 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1274 ippDelete(event
->attrs
);
1281 * 'cupsd_send_dbus()' - Send a DBUS notification...
1285 cupsd_send_dbus(cupsd_eventmask_t event
,/* I - Event to send */
1286 cupsd_printer_t
*dest
,/* I - Destination, if any */
1287 cupsd_job_t
*job
) /* I - Job, if any */
1289 DBusError error
; /* Error, if any */
1290 DBusMessage
*message
; /* Message to send */
1291 DBusMessageIter iter
; /* Iterator for message data */
1292 const char *what
; /* What to send */
1293 static DBusConnection
*con
= NULL
; /* Connection to DBUS server */
1297 * Figure out what to send, if anything...
1300 if (event
& CUPSD_EVENT_PRINTER_ADDED
)
1301 what
= "PrinterAdded";
1302 else if (event
& CUPSD_EVENT_PRINTER_DELETED
)
1303 what
= "PrinterRemoved";
1304 else if (event
& CUPSD_EVENT_PRINTER_CHANGED
)
1305 what
= "QueueChanged";
1306 else if (event
& CUPSD_EVENT_JOB_CREATED
)
1307 what
= "JobQueuedLocal";
1308 else if ((event
& CUPSD_EVENT_JOB_STATE
) && job
&&
1309 job
->state_value
== IPP_JOB_PROCESSING
)
1310 what
= "JobStartedLocal";
1315 * Verify connection to DBUS server...
1318 if (con
&& !dbus_connection_get_is_connected(con
))
1320 dbus_connection_unref(con
);
1326 dbus_error_init(&error
);
1328 con
= dbus_bus_get(getuid() ? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &error
);
1331 dbus_error_free(&error
);
1337 * Create and send the new message...
1340 message
= dbus_message_new_signal("/com/redhat/PrinterSpooler",
1341 "com.redhat.PrinterSpooler", what
);
1343 dbus_message_append_iter_init(message
, &iter
);
1345 dbus_message_iter_append_string(&iter
, dest
->name
);
1348 dbus_message_iter_append_uint32(&iter
, job
->id
);
1349 dbus_message_iter_append_string(&iter
, job
->username
);
1352 dbus_connection_send(con
, message
, NULL
);
1353 dbus_connection_flush(con
);
1354 dbus_message_unref(message
);
1356 #endif /* HAVE_DBUS */
1360 * 'cupsd_send_notification()' - Send a notification for the specified event.
1364 cupsd_send_notification(
1365 cupsd_subscription_t
*sub
, /* I - Subscription object */
1366 cupsd_event_t
*event
) /* I - Event to send */
1368 ipp_state_t state
; /* IPP event state */
1371 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1372 "cupsd_send_notification(sub=%p(%d), event=%p(%s))\n",
1373 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1376 * Allocate the events array as needed...
1381 sub
->events
= calloc(MaxEvents
, sizeof(cupsd_event_t
*));
1385 cupsdLogMessage(CUPSD_LOG_CRIT
,
1386 "Unable to allocate memory for subscription #%d!",
1393 * Purge an old event as needed...
1396 if (sub
->num_events
>= MaxEvents
)
1399 * Purge the oldest event in the cache...
1402 cupsd_delete_event(sub
->events
[0]);
1405 sub
->first_event_id
++;
1407 memmove(sub
->events
, sub
->events
+ 1,
1408 sub
->num_events
* sizeof(cupsd_event_t
*));
1412 * Add the event to the subscription. Since the events array is
1413 * always MaxEvents in length, and since we will have already
1414 * removed an event from the subscription cache if we hit the
1415 * event cache limit, we don't need to check for overflow here...
1418 sub
->events
[sub
->num_events
] = event
;
1422 * Deliver the event...
1430 cupsd_start_notifier(sub
);
1432 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "sub->pipe=%d", sub
->pipe
);
1437 event
->attrs
->state
= IPP_IDLE
;
1439 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1440 if (state
== IPP_ERROR
)
1443 if (state
== IPP_ERROR
)
1448 * Notifier died, try restarting it...
1451 cupsdLogMessage(CUPSD_LOG_WARN
,
1452 "Notifier for subscription %d (%s) went away, "
1454 sub
->id
, sub
->recipient
);
1455 cupsdEndProcess(sub
->pid
, 0);
1462 cupsdLogMessage(CUPSD_LOG_ERROR
,
1463 "Unable to send event for subscription %d (%s)!",
1464 sub
->id
, sub
->recipient
);
1468 * If we get this far, break out of the loop...
1476 * Bump the event sequence number...
1479 sub
->next_event_id
++;
1484 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1488 cupsd_start_notifier(
1489 cupsd_subscription_t
*sub
) /* I - Subscription object */
1491 int pid
; /* Notifier process ID */
1492 int fds
[2]; /* Pipe file descriptors */
1493 char *argv
[4], /* Command-line arguments */
1494 *envp
[MAX_ENV
], /* Environment variables */
1495 user_data
[128], /* Base-64 encoded user data */
1496 scheme
[256], /* notify-recipient-uri scheme */
1497 *ptr
, /* Pointer into scheme */
1498 command
[1024]; /* Notifier command */
1502 * Extract the scheme name from the recipient URI and point to the
1503 * notifier program...
1506 strlcpy(scheme
, sub
->recipient
, sizeof(scheme
));
1507 if ((ptr
= strchr(scheme
, ':')) != NULL
)
1510 snprintf(command
, sizeof(command
), "%s/notifier/%s", ServerBin
, scheme
);
1513 * Base-64 encode the user data...
1516 httpEncode64_2(user_data
, sizeof(user_data
), (char *)sub
->user_data
,
1517 sub
->user_data_len
);
1520 * Setup the argument array...
1524 argv
[1] = sub
->recipient
;
1525 argv
[2] = user_data
;
1529 * Setup the environment...
1532 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1535 * Create pipes as needed...
1538 if (!NotifierStatusBuffer
)
1541 * Create the status pipe...
1544 if (cupsdOpenPipe(NotifierPipes
))
1546 cupsdLogMessage(CUPSD_LOG_ERROR
,
1547 "Unable to create pipes for notifier status - %s",
1552 NotifierStatusBuffer
= cupsdStatBufNew(NotifierPipes
[0], "[Notifier]");
1554 cupsdAddSelect(NotifierPipes
[0], (cupsd_selfunc_t
)cupsd_update_notifier
,
1558 if (cupsdOpenPipe(fds
))
1560 cupsdLogMessage(CUPSD_LOG_ERROR
,
1561 "Unable to create pipes for notifier %s - %s",
1562 scheme
, strerror(errno
));
1567 * Make sure the delivery pipe is non-blocking...
1570 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1573 * Create the notifier process...
1576 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1577 -1, -1, 0, &pid
) < 0)
1580 * Error - can't fork!
1583 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1584 scheme
, strerror(errno
));
1586 cupsdClosePipe(fds
);
1591 * Fork successful - return the PID...
1594 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1607 * 'cupsd_update_notifier()' - Read messages from notifiers.
1611 cupsd_update_notifier(void)
1613 char message
[1024]; /* Pointer to message text */
1614 int loglevel
; /* Log level for message */
1617 while (cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1618 message
, sizeof(message
)))
1619 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))
1625 * End of "$Id: subscriptions.c 6376 2007-03-21 06:39:10Z mike $".