2 * "$Id: subscriptions.c 181 2006-06-22 20:01:18Z jlovell $"
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
6 * Copyright 1997-2006 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 * cupsdUpdateNotifierStatus() - Read messages from notifiers.
38 * cupsd_compare_subscriptions() - Compare two subscriptions.
39 * cupsd_delete_event() - Delete a single event...
40 * cupsd_send_dbus() - Send a DBUS notification...
41 * cupsd_send_notification() - Send a notification for the specified
43 * cupsd_start_notifier() - Start a notifier subprocess...
47 * Include necessary headers...
52 # include <dbus/dbus.h>
53 #endif /* HAVE_DBUS */
60 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
61 cupsd_subscription_t
*second
,
63 static void cupsd_delete_event(cupsd_event_t
*event
);
65 static void cupsd_send_dbus(cupsd_eventmask_t event
, cupsd_printer_t
*dest
,
67 #endif /* HAVE_DBUS */
68 static void cupsd_send_notification(cupsd_subscription_t
*sub
,
69 cupsd_event_t
*event
);
70 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
74 * 'cupsdAddEvent()' - Add an event to the global event cache.
79 cupsd_eventmask_t event
, /* I - Event */
80 cupsd_printer_t
*dest
, /* I - Printer associated with event */
81 cupsd_job_t
*job
, /* I - Job associated with event */
82 const char *text
, /* I - Notification text */
83 ...) /* I - Additional arguments as needed */
85 va_list ap
; /* Pointer to additional arguments */
86 char ftext
[1024]; /* Formatted text buffer */
87 ipp_attribute_t
*attr
; /* Printer/job attribute */
88 cupsd_event_t
*temp
; /* New event pointer */
89 cupsd_subscription_t
*sub
; /* Current subscription */
93 * Keep track of events with any OS-supplied notification mechanisms...
99 cupsd_send_dbus(event
, dest
, job
);
100 #endif /* HAVE_DBUS */
103 * Return if we aren't keeping events...
108 cupsdLogMessage(CUPSD_LOG_WARN
,
109 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
110 cupsdEventName(event
), MaxEvents
);
115 * Then loop through the subscriptions and add the event to the corresponding
119 for (temp
= NULL
, sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
121 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
124 * Check if this subscription requires this event...
127 if ((sub
->mask
& event
) != 0 &&
128 (sub
->dest
== dest
|| !sub
->dest
) &&
129 (sub
->job
== job
|| !sub
->job
))
132 * Need this event, so create a new event record...
135 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
137 cupsdLogMessage(CUPSD_LOG_CRIT
,
138 "Unable to allocate memory for event - %s",
144 temp
->time
= time(NULL
);
145 temp
->attrs
= ippNew();
150 * Add common event notification attributes...
153 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_CHARSET
,
154 "notify-charset", NULL
, "utf-8");
156 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_LANGUAGE
,
157 "notify-natural-langugage", NULL
, "en-US");
159 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
160 "notify-subscription-id", sub
->id
);
162 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
163 "notify-sequence-number", sub
->next_event_id
);
165 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
166 "notify-subscribed-event", NULL
, cupsdEventName(event
));
168 if (sub
->user_data_len
> 0)
169 ippAddOctetString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
170 "notify-user-data", sub
->user_data
,
173 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
174 "printer-up-time", time(NULL
));
177 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
180 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
181 "notify-text", NULL
, ftext
);
186 * Add printer attributes...
189 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
,
190 "notify-printer-uri", NULL
, dest
->uri
);
192 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
193 "printer-name", NULL
, dest
->name
);
195 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
196 "printer-state", dest
->state
);
198 if (dest
->num_reasons
== 0)
199 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
200 IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
,
201 dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
203 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
204 IPP_TAG_KEYWORD
, "printer-state-reasons",
205 dest
->num_reasons
, NULL
,
206 (const char * const *)dest
->reasons
);
208 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
209 "printer-is-accepting-jobs", dest
->accepting
);
215 * Add job attributes...
218 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
219 "notify-job-id", job
->id
);
220 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
221 "job-state", job
->state_value
);
223 if ((attr
= ippFindAttribute(job
->attrs
, "job-name",
224 IPP_TAG_NAME
)) != NULL
)
225 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
226 "job-name", NULL
, attr
->values
[0].string
.text
);
228 switch (job
->state_value
)
230 case IPP_JOB_PENDING
:
231 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
232 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
233 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
236 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
237 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
242 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
243 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
244 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
245 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
246 "job-hold-until-specified");
248 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
249 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
253 case IPP_JOB_PROCESSING
:
254 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
255 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
259 case IPP_JOB_STOPPED
:
260 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
261 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
265 case IPP_JOB_CANCELLED
:
266 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
267 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
268 "job-canceled-by-user");
271 case IPP_JOB_ABORTED
:
272 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
273 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
274 "aborted-by-system");
277 case IPP_JOB_COMPLETED
:
278 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
279 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
280 "job-completed-successfully");
284 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
285 "job-impressions-completed",
286 job
->sheets
? job
->sheets
->values
[0].integer
: 0);
289 * Send the notification for this subscription...
292 cupsd_send_notification(sub
, temp
);
298 cupsdSaveAllSubscriptions();
300 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...",
301 cupsdEventName(event
));
306 * 'cupsdAddSubscription()' - Add a new subscription object.
309 cupsd_subscription_t
* /* O - New subscription object */
310 cupsdAddSubscription(
311 unsigned mask
, /* I - Event mask */
312 cupsd_printer_t
*dest
, /* I - Printer, if any */
313 cupsd_job_t
*job
, /* I - Job, if any */
314 const char *uri
, /* I - notify-recipient-uri, if any */
315 int sub_id
) /* I - notify-subscription-id or 0 */
317 cupsd_subscription_t
*temp
; /* New subscription object */
320 cupsdLogMessage(CUPSD_LOG_DEBUG
,
321 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
323 mask
, dest
, dest
? dest
->name
: "", job
, job
? job
->id
: 0,
327 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
332 cupsdLogMessage(CUPSD_LOG_CRIT
,
333 "Unable to allocate memory for subscriptions - %s",
339 * Limit the number of subscriptions...
342 if (cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
346 * Allocate memory for this subscription...
349 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
351 cupsdLogMessage(CUPSD_LOG_CRIT
,
352 "Unable to allocate memory for subscription object - %s",
358 * Fill in common data...
365 if (sub_id
>= NextSubscriptionId
)
366 NextSubscriptionId
= sub_id
+ 1;
370 temp
->id
= NextSubscriptionId
;
372 NextSubscriptionId
++;
379 temp
->first_event_id
= 1;
380 temp
->next_event_id
= 1;
382 cupsdSetString(&(temp
->recipient
), uri
);
385 * Add the subscription to the array...
388 cupsArrayAdd(Subscriptions
, temp
);
395 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
399 cupsdDeleteAllSubscriptions(void)
401 cupsd_subscription_t
*sub
; /* Subscription */
407 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
409 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
410 cupsdDeleteSubscription(sub
, 0);
412 cupsArrayDelete(Subscriptions
);
413 Subscriptions
= NULL
;
418 * 'cupsdDeleteSubscription()' - Delete a subscription object.
422 cupsdDeleteSubscription(
423 cupsd_subscription_t
*sub
, /* I - Subscription object */
424 int update
) /* I - 1 = update subscriptions.conf */
426 int i
; /* Looping var */
430 * Close the pipe to the notifier as needed...
437 * Remove subscription from array...
440 cupsArrayRemove(Subscriptions
, sub
);
446 cupsdClearString(&(sub
->owner
));
447 cupsdClearString(&(sub
->recipient
));
451 for (i
= 0; i
< sub
->num_events
; i
++)
452 cupsd_delete_event(sub
->events
[i
]);
460 * Update the subscriptions as needed...
464 cupsdSaveAllSubscriptions();
469 * 'cupsdEventName()' - Return a single event name.
472 const char * /* O - Event name */
474 cupsd_eventmask_t event
) /* I - Event value */
481 case CUPSD_EVENT_PRINTER_RESTARTED
:
482 return ("printer-restarted");
484 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
485 return ("printer-shutdown");
487 case CUPSD_EVENT_PRINTER_STOPPED
:
488 return ("printer-stopped");
490 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
491 return ("printer-finishings-changed");
493 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
494 return ("printer-media-changed");
496 case CUPSD_EVENT_PRINTER_ADDED
:
497 return ("printer-added");
499 case CUPSD_EVENT_PRINTER_DELETED
:
500 return ("printer-deleted");
502 case CUPSD_EVENT_PRINTER_MODIFIED
:
503 return ("printer-modified");
505 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
506 return ("printer-state-changed");
508 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
509 return ("printer-config-changed");
511 case CUPSD_EVENT_PRINTER_CHANGED
:
512 return ("printer-changed");
514 case CUPSD_EVENT_JOB_CREATED
:
515 return ("job-created");
517 case CUPSD_EVENT_JOB_COMPLETED
:
518 return ("job-completed");
520 case CUPSD_EVENT_JOB_STOPPED
:
521 return ("job-stopped");
523 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
524 return ("job-config-changed");
526 case CUPSD_EVENT_JOB_PROGRESS
:
527 return ("job-progress");
529 case CUPSD_EVENT_JOB_STATE
:
530 return ("job-state");
532 case CUPSD_EVENT_JOB_STATE_CHANGED
:
533 return ("job-state-changed");
535 case CUPSD_EVENT_SERVER_RESTARTED
:
536 return ("server-restarted");
538 case CUPSD_EVENT_SERVER_STARTED
:
539 return ("server-started");
541 case CUPSD_EVENT_SERVER_STOPPED
:
542 return ("server-stopped");
544 case CUPSD_EVENT_SERVER_AUDIT
:
545 return ("server-audit");
547 case CUPSD_EVENT_ALL
:
554 * 'cupsdEventValue()' - Return the event mask value for a name.
557 cupsd_eventmask_t
/* O - Event mask value */
558 cupsdEventValue(const char *name
) /* I - Name of event */
560 if (!strcmp(name
, "all"))
561 return (CUPSD_EVENT_ALL
);
562 else if (!strcmp(name
, "printer-restarted"))
563 return (CUPSD_EVENT_PRINTER_RESTARTED
);
564 else if (!strcmp(name
, "printer-shutdown"))
565 return (CUPSD_EVENT_PRINTER_SHUTDOWN
);
566 else if (!strcmp(name
, "printer-stopped"))
567 return (CUPSD_EVENT_PRINTER_STOPPED
);
568 else if (!strcmp(name
, "printer-finishings-changed"))
569 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
);
570 else if (!strcmp(name
, "printer-media-changed"))
571 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED
);
572 else if (!strcmp(name
, "printer-added"))
573 return (CUPSD_EVENT_PRINTER_ADDED
);
574 else if (!strcmp(name
, "printer-deleted"))
575 return (CUPSD_EVENT_PRINTER_DELETED
);
576 else if (!strcmp(name
, "printer-modified"))
577 return (CUPSD_EVENT_PRINTER_MODIFIED
);
578 else if (!strcmp(name
, "printer-state-changed"))
579 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
580 else if (!strcmp(name
, "printer-config-changed"))
581 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
582 else if (!strcmp(name
, "printer-changed"))
583 return (CUPSD_EVENT_PRINTER_CHANGED
);
584 else if (!strcmp(name
, "job-state"))
585 return (CUPSD_EVENT_JOB_STATE
);
586 else if (!strcmp(name
, "job-created"))
587 return (CUPSD_EVENT_JOB_CREATED
);
588 else if (!strcmp(name
, "job-completed"))
589 return (CUPSD_EVENT_JOB_COMPLETED
);
590 else if (!strcmp(name
, "job-stopped"))
591 return (CUPSD_EVENT_JOB_STOPPED
);
592 else if (!strcmp(name
, "job-config-changed"))
593 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
594 else if (!strcmp(name
, "job-progress"))
595 return (CUPSD_EVENT_JOB_PROGRESS
);
596 else if (!strcmp(name
, "job-state-changed"))
597 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
598 else if (!strcmp(name
, "server-restarted"))
599 return (CUPSD_EVENT_SERVER_RESTARTED
);
600 else if (!strcmp(name
, "server-started"))
601 return (CUPSD_EVENT_SERVER_STARTED
);
602 else if (!strcmp(name
, "server-stopped"))
603 return (CUPSD_EVENT_SERVER_STOPPED
);
604 else if (!strcmp(name
, "server-audit"))
605 return (CUPSD_EVENT_SERVER_AUDIT
);
607 return (CUPSD_EVENT_NONE
);
612 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
616 cupsdExpireSubscriptions(
617 cupsd_printer_t
*dest
, /* I - Printer, if any */
618 cupsd_job_t
*job
) /* I - Job, if any */
620 cupsd_subscription_t
*sub
; /* Current subscription */
621 int update
; /* Update subscriptions.conf? */
622 time_t curtime
; /* Current time */
625 curtime
= time(NULL
);
628 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
630 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
631 if ((!sub
->job
&& !dest
&& sub
->expire
&& sub
->expire
<= curtime
) ||
632 (dest
&& sub
->dest
== dest
) ||
633 (job
&& sub
->job
== job
))
635 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...",
638 cupsdDeleteSubscription(sub
, 0);
644 cupsdSaveAllSubscriptions();
649 * 'cupsdFindSubscription()' - Find a subscription by ID.
652 cupsd_subscription_t
* /* O - Subscription object */
653 cupsdFindSubscription(int id
) /* I - Subscription ID */
655 cupsd_subscription_t sub
; /* Subscription template */
660 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
665 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
669 cupsdLoadAllSubscriptions(void)
671 int i
; /* Looping var */
672 cups_file_t
*fp
; /* subscriptions.conf file */
673 int linenum
; /* Current line number */
674 char line
[1024], /* Line from file */
675 *value
, /* Pointer to value */
676 *valueptr
; /* Pointer into value */
677 cupsd_subscription_t
*sub
; /* Current subscription */
678 int hex
; /* Non-zero if reading hex data */
679 int delete_sub
; /* Delete subscription? */
683 * Open the subscriptions.conf file...
686 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
687 if ((fp
= cupsFileOpen(line
, "r")) == NULL
)
690 cupsdLogMessage(CUPSD_LOG_ERROR
,
691 "LoadAllSubscriptions: Unable to open %s - %s", line
,
697 * Read all of the lines from the file...
704 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
706 if (!strcasecmp(line
, "NextSubscriptionId") && value
)
709 * NextSubscriptionId NNN
713 if (i
>= NextSubscriptionId
&& i
> 0)
714 NextSubscriptionId
= i
;
716 else if (!strcasecmp(line
, "<Subscription"))
722 if (!sub
&& value
&& isdigit(value
[0] & 255))
724 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
729 cupsdLogMessage(CUPSD_LOG_ERROR
,
730 "Syntax error on line %d of subscriptions.conf.",
735 else if (!strcasecmp(line
, "</Subscription>"))
739 cupsdLogMessage(CUPSD_LOG_ERROR
,
740 "Syntax error on line %d of subscriptions.conf.",
746 cupsdDeleteSubscription(sub
, 0);
753 cupsdLogMessage(CUPSD_LOG_ERROR
,
754 "Syntax error on line %d of subscriptions.conf.",
758 else if (!strcasecmp(line
, "Events"))
762 * Events name name name ...
767 cupsdLogMessage(CUPSD_LOG_ERROR
,
768 "Syntax error on line %d of subscriptions.conf.",
776 * Separate event names...
779 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
781 while (isspace(*valueptr
& 255))
785 * See if the name exists...
788 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
790 cupsdLogMessage(CUPSD_LOG_ERROR
,
791 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
799 else if (!strcasecmp(line
, "Owner"))
806 cupsdSetString(&sub
->owner
, value
);
809 cupsdLogMessage(CUPSD_LOG_ERROR
,
810 "Syntax error on line %d of subscriptions.conf.",
815 else if (!strcasecmp(line
, "Recipient"))
822 cupsdSetString(&sub
->recipient
, value
);
825 cupsdLogMessage(CUPSD_LOG_ERROR
,
826 "Syntax error on line %d of subscriptions.conf.",
831 else if (!strcasecmp(line
, "JobId"))
837 if (value
&& isdigit(*value
& 255))
839 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
841 cupsdLogMessage(CUPSD_LOG_ERROR
,
842 "Job %s not found on line %d of subscriptions.conf.",
849 cupsdLogMessage(CUPSD_LOG_ERROR
,
850 "Syntax error on line %d of subscriptions.conf.",
855 else if (!strcasecmp(line
, "PrinterName"))
863 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
865 cupsdLogMessage(CUPSD_LOG_ERROR
,
866 "Printer \'%s\' not found on line %d of subscriptions.conf.",
873 cupsdLogMessage(CUPSD_LOG_ERROR
,
874 "Syntax error on line %d of subscriptions.conf.",
879 else if (!strcasecmp(line
, "UserData"))
882 * UserData encoded-string
887 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
889 if (*valueptr
== '<' && !hex
)
897 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
899 if (isdigit(valueptr
[0]))
900 sub
->user_data
[i
] = (valueptr
[0] - '0') << 4;
902 sub
->user_data
[i
] = (tolower(valueptr
[0]) - 'a' + 10) << 4;
904 if (isdigit(valueptr
[1]))
905 sub
->user_data
[i
] |= valueptr
[1] - '0';
907 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
911 if (*valueptr
== '>')
921 sub
->user_data
[i
] = *valueptr
++;
926 cupsdLogMessage(CUPSD_LOG_ERROR
,
927 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
931 sub
->user_data_len
= i
;
935 cupsdLogMessage(CUPSD_LOG_ERROR
,
936 "Syntax error on line %d of subscriptions.conf.",
941 else if (!strcasecmp(line
, "LeaseDuration"))
947 if (value
&& isdigit(*value
& 255))
949 sub
->lease
= atoi(value
);
950 sub
->expire
= sub
->lease
? time(NULL
) + sub
->lease
: 0;
954 cupsdLogMessage(CUPSD_LOG_ERROR
,
955 "Syntax error on line %d of subscriptions.conf.",
960 else if (!strcasecmp(line
, "Interval"))
966 if (value
&& isdigit(*value
& 255))
967 sub
->interval
= atoi(value
);
970 cupsdLogMessage(CUPSD_LOG_ERROR
,
971 "Syntax error on line %d of subscriptions.conf.",
976 else if (!strcasecmp(line
, "ExpirationTime"))
982 if (value
&& isdigit(*value
& 255))
983 sub
->expire
= atoi(value
);
986 cupsdLogMessage(CUPSD_LOG_ERROR
,
987 "Syntax error on line %d of subscriptions.conf.",
992 else if (!strcasecmp(line
, "NextEventId"))
998 if (value
&& isdigit(*value
& 255))
999 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
1002 cupsdLogMessage(CUPSD_LOG_ERROR
,
1003 "Syntax error on line %d of subscriptions.conf.",
1011 * Something else we don't understand...
1014 cupsdLogMessage(CUPSD_LOG_ERROR
,
1015 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1025 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1029 cupsdSaveAllSubscriptions(void)
1031 int i
; /* Looping var */
1032 cups_file_t
*fp
; /* subscriptions.conf file */
1033 char temp
[1024]; /* Temporary string */
1034 char backup
[1024]; /* subscriptions.conf.O file */
1035 cupsd_subscription_t
*sub
; /* Current subscription */
1036 time_t curtime
; /* Current time */
1037 struct tm
*curdate
; /* Current date */
1038 unsigned mask
; /* Current event mask */
1039 const char *name
; /* Current event name */
1040 int hex
; /* Non-zero if we are writing hex data */
1044 * Create the subscriptions.conf file...
1047 snprintf(temp
, sizeof(temp
), "%s/subscriptions.conf", ServerRoot
);
1048 snprintf(backup
, sizeof(backup
), "%s/subscriptions.conf.O", ServerRoot
);
1050 if (rename(temp
, backup
))
1052 if (errno
!= ENOENT
)
1053 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to backup subscriptions.conf - %s",
1057 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1059 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to save subscriptions.conf - %s",
1062 if (rename(backup
, temp
))
1063 cupsdLogMessage(CUPSD_LOG_ERROR
,
1064 "Unable to restore subscriptions.conf - %s",
1069 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1072 * Restrict access to the file...
1075 fchown(cupsFileNumber(fp
), getuid(), Group
);
1076 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1079 * Write a small header to the file...
1082 curtime
= time(NULL
);
1083 curdate
= localtime(&curtime
);
1084 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1086 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1087 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1089 cupsFilePrintf(fp
, "NextSubscriptionId %d\n", NextSubscriptionId
);
1092 * Write every subscription known to the system...
1095 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1097 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1099 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1101 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1104 * Simple event list...
1107 cupsFilePrintf(fp
, "Events %s\n", name
);
1112 * Complex event list...
1115 cupsFilePuts(fp
, "Events");
1117 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1118 if (sub
->mask
& mask
)
1119 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1121 cupsFilePuts(fp
, "\n");
1125 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1127 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1129 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1131 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1133 if (sub
->user_data_len
> 0)
1135 cupsFilePuts(fp
, "UserData ");
1137 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1139 if (sub
->user_data
[i
] < ' ' ||
1140 sub
->user_data
[i
] > 0x7f ||
1141 sub
->user_data
[i
] == '<')
1145 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1149 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1155 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1159 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1164 cupsFilePuts(fp
, ">\n");
1166 cupsFilePutChar(fp
, '\n');
1169 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1170 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1171 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1172 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1174 cupsFilePuts(fp
, "</Subscription>\n");
1182 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1186 cupsdStopAllNotifiers(void)
1188 cupsd_subscription_t
*sub
; /* Current subscription */
1192 * See if we have started any notifiers...
1195 if (!NotifierStatusBuffer
)
1199 * Yes, kill any processes that are left...
1202 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1204 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1207 cupsdEndProcess(sub
->pid
, 0);
1214 * Close the status pipes...
1217 if (NotifierPipes
[0] >= 0)
1219 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1220 "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
1222 FD_CLR(NotifierPipes
[0], InputSet
);
1224 cupsdStatBufDelete(NotifierStatusBuffer
);
1226 close(NotifierPipes
[0]);
1227 close(NotifierPipes
[1]);
1229 NotifierPipes
[0] = -1;
1230 NotifierPipes
[1] = -1;
1231 NotifierStatusBuffer
= NULL
;
1237 * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
1241 cupsdUpdateNotifierStatus(void)
1243 char *ptr
, /* Pointer to end of line in buffer */
1244 message
[1024]; /* Pointer to message text */
1245 int loglevel
; /* Log level for message */
1248 while ((ptr
= cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1249 message
, sizeof(message
))) != NULL
)
1250 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))
1256 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1259 static int /* O - Result of comparison */
1260 cupsd_compare_subscriptions(
1261 cupsd_subscription_t
*first
, /* I - First subscription object */
1262 cupsd_subscription_t
*second
, /* I - Second subscription object */
1263 void *unused
) /* I - Unused user data pointer */
1267 return (first
->id
- second
->id
);
1272 * 'cupsd_delete_event()' - Delete a single event...
1274 * Oldest events must be deleted first, otherwise the subscription cache
1275 * flushing code will not work properly.
1279 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1285 ippDelete(event
->attrs
);
1292 * 'cupsd_send_dbus()' - Send a DBUS notification...
1296 cupsd_send_dbus(cupsd_eventmask_t event
,/* I - Event to send */
1297 cupsd_printer_t
*dest
,/* I - Destination, if any */
1298 cupsd_job_t
*job
) /* I - Job, if any */
1300 DBusError error
; /* Error, if any */
1301 DBusMessage
*message
; /* Message to send */
1302 DBusMessageIter iter
; /* Iterator for message data */
1303 const char *what
; /* What to send */
1304 static DBusConnection
*con
= NULL
; /* Connection to DBUS server */
1308 * Figure out what to send, if anything...
1311 if (event
& CUPSD_EVENT_PRINTER_ADDED
)
1312 what
= "PrinterAdded";
1313 else if (event
& CUPSD_EVENT_PRINTER_DELETED
)
1314 what
= "PrinterRemoved";
1315 else if (event
& CUPSD_EVENT_PRINTER_CHANGED
)
1316 what
= "QueueChanged";
1317 else if (event
& CUPSD_EVENT_JOB_CREATED
)
1318 what
= "JobQueuedLocal";
1319 else if ((event
& CUPSD_EVENT_JOB_STATE
) && job
&&
1320 job
->state_value
== IPP_JOB_PROCESSING
)
1321 what
= "JobStartedLocal";
1326 * Verify connection to DBUS server...
1329 if (con
&& !dbus_connection_get_is_connected(con
))
1331 dbus_connection_unref(con
);
1337 dbus_error_init(&error
);
1339 con
= dbus_bus_get(getuid() ? DBUS_BUS_SESSION
: DBUS_BUS_SYSTEM
, &error
);
1342 dbus_error_free(&error
);
1348 * Create and send the new message...
1351 message
= dbus_message_new_signal("/com/redhat/PrinterSpooler",
1352 "com.redhat.PrinterSpooler", what
);
1354 dbus_message_iter_init_append(message
, &iter
);
1356 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_STRING
, &(dest
->name
));
1359 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_UINT32
, &(job
->id
));
1360 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_STRING
, &(job
->username
));
1363 dbus_connection_send(con
, message
, NULL
);
1364 dbus_connection_flush(con
);
1365 dbus_message_unref(message
);
1367 #endif /* HAVE_DBUS */
1371 * 'cupsd_send_notification()' - Send a notification for the specified event.
1375 cupsd_send_notification(
1376 cupsd_subscription_t
*sub
, /* I - Subscription object */
1377 cupsd_event_t
*event
) /* I - Event to send */
1379 ipp_state_t state
; /* IPP event state */
1382 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1383 "cupsd_send_notification(sub=%p(%d), event=%p(%s))\n",
1384 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1387 * Allocate the events array as needed...
1392 sub
->events
= calloc(MaxEvents
, sizeof(cupsd_event_t
*));
1396 cupsdLogMessage(CUPSD_LOG_CRIT
,
1397 "Unable to allocate memory for subscription #%d!",
1404 * Purge an old event as needed...
1407 if (sub
->num_events
>= MaxEvents
)
1410 * Purge the oldest event in the cache...
1413 cupsd_delete_event(sub
->events
[0]);
1416 sub
->first_event_id
++;
1418 memmove(sub
->events
, sub
->events
+ 1,
1419 sub
->num_events
* sizeof(cupsd_event_t
*));
1423 * Add the event to the subscription. Since the events array is
1424 * always MaxEvents in length, and since we will have already
1425 * removed an event from the subscription cache if we hit the
1426 * event cache limit, we don't need to check for overflow here...
1429 sub
->events
[sub
->num_events
] = event
;
1433 * Deliver the event...
1439 cupsd_start_notifier(sub
);
1441 cupsdLogMessage(CUPSD_LOG_DEBUG
, "sub->pipe=%d", sub
->pipe
);
1445 event
->attrs
->state
= IPP_IDLE
;
1447 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1448 if (state
== IPP_ERROR
)
1451 if (state
== IPP_ERROR
)
1452 cupsdLogMessage(CUPSD_LOG_ERROR
,
1453 "Unable to send event for subscription %d (%s)!",
1454 sub
->id
, sub
->recipient
);
1459 * Bump the event sequence number...
1462 sub
->next_event_id
++;
1467 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1471 cupsd_start_notifier(
1472 cupsd_subscription_t
*sub
) /* I - Subscription object */
1474 int pid
; /* Notifier process ID */
1475 int fds
[2]; /* Pipe file descriptors */
1476 int envc
; /* Number of environment variables */
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 envc
= 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 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1539 "start_notifier: Adding fd %d to InputSet...",
1542 FD_SET(NotifierPipes
[0], InputSet
);
1545 if (cupsdOpenPipe(fds
))
1547 cupsdLogMessage(CUPSD_LOG_ERROR
,
1548 "Unable to create pipes for notifier %s - %s",
1549 scheme
, strerror(errno
));
1554 * Make sure the delivery pipe is non-blocking...
1557 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1560 * Create the notifier process...
1563 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1567 * Error - can't fork!
1570 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1571 scheme
, strerror(errno
));
1573 cupsdClosePipe(fds
);
1578 * Fork successful - return the PID...
1581 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1594 * End of "$Id: subscriptions.c 181 2006-06-22 20:01:18Z jlovell $".