2 * "$Id: subscriptions.c 4993 2006-01-26 19:27:40Z mike $"
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 * cupsdDeleteAllEvents() - Delete all cached events.
29 * cupsdDeleteAllSubscriptions() - Delete all subscriptions.
30 * cupsdDeleteSubscription() - Delete a subscription object.
31 * cupsdEventName() - Return a single event name.
32 * cupsdEventValue() - Return the event mask value for a name.
33 * cupsdExpireSubscriptions() - Expire old subscription objects.
34 * cupsdFindSubscription() - Find a subscription by ID.
35 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
36 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
37 * cupsdSendNotification() - Send a notification for the specified event.
38 * cupsdStopAllNotifiers() - Stop all notifier processes.
39 * cupsdUpdateNotifierStatus() - Read messages from notifiers.
40 * cupsd_compare_subscriptions() - Compare two subscriptions.
41 * cupsd_delete_event() - Delete a single event...
42 * cupsd_start_notifier() - Start a notifier subprocess...
46 * Include necessary headers...
56 static int cupsd_compare_subscriptions(cupsd_subscription_t
*first
,
57 cupsd_subscription_t
*second
,
59 static void cupsd_delete_event(cupsd_event_t
*event
);
60 static void cupsd_start_notifier(cupsd_subscription_t
*sub
);
64 * 'cupsdAddEvent()' - Add an event to the global event cache.
69 cupsd_eventmask_t event
, /* I - Event */
70 cupsd_printer_t
*dest
, /* I - Printer associated with event */
71 cupsd_job_t
*job
, /* I - Job associated with event */
72 const char *text
, /* I - Notification text */
73 ...) /* I - Additional arguments as needed */
75 va_list ap
; /* Pointer to additional arguments */
76 char ftext
[1024]; /* Formatted text buffer */
77 ipp_attribute_t
*attr
; /* Printer/job attribute */
78 cupsd_event_t
*temp
; /* New event pointer */
79 cupsd_subscription_t
*sub
; /* Current subscription */
85 * Return if we aren't keeping events...
90 cupsdLogMessage(CUPSD_LOG_WARN
,
91 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
92 cupsdEventName(event
), MaxEvents
);
97 * Allocate memory for the event cache as needed...
102 Events
= calloc(MaxEvents
, sizeof(cupsd_event_t
*));
107 cupsdLogMessage(CUPSD_LOG_CRIT
,
108 "Unable to allocate memory for event cache - %s",
115 * Then loop through the subscriptions and add the event to the corresponding
119 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
), temp
= NULL
;
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
))
138 * Create the new event record...
141 if ((temp
= (cupsd_event_t
*)calloc(1, sizeof(cupsd_event_t
))) == NULL
)
143 cupsdLogMessage(CUPSD_LOG_CRIT
,
144 "Unable to allocate memory for event - %s",
150 temp
->time
= time(NULL
);
151 temp
->attrs
= ippNew();
156 * Add common event notification attributes...
159 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
160 "notify-subscription-id", sub
->id
);
162 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_KEYWORD
,
163 "notify-subscribed-event", NULL
, cupsdEventName(event
));
165 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
166 "printer-up-time", time(NULL
));
169 vsnprintf(ftext
, sizeof(ftext
), text
, ap
);
172 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_TEXT
,
173 "notify-text", NULL
, ftext
);
178 * Add printer attributes...
181 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_URI
,
182 "notify-printer-uri", NULL
, dest
->uri
);
184 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
185 "printer-name", NULL
, dest
->name
);
187 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
188 "printer-state", dest
->state
);
190 if (dest
->num_reasons
== 0)
191 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
192 IPP_TAG_KEYWORD
, "printer-state-reasons", NULL
,
193 dest
->state
== IPP_PRINTER_STOPPED
? "paused" : "none");
195 ippAddStrings(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
196 IPP_TAG_KEYWORD
, "printer-state-reasons",
197 dest
->num_reasons
, NULL
,
198 (const char * const *)dest
->reasons
);
200 ippAddBoolean(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
201 "printer-is-accepting-jobs", dest
->accepting
);
207 * Add job attributes...
210 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
211 "notify-job-id", job
->id
);
212 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_ENUM
,
213 "job-state", (int)job
->state
);
215 if ((attr
= ippFindAttribute(job
->attrs
, "job-name",
216 IPP_TAG_NAME
)) != NULL
)
217 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_NAME
,
218 "job-name", NULL
, attr
->values
[0].string
.text
);
220 switch (job
->state
->values
[0].integer
)
222 case IPP_JOB_PENDING
:
223 if (dest
&& dest
->state
== IPP_PRINTER_STOPPED
)
224 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
225 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
228 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
229 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
234 if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
) != NULL
||
235 ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
) != NULL
)
236 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
237 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
238 "job-hold-until-specified");
240 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
241 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
245 case IPP_JOB_PROCESSING
:
246 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
247 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
251 case IPP_JOB_STOPPED
:
252 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
253 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
257 case IPP_JOB_CANCELLED
:
258 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
259 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
260 "job-canceled-by-user");
263 case IPP_JOB_ABORTED
:
264 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
265 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
266 "aborted-by-system");
269 case IPP_JOB_COMPLETED
:
270 ippAddString(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
,
271 IPP_TAG_KEYWORD
, "job-state-reasons", NULL
,
272 "job-completed-successfully");
276 ippAddInteger(temp
->attrs
, IPP_TAG_EVENT_NOTIFICATION
, IPP_TAG_INTEGER
,
277 "job-impressions-completed",
278 job
->sheets
? job
->sheets
->values
[0].integer
: 0);
282 * Purge an old event as needed...
285 if (NumEvents
>= MaxEvents
)
288 * Purge the oldest event in the cache...
291 cupsd_delete_event(Events
[0]);
295 memmove(Events
, Events
+ 1, NumEvents
* sizeof(cupsd_event_t
*));
299 * Add the new event to the main cache...
302 Events
[NumEvents
] = temp
;
307 * Send the notification for this subscription...
310 cupsdSendNotification(sub
, temp
);
315 cupsdSaveAllSubscriptions();
317 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Discarding unused %s event...",
318 cupsdEventName(event
));
323 * 'cupsdAddSubscription()' - Add a new subscription object.
326 cupsd_subscription_t
* /* O - New subscription object */
327 cupsdAddSubscription(
328 unsigned mask
, /* I - Event mask */
329 cupsd_printer_t
*dest
, /* I - Printer, if any */
330 cupsd_job_t
*job
, /* I - Job, if any */
331 const char *uri
, /* I - notify-recipient-uri, if any */
332 int sub_id
) /* I - notify-subscription-id or 0 */
334 cupsd_subscription_t
*temp
; /* New subscription object */
337 cupsdLogMessage(CUPSD_LOG_DEBUG
,
338 "cupsdAddSubscription(mask=%x(%s), dest=%p(%s), job=%p(%d), uri=\"%s\")",
339 mask
, cupsdEventName(mask
), dest
, dest
? dest
->name
: "",
340 job
, job
? job
->id
: 0, uri
);
343 Subscriptions
= cupsArrayNew((cups_array_func_t
)cupsd_compare_subscriptions
,
348 cupsdLogMessage(CUPSD_LOG_CRIT
,
349 "Unable to allocate memory for subscriptions - %s",
355 * Limit the number of subscriptions...
358 if (cupsArrayCount(Subscriptions
) >= MaxSubscriptions
)
362 * Allocate memory for this subscription...
365 if ((temp
= calloc(1, sizeof(cupsd_subscription_t
))) == NULL
)
367 cupsdLogMessage(CUPSD_LOG_CRIT
,
368 "Unable to allocate memory for subscription object - %s",
374 * Fill in common data...
381 if (sub_id
>= NextSubscriptionId
)
382 NextSubscriptionId
= sub_id
+ 1;
386 temp
->id
= NextSubscriptionId
;
388 NextSubscriptionId
++;
395 temp
->first_event_id
= 1;
396 temp
->next_event_id
= 1;
398 cupsdSetString(&(temp
->recipient
), uri
);
401 * Add the subscription to the array...
404 cupsArrayAdd(Subscriptions
, temp
);
411 * 'cupsdDeleteAllEvents()' - Delete all cached events.
415 cupsdDeleteAllEvents(void)
417 int i
; /* Looping var */
420 if (MaxEvents
<= 0 || !Events
)
423 for (i
= 0; i
< NumEvents
; i
++)
424 cupsd_delete_event(Events
[i
]);
432 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
436 cupsdDeleteAllSubscriptions(void)
438 cupsd_subscription_t
*sub
; /* Subscription */
444 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
446 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
447 cupsdDeleteSubscription(sub
, 0);
449 cupsArrayDelete(Subscriptions
);
450 Subscriptions
= NULL
;
455 * 'cupsdDeleteSubscription()' - Delete a subscription object.
459 cupsdDeleteSubscription(
460 cupsd_subscription_t
*sub
, /* I - Subscription object */
461 int update
) /* I - 1 = update subscriptions.conf */
464 * Close the pipe to the notifier as needed...
471 * Remove subscription from array...
474 cupsArrayRemove(Subscriptions
, sub
);
480 cupsdClearString(&(sub
->owner
));
481 cupsdClearString(&(sub
->recipient
));
489 * Update the subscriptions as needed...
493 cupsdSaveAllSubscriptions();
498 * 'cupsdEventName()' - Return a single event name.
501 const char * /* O - Event name */
503 cupsd_eventmask_t event
) /* I - Event value */
510 case CUPSD_EVENT_PRINTER_RESTARTED
:
511 return ("printer-restarted");
513 case CUPSD_EVENT_PRINTER_SHUTDOWN
:
514 return ("printer-shutdown");
516 case CUPSD_EVENT_PRINTER_STOPPED
:
517 return ("printer-stopped");
519 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
:
520 return ("printer-finishings-changed");
522 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED
:
523 return ("printer-media-changed");
525 case CUPSD_EVENT_PRINTER_ADDED
:
526 return ("printer-added");
528 case CUPSD_EVENT_PRINTER_DELETED
:
529 return ("printer-deleted");
531 case CUPSD_EVENT_PRINTER_MODIFIED
:
532 return ("printer-modified");
534 case CUPSD_EVENT_PRINTER_STATE_CHANGED
:
535 return ("printer-state-changed");
537 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED
:
538 return ("printer-config-changed");
540 case CUPSD_EVENT_PRINTER_CHANGED
:
541 return ("printer-changed");
543 case CUPSD_EVENT_JOB_CREATED
:
544 return ("job-created");
546 case CUPSD_EVENT_JOB_COMPLETED
:
547 return ("job-completed");
549 case CUPSD_EVENT_JOB_STOPPED
:
550 return ("job-stopped");
552 case CUPSD_EVENT_JOB_CONFIG_CHANGED
:
553 return ("job-config-changed");
555 case CUPSD_EVENT_JOB_PROGRESS
:
556 return ("job-progress");
558 case CUPSD_EVENT_JOB_STATE_CHANGED
:
559 return ("job-state-changed");
561 case CUPSD_EVENT_SERVER_RESTARTED
:
562 return ("server-restarted");
564 case CUPSD_EVENT_SERVER_STARTED
:
565 return ("server-started");
567 case CUPSD_EVENT_SERVER_STOPPED
:
568 return ("server-stopped");
570 case CUPSD_EVENT_SERVER_AUDIT
:
571 return ("server-audit");
573 case CUPSD_EVENT_ALL
:
580 * 'cupsdEventValue()' - Return the event mask value for a name.
583 cupsd_eventmask_t
/* O - Event mask value */
584 cupsdEventValue(const char *name
) /* I - Name of event */
586 if (!strcmp(name
, "all"))
587 return (CUPSD_EVENT_ALL
);
588 else if (!strcmp(name
, "printer-restarted"))
589 return (CUPSD_EVENT_PRINTER_RESTARTED
);
590 else if (!strcmp(name
, "printer-shutdown"))
591 return (CUPSD_EVENT_PRINTER_SHUTDOWN
);
592 else if (!strcmp(name
, "printer-stopped"))
593 return (CUPSD_EVENT_PRINTER_STOPPED
);
594 else if (!strcmp(name
, "printer-finishings-changed"))
595 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED
);
596 else if (!strcmp(name
, "printer-media-changed"))
597 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED
);
598 else if (!strcmp(name
, "printer-added"))
599 return (CUPSD_EVENT_PRINTER_ADDED
);
600 else if (!strcmp(name
, "printer-deleted"))
601 return (CUPSD_EVENT_PRINTER_DELETED
);
602 else if (!strcmp(name
, "printer-modified"))
603 return (CUPSD_EVENT_PRINTER_MODIFIED
);
604 else if (!strcmp(name
, "printer-state-changed"))
605 return (CUPSD_EVENT_PRINTER_STATE_CHANGED
);
606 else if (!strcmp(name
, "printer-config-changed"))
607 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED
);
608 else if (!strcmp(name
, "printer-changed"))
609 return (CUPSD_EVENT_PRINTER_CHANGED
);
610 else if (!strcmp(name
, "job-created"))
611 return (CUPSD_EVENT_JOB_CREATED
);
612 else if (!strcmp(name
, "job-completed"))
613 return (CUPSD_EVENT_JOB_COMPLETED
);
614 else if (!strcmp(name
, "job-stopped"))
615 return (CUPSD_EVENT_JOB_STOPPED
);
616 else if (!strcmp(name
, "job-config-changed"))
617 return (CUPSD_EVENT_JOB_CONFIG_CHANGED
);
618 else if (!strcmp(name
, "job-progress"))
619 return (CUPSD_EVENT_JOB_PROGRESS
);
620 else if (!strcmp(name
, "job-state-changed"))
621 return (CUPSD_EVENT_JOB_STATE_CHANGED
);
622 else if (!strcmp(name
, "server-restarted"))
623 return (CUPSD_EVENT_SERVER_RESTARTED
);
624 else if (!strcmp(name
, "server-started"))
625 return (CUPSD_EVENT_SERVER_STARTED
);
626 else if (!strcmp(name
, "server-stopped"))
627 return (CUPSD_EVENT_SERVER_STOPPED
);
628 else if (!strcmp(name
, "server-audit"))
629 return (CUPSD_EVENT_SERVER_AUDIT
);
631 return (CUPSD_EVENT_NONE
);
636 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
640 cupsdExpireSubscriptions(
641 cupsd_printer_t
*dest
, /* I - Printer, if any */
642 cupsd_job_t
*job
) /* I - Job, if any */
644 cupsd_subscription_t
*sub
; /* Current subscription */
645 int update
; /* Update subscriptions.conf? */
646 time_t curtime
; /* Current time */
649 curtime
= time(NULL
);
652 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
654 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
655 if ((sub
->expire
<= curtime
&& dest
&& sub
->dest
== dest
) ||
656 (job
&& sub
->job
== job
))
658 cupsdLogMessage(CUPSD_LOG_INFO
, "Subscription %d has expired...", sub
->id
);
660 cupsdDeleteSubscription(sub
, 0);
666 cupsdSaveAllSubscriptions();
671 * 'cupsdFindSubscription()' - Find a subscription by ID.
674 cupsd_subscription_t
* /* O - Subscription object */
675 cupsdFindSubscription(int id
) /* I - Subscription ID */
677 cupsd_subscription_t sub
; /* Subscription template */
682 return ((cupsd_subscription_t
*)cupsArrayFind(Subscriptions
, &sub
));
687 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
691 cupsdLoadAllSubscriptions(void)
693 int i
; /* Looping var */
694 cups_file_t
*fp
; /* subscriptions.conf file */
695 int linenum
; /* Current line number */
696 char line
[1024], /* Line from file */
697 *value
, /* Pointer to value */
698 *valueptr
; /* Pointer into value */
699 cupsd_subscription_t
*sub
; /* Current subscription */
700 int hex
; /* Non-zero if reading hex data */
701 int delete_sub
; /* Delete subscription? */
705 * Open the subscriptions.conf file...
708 snprintf(line
, sizeof(line
), "%s/subscriptions.conf", ServerRoot
);
709 if ((fp
= cupsFileOpen(line
, "r")) == NULL
)
712 cupsdLogMessage(CUPSD_LOG_ERROR
,
713 "LoadAllSubscriptions: Unable to open %s - %s", line
,
719 * Read all of the lines from the file...
726 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
728 if (!strcasecmp(line
, "<Subscription"))
734 if (!sub
&& value
&& isdigit(value
[0] & 255))
736 sub
= cupsdAddSubscription(CUPSD_EVENT_NONE
, NULL
, NULL
, NULL
,
741 cupsdLogMessage(CUPSD_LOG_ERROR
,
742 "Syntax error on line %d of subscriptions.conf.",
747 else if (!strcasecmp(line
, "</Subscription>"))
751 cupsdLogMessage(CUPSD_LOG_ERROR
,
752 "Syntax error on line %d of subscriptions.conf.",
758 cupsdDeleteSubscription(sub
, 0);
765 cupsdLogMessage(CUPSD_LOG_ERROR
,
766 "Syntax error on line %d of subscriptions.conf.",
770 else if (!strcasecmp(line
, "Events"))
774 * Events name name name ...
779 cupsdLogMessage(CUPSD_LOG_ERROR
,
780 "Syntax error on line %d of subscriptions.conf.",
788 * Separate event names...
791 for (valueptr
= value
; !isspace(*valueptr
) && *valueptr
; valueptr
++);
793 while (isspace(*valueptr
& 255))
797 * See if the name exists...
800 if ((sub
->mask
|= cupsdEventValue(value
)) == CUPSD_EVENT_NONE
)
802 cupsdLogMessage(CUPSD_LOG_ERROR
,
803 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
811 else if (!strcasecmp(line
, "Owner"))
818 cupsdSetString(&sub
->owner
, value
);
821 cupsdLogMessage(CUPSD_LOG_ERROR
,
822 "Syntax error on line %d of subscriptions.conf.",
827 else if (!strcasecmp(line
, "Recipient"))
834 cupsdSetString(&sub
->recipient
, value
);
837 cupsdLogMessage(CUPSD_LOG_ERROR
,
838 "Syntax error on line %d of subscriptions.conf.",
843 else if (!strcasecmp(line
, "JobId"))
849 if (value
&& isdigit(*value
& 255))
851 if ((sub
->job
= cupsdFindJob(atoi(value
))) == NULL
)
853 cupsdLogMessage(CUPSD_LOG_ERROR
,
854 "Job %s not found on line %d of subscriptions.conf.",
861 cupsdLogMessage(CUPSD_LOG_ERROR
,
862 "Syntax error on line %d of subscriptions.conf.",
867 else if (!strcasecmp(line
, "PrinterName"))
875 if ((sub
->dest
= cupsdFindDest(value
)) == NULL
)
877 cupsdLogMessage(CUPSD_LOG_ERROR
,
878 "Printer \'%s\' not found on line %d of subscriptions.conf.",
885 cupsdLogMessage(CUPSD_LOG_ERROR
,
886 "Syntax error on line %d of subscriptions.conf.",
891 else if (!strcasecmp(line
, "UserData"))
894 * UserData encoded-string
899 for (i
= 0, valueptr
= value
, hex
= 0; i
< 63 && *valueptr
; i
++)
901 if (*valueptr
== '<' && !hex
)
909 if (isxdigit(valueptr
[0]) && isxdigit(valueptr
[1]))
911 if (isdigit(valueptr
[0]))
912 sub
->user_data
[i
] = (valueptr
[0] - '0') << 4;
914 sub
->user_data
[i
] = (tolower(valueptr
[0]) - 'a' + 10) << 4;
916 if (isdigit(valueptr
[1]))
917 sub
->user_data
[i
] |= valueptr
[1] - '0';
919 sub
->user_data
[i
] |= tolower(valueptr
[1]) - 'a' + 10;
923 if (*valueptr
== '>')
933 sub
->user_data
[i
] = *valueptr
++;
938 cupsdLogMessage(CUPSD_LOG_ERROR
,
939 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
943 sub
->user_data_len
= i
;
947 cupsdLogMessage(CUPSD_LOG_ERROR
,
948 "Syntax error on line %d of subscriptions.conf.",
953 else if (!strcasecmp(line
, "LeaseDuration"))
959 if (value
&& isdigit(*value
& 255))
960 sub
->lease
= atoi(value
);
963 cupsdLogMessage(CUPSD_LOG_ERROR
,
964 "Syntax error on line %d of subscriptions.conf.",
969 else if (!strcasecmp(line
, "Interval"))
975 if (value
&& isdigit(*value
& 255))
976 sub
->interval
= atoi(value
);
979 cupsdLogMessage(CUPSD_LOG_ERROR
,
980 "Syntax error on line %d of subscriptions.conf.",
985 else if (!strcasecmp(line
, "ExpirationTime"))
991 if (value
&& isdigit(*value
& 255))
992 sub
->expire
= atoi(value
);
995 cupsdLogMessage(CUPSD_LOG_ERROR
,
996 "Syntax error on line %d of subscriptions.conf.",
1001 else if (!strcasecmp(line
, "NextEventId"))
1007 if (value
&& isdigit(*value
& 255))
1008 sub
->next_event_id
= sub
->first_event_id
= atoi(value
);
1011 cupsdLogMessage(CUPSD_LOG_ERROR
,
1012 "Syntax error on line %d of subscriptions.conf.",
1020 * Something else we don't understand...
1023 cupsdLogMessage(CUPSD_LOG_ERROR
,
1024 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1034 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1038 cupsdSaveAllSubscriptions(void)
1040 int i
; /* Looping var */
1041 cups_file_t
*fp
; /* subscriptions.conf file */
1042 char temp
[1024]; /* Temporary string */
1043 char backup
[1024]; /* subscriptions.conf.O file */
1044 cupsd_subscription_t
*sub
; /* Current subscription */
1045 time_t curtime
; /* Current time */
1046 struct tm
*curdate
; /* Current date */
1047 unsigned mask
; /* Current event mask */
1048 const char *name
; /* Current event name */
1049 int hex
; /* Non-zero if we are writing hex data */
1053 * Create the subscriptions.conf file...
1056 snprintf(temp
, sizeof(temp
), "%s/subscriptions.conf", ServerRoot
);
1057 snprintf(backup
, sizeof(backup
), "%s/subscriptions.conf.O", ServerRoot
);
1059 if (rename(temp
, backup
))
1061 if (errno
!= ENOENT
)
1062 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to backup subscriptions.conf - %s",
1066 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
1068 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to save subscriptions.conf - %s",
1071 if (rename(backup
, temp
))
1072 cupsdLogMessage(CUPSD_LOG_ERROR
,
1073 "Unable to restore subscriptions.conf - %s",
1078 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving subscriptions.conf...");
1081 * Restrict access to the file...
1084 fchown(cupsFileNumber(fp
), getuid(), Group
);
1085 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
1088 * Write a small header to the file...
1091 curtime
= time(NULL
);
1092 curdate
= localtime(&curtime
);
1093 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
1095 cupsFilePuts(fp
, "# Subscription configuration file for " CUPS_SVERSION
"\n");
1096 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
1099 * Write every subscription known to the system...
1102 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1104 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1106 cupsFilePrintf(fp
, "<Subscription %d>\n", sub
->id
);
1108 if ((name
= cupsdEventName((cupsd_eventmask_t
)sub
->mask
)) != NULL
)
1111 * Simple event list...
1114 cupsFilePrintf(fp
, "Events %s\n", name
);
1119 * Complex event list...
1122 cupsFilePuts(fp
, "Events");
1124 for (mask
= 1; mask
< CUPSD_EVENT_ALL
; mask
<<= 1)
1125 if (sub
->mask
& mask
)
1126 cupsFilePrintf(fp
, " %s", cupsdEventName((cupsd_eventmask_t
)mask
));
1128 cupsFilePuts(fp
, "\n");
1132 cupsFilePrintf(fp
, "Owner %s\n", sub
->owner
);
1134 cupsFilePrintf(fp
, "Recipient %s\n", sub
->recipient
);
1136 cupsFilePrintf(fp
, "JobId %d\n", sub
->job
->id
);
1138 cupsFilePrintf(fp
, "PrinterName %s\n", sub
->dest
->name
);
1140 if (sub
->user_data_len
> 0)
1142 cupsFilePuts(fp
, "UserData ");
1144 for (i
= 0, hex
= 0; i
< sub
->user_data_len
; i
++)
1146 if (sub
->user_data
[i
] < ' ' ||
1147 sub
->user_data
[i
] > 0x7f ||
1148 sub
->user_data
[i
] == '<')
1152 cupsFilePrintf(fp
, "<%02X", sub
->user_data
[i
]);
1156 cupsFilePrintf(fp
, "%02X", sub
->user_data
[i
]);
1162 cupsFilePrintf(fp
, ">%c", sub
->user_data
[i
]);
1166 cupsFilePutChar(fp
, sub
->user_data
[i
]);
1171 cupsFilePuts(fp
, ">\n");
1173 cupsFilePutChar(fp
, '\n');
1176 cupsFilePrintf(fp
, "LeaseDuration %d\n", sub
->lease
);
1177 cupsFilePrintf(fp
, "Interval %d\n", sub
->interval
);
1178 cupsFilePrintf(fp
, "ExpirationTime %ld\n", (long)sub
->expire
);
1179 cupsFilePrintf(fp
, "NextEventId %d\n", sub
->next_event_id
);
1181 cupsFilePuts(fp
, "</Subscription>\n");
1189 * 'cupsdSendNotification()' - Send a notification for the specified event.
1193 cupsdSendNotification(
1194 cupsd_subscription_t
*sub
, /* I - Subscription object */
1195 cupsd_event_t
*event
) /* I - Event to send */
1197 ipp_state_t state
; /* IPP event state */
1200 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1201 "cupsdSendNotification(sub=%p(%d), event=%p(%s))\n",
1202 sub
, sub
->id
, event
, cupsdEventName(event
->event
));
1205 * Allocate the events array as needed...
1210 sub
->events
= calloc(MaxEvents
, sizeof(cupsd_event_t
*));
1214 cupsdLogMessage(CUPSD_LOG_CRIT
,
1215 "Unable to allocate memory for subscription #%d!",
1222 * Add the event to the subscription. Since the events array is
1223 * always MaxEvents in length, and since we will have already
1224 * removed an event from the subscription cache if we hit the
1225 * event cache limit, we don't need to check for overflow here...
1228 sub
->events
[sub
->num_events
] = event
;
1232 * Deliver the event...
1238 cupsd_start_notifier(sub
);
1240 cupsdLogMessage(CUPSD_LOG_DEBUG
, "sub->pipe=%d", sub
->pipe
);
1244 event
->attrs
->state
= IPP_IDLE
;
1246 while ((state
= ippWriteFile(sub
->pipe
, event
->attrs
)) != IPP_DATA
)
1247 if (state
== IPP_ERROR
)
1250 if (state
== IPP_ERROR
)
1251 cupsdLogMessage(CUPSD_LOG_ERROR
,
1252 "Unable to send event for subscription %d (%s)!",
1253 sub
->id
, sub
->recipient
);
1258 * Bump the event sequence number...
1261 sub
->next_event_id
++;
1266 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1270 cupsdStopAllNotifiers(void)
1272 cupsd_subscription_t
*sub
; /* Current subscription */
1276 * See if we have started any notifiers...
1279 if (!NotifierStatusBuffer
)
1283 * Yes, kill any processes that are left...
1286 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1288 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1291 cupsdEndProcess(sub
->pid
, 0);
1298 * Close the status pipes...
1301 if (NotifierPipes
[0] >= 0)
1303 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1304 "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
1306 FD_CLR(NotifierPipes
[0], InputSet
);
1308 cupsdStatBufDelete(NotifierStatusBuffer
);
1310 close(NotifierPipes
[0]);
1311 close(NotifierPipes
[1]);
1313 NotifierPipes
[0] = -1;
1314 NotifierPipes
[1] = -1;
1315 NotifierStatusBuffer
= NULL
;
1321 * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
1325 cupsdUpdateNotifierStatus(void)
1327 char *ptr
, /* Pointer to end of line in buffer */
1328 message
[1024]; /* Pointer to message text */
1329 int loglevel
; /* Log level for message */
1332 while ((ptr
= cupsdStatBufUpdate(NotifierStatusBuffer
, &loglevel
,
1333 message
, sizeof(message
))) != NULL
)
1334 if (!strchr(NotifierStatusBuffer
->buffer
, '\n'))
1340 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1343 static int /* O - Result of comparison */
1344 cupsd_compare_subscriptions(
1345 cupsd_subscription_t
*first
, /* I - First subscription object */
1346 cupsd_subscription_t
*second
, /* I - Second subscription object */
1347 void *unused
) /* I - Unused user data pointer */
1351 return (first
->id
- second
->id
);
1356 * 'cupsd_delete_event()' - Delete a single event...
1358 * Oldest events must be deleted first, otherwise the subscription cache
1359 * flushing code will not work properly.
1363 cupsd_delete_event(cupsd_event_t
*event
)/* I - Event to delete */
1365 cupsd_subscription_t
*sub
; /* Current subscription */
1369 * Loop through the subscriptions and look for the event in the cache...
1372 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
1374 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
1377 * Only check the first event in the subscription cache, since the
1378 * caller will only delete the oldest event in the cache...
1381 if (sub
->num_events
> 0 && sub
->events
[0] == event
)
1384 * Remove this event...
1388 sub
->first_event_id
++;
1390 if (sub
->num_events
> 0)
1393 * Shift other events upward in cache...
1396 memmove(sub
->events
, sub
->events
+ 1,
1397 sub
->num_events
* sizeof(cupsd_event_t
*));
1406 ippDelete(event
->attrs
);
1412 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1416 cupsd_start_notifier(
1417 cupsd_subscription_t
*sub
) /* I - Subscription object */
1419 int pid
; /* Notifier process ID */
1420 int fds
[2]; /* Pipe file descriptors */
1421 int envc
; /* Number of environment variables */
1422 char *argv
[4], /* Command-line arguments */
1423 *envp
[100], /* Environment variables */
1424 user_data
[128], /* Base-64 encoded user data */
1425 scheme
[256], /* notify-recipient-uri scheme */
1426 *ptr
, /* Pointer into scheme */
1427 command
[1024]; /* Notifier command */
1431 * Extract the scheme name from the recipient URI and point to the
1432 * notifier program...
1435 strlcpy(scheme
, sub
->recipient
, sizeof(scheme
));
1436 if ((ptr
= strchr(scheme
, ':')) != NULL
)
1439 snprintf(command
, sizeof(command
), "%s/notifier/%s", ServerBin
, scheme
);
1442 * Base-64 encode the user data...
1445 httpEncode64_2(user_data
, sizeof(user_data
), (char *)sub
->user_data
,
1446 sub
->user_data_len
);
1449 * Setup the argument array...
1453 argv
[1] = sub
->recipient
;
1454 argv
[2] = user_data
;
1458 * Setup the environment...
1461 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1464 * Create pipes as needed...
1467 if (!NotifierStatusBuffer
)
1470 * Create the status pipe...
1473 if (cupsdOpenPipe(NotifierPipes
))
1475 cupsdLogMessage(CUPSD_LOG_ERROR
,
1476 "Unable to create pipes for notifier status - %s",
1481 NotifierStatusBuffer
= cupsdStatBufNew(NotifierPipes
[0], "[Notifier]");
1483 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1484 "start_notifier: Adding fd %d to InputSet...",
1487 FD_SET(NotifierPipes
[0], InputSet
);
1490 if (cupsdOpenPipe(fds
))
1492 cupsdLogMessage(CUPSD_LOG_ERROR
,
1493 "Unable to create pipes for notifier %s - %s",
1494 scheme
, strerror(errno
));
1499 * Make sure the delivery pipe is non-blocking...
1502 fcntl(fds
[1], F_SETFL
, fcntl(fds
[1], F_GETFL
) | O_NONBLOCK
);
1505 * Create the notifier process...
1508 if (cupsdStartProcess(command
, argv
, envp
, fds
[0], -1, NotifierPipes
[1],
1512 * Error - can't fork!
1515 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to fork for notifier %s - %s",
1516 scheme
, strerror(errno
));
1518 cupsdClosePipe(fds
);
1523 * Fork successful - return the PID...
1526 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Notifier %s started - PID = %d",
1539 * End of "$Id: subscriptions.c 4993 2006-01-26 19:27:40Z mike $".