/*
- * "$Id: subscriptions.c 4840 2005-11-14 21:53:30Z mike $"
+ * "$Id$"
*
- * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
+ * Subscription routines for the CUPS scheduler.
*
- * Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2011 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
- * property of Easy Software Products and are protected by Federal
- * copyright law. Distribution and use rights are outlined in the file
- * "LICENSE.txt" which should have been included with this file. If this
- * file is missing or damaged please contact Easy Software Products
- * at:
- *
- * Attn: CUPS Licensing Information
- * Easy Software Products
- * 44141 Airport View Drive, Suite 204
- * Hollywood, Maryland 20636 USA
- *
- * Voice: (301) 373-9600
- * EMail: cups-info@cups.org
- * WWW: http://www.cups.org
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
*
* Contents:
*
* cupsdAddEvent() - Add an event to the global event cache.
* cupsdAddSubscription() - Add a new subscription object.
- * cupsdDeleteAllEvents() - Delete all cached events.
* cupsdDeleteAllSubscriptions() - Delete all subscriptions.
* cupsdDeleteSubscription() - Delete a subscription object.
* cupsdEventName() - Return a single event name.
* cupsdFindSubscription() - Find a subscription by ID.
* cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
* cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
- * cupsdSendNotification() - Send a notification for the specified event.
* cupsdStopAllNotifiers() - Stop all notifier processes.
- * cupsdUpdateNotifierStatus() - Read messages from notifiers.
* cupsd_compare_subscriptions() - Compare two subscriptions.
* cupsd_delete_event() - Delete a single event...
+ * cupsd_send_dbus() - Send a DBUS notification...
+ * cupsd_send_notification() - Send a notification for the specified
+ * event.
* cupsd_start_notifier() - Start a notifier subprocess...
+ * cupsd_update_notifier() - Read messages from notifiers.
*/
/*
*/
#include "cupsd.h"
+#ifdef HAVE_DBUS
+# include <dbus/dbus.h>
+# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
+# define dbus_message_append_iter_init dbus_message_iter_init_append
+# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
+# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
+# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
+#endif /* HAVE_DBUS */
/*
cupsd_subscription_t *second,
void *unused);
static void cupsd_delete_event(cupsd_event_t *event);
+#ifdef HAVE_DBUS
+static void cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
+ cupsd_job_t *job);
+#endif /* HAVE_DBUS */
+static void cupsd_send_notification(cupsd_subscription_t *sub,
+ cupsd_event_t *event);
static void cupsd_start_notifier(cupsd_subscription_t *sub);
+static void cupsd_update_notifier(void);
/*
{
va_list ap; /* Pointer to additional arguments */
char ftext[1024]; /* Formatted text buffer */
+ ipp_attribute_t *attr; /* Printer/job attribute */
cupsd_event_t *temp; /* New event pointer */
cupsd_subscription_t *sub; /* Current subscription */
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
+ cupsdEventName(event), dest, dest ? dest->name : "",
+ job, job ? job->id : 0, text);
+
+ /*
+ * Keep track of events with any OS-supplied notification mechanisms...
+ */
+
+ LastEvent |= event;
+
+#ifdef HAVE_DBUS
+ cupsd_send_dbus(event, dest, job);
+#endif /* HAVE_DBUS */
+
/*
* Return if we aren't keeping events...
*/
return;
}
- /*
- * Allocate memory for the event cache as needed...
- */
-
- if (!Events)
- {
- Events = calloc(MaxEvents, sizeof(cupsd_event_t *));
- NumEvents = 0;
-
- if (!Events)
- {
- cupsdLogMessage(CUPSD_LOG_CRIT,
- "Unable to allocate memory for event cache - %s",
- strerror(errno));
- return;
- }
- }
-
/*
* Then loop through the subscriptions and add the event to the corresponding
* caches...
*/
- for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), temp = NULL;
+ for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
sub;
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
{
(sub->job == job || !sub->job))
{
/*
- * Need this event...
+ * Need this event, so create a new event record...
*/
- if (!temp)
+ if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
{
- /*
- * Create the new event record...
- */
+ cupsdLogMessage(CUPSD_LOG_CRIT,
+ "Unable to allocate memory for event - %s",
+ strerror(errno));
+ return;
+ }
- if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
- {
- cupsdLogMessage(CUPSD_LOG_CRIT,
- "Unable to allocate memory for event - %s",
- strerror(errno));
- return;
- }
+ temp->event = event;
+ temp->time = time(NULL);
+ temp->attrs = ippNew();
+ temp->job = job;
- temp->event = event;
- temp->time = time(NULL);
- temp->attrs = ippNew();
- temp->job = job;
- temp->dest = dest;
+ if (dest)
+ temp->dest = dest;
+ else if (job)
+ temp->dest = dest = cupsdFindPrinter(job->dest);
- /*
- * Add common event notification attributes...
- */
+ /*
+ * Add common event notification attributes...
+ */
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
- "notify-subscription-id", sub->id);
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
+ "notify-charset", NULL, "utf-8");
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
- "notify-subscribed-event", NULL, cupsdEventName(event));
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
+ "notify-natural-language", NULL, "en-US");
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
- "printer-up-time", time(NULL));
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
+ "notify-subscription-id", sub->id);
- va_start(ap, text);
- vsnprintf(ftext, sizeof(ftext), text, ap);
- va_end(ap);
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
+ "notify-sequence-number", sub->next_event_id);
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
- "notify-text", NULL, ftext);
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
+ "notify-subscribed-event", NULL, cupsdEventName(event));
- if (dest)
- {
- /*
- * Add printer attributes...
- */
+ if (sub->user_data_len > 0)
+ ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ "notify-user-data", sub->user_data,
+ sub->user_data_len);
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
- "notify-printer-uri", NULL, dest->uri);
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
+ "printer-up-time", time(NULL));
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
- "printer-name", NULL, dest->name);
+ va_start(ap, text);
+ vsnprintf(ftext, sizeof(ftext), text, ap);
+ va_end(ap);
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
- "printer-state", dest->state);
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
+ "notify-text", NULL, ftext);
- if (dest->num_reasons == 0)
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
- dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
- else
- ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "printer-state-reasons",
- dest->num_reasons, NULL,
- (const char * const *)dest->reasons);
+ if (dest)
+ {
+ /*
+ * Add printer attributes...
+ */
- ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- "printer-is-accepting-jobs", dest->accepting);
- }
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
+ "notify-printer-uri", NULL, dest->uri);
- if (job)
- {
- /*
- * Add job attributes...
- */
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
+ "printer-name", NULL, dest->name);
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
- "job-id", job->id);
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
- "job-state", (int)job->state);
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
+ "printer-state", dest->state);
- switch (job->state->values[0].integer)
- {
- case IPP_JOB_PENDING :
- if (dest && dest->state == IPP_PRINTER_STOPPED)
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "printer-stopped");
- else
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "none");
- break;
-
- case IPP_JOB_HELD :
- if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
- ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-hold-until-specified");
- else
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-incoming");
- break;
-
- case IPP_JOB_PROCESSING :
- ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
- IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-printing");
- break;
+ if (dest->num_reasons == 0)
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
+ dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
+ else
+ ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "printer-state-reasons",
+ dest->num_reasons, NULL,
+ (const char * const *)dest->reasons);
+
+ ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ "printer-is-accepting-jobs", dest->accepting);
+ }
- case IPP_JOB_STOPPED :
+ if (job)
+ {
+ /*
+ * Add job attributes...
+ */
+
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
+ "notify-job-id", job->id);
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
+ "job-state", job->state_value);
+
+ if ((attr = ippFindAttribute(job->attrs, "job-name",
+ IPP_TAG_NAME)) != NULL)
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
+ "job-name", NULL, attr->values[0].string.text);
+
+ switch (job->state_value)
+ {
+ case IPP_JOB_PENDING :
+ if (dest && dest->state == IPP_PRINTER_STOPPED)
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-stopped");
- break;
-
- case IPP_JOB_CANCELLED :
+ "printer-stopped");
+ else
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-canceled-by-user");
- break;
+ "none");
+ break;
- case IPP_JOB_ABORTED :
+ case IPP_JOB_HELD :
+ if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
+ ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "aborted-by-system");
- break;
-
- case IPP_JOB_COMPLETED :
+ "job-hold-until-specified");
+ else
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
- "job-completed-successfully");
- break;
- }
-
- ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
- "job-impressions-completed",
- job->sheets ? job->sheets->values[0].integer : 0);
- }
-
- /*
- * Purge an old event as needed...
- */
-
- if (NumEvents >= MaxEvents)
- {
- /*
- * Purge the oldest event in the cache...
- */
-
- cupsd_delete_event(Events[0]);
-
- NumEvents --;
-
- memmove(Events, Events + 1, NumEvents * sizeof(cupsd_event_t *));
+ "job-incoming");
+ break;
+
+ case IPP_JOB_PROCESSING :
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "job-state-reasons", NULL,
+ "job-printing");
+ break;
+
+ case IPP_JOB_STOPPED :
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "job-state-reasons", NULL,
+ "job-stopped");
+ break;
+
+ case IPP_JOB_CANCELED :
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "job-state-reasons", NULL,
+ "job-canceled-by-user");
+ break;
+
+ case IPP_JOB_ABORTED :
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "job-state-reasons", NULL,
+ "aborted-by-system");
+ break;
+
+ case IPP_JOB_COMPLETED :
+ ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
+ IPP_TAG_KEYWORD, "job-state-reasons", NULL,
+ "job-completed-successfully");
+ break;
}
- /*
- * Add the new event to the main cache...
- */
-
- Events[NumEvents] = temp;
- NumEvents ++;
+ ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
+ "job-impressions-completed",
+ job->sheets ? job->sheets->values[0].integer : 0);
}
/*
* Send the notification for this subscription...
*/
- cupsdSendNotification(sub, temp);
+ cupsd_send_notification(sub, temp);
}
}
if (temp)
- cupsdSaveAllSubscriptions();
+ cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
else
cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...",
cupsdEventName(event));
cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdAddSubscription(mask=%x(%s), dest=%p(%s), job=%p(%d), uri=\"%s\")",
- mask, cupsdEventName(mask), dest, dest ? dest->name : "",
- job, job ? job->id : 0, uri);
+ "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
+ "uri=\"%s\")",
+ mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
+ uri ? uri : "(null)");
if (!Subscriptions)
Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
* Limit the number of subscriptions...
*/
- if (cupsArrayCount(Subscriptions) >= MaxSubscriptions)
+ if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdAddSubscription: Reached MaxSubscriptions %d "
+ "(count=%d)", MaxSubscriptions,
+ cupsArrayCount(Subscriptions));
return (NULL);
+ }
+
+ if (MaxSubscriptionsPerJob > 0 && job)
+ {
+ int count; /* Number of job subscriptions */
+
+ for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
+ count = 0;
+ temp;
+ temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
+ if (temp->job == job)
+ count ++;
+
+ if (count >= MaxSubscriptionsPerJob)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
+ "for job #%d (count=%d)", MaxSubscriptionsPerJob,
+ job->id, count);
+ return (NULL);
+ }
+ }
+
+ if (MaxSubscriptionsPerPrinter > 0 && dest)
+ {
+ int count; /* Number of printer subscriptions */
+
+ for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
+ count = 0;
+ temp;
+ temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
+ if (temp->dest == dest)
+ count ++;
+
+ if (count >= MaxSubscriptionsPerPrinter)
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "cupsdAddSubscription: Reached "
+ "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
+ MaxSubscriptionsPerPrinter, dest->name, count);
+ return (NULL);
+ }
+ }
/*
* Allocate memory for this subscription...
cupsArrayAdd(Subscriptions, temp);
- return (temp);
-}
-
-
-/*
- * 'cupsdDeleteAllEvents()' - Delete all cached events.
- */
-
-void
-cupsdDeleteAllEvents(void)
-{
- int i; /* Looping var */
-
-
- if (MaxEvents <= 0 || !Events)
- return;
+ /*
+ * For RSS subscriptions, run the notifier immediately...
+ */
- for (i = 0; i < NumEvents; i ++)
- cupsd_delete_event(Events[i]);
+ if (uri && !strncmp(uri, "rss:", 4))
+ cupsd_start_notifier(temp);
- free(Events);
- Events = NULL;
+ return (temp);
}
cupsdClearString(&(sub->owner));
cupsdClearString(&(sub->recipient));
- if (sub->events)
- free(sub->events);
+ cupsArrayDelete(sub->events);
free(sub);
*/
if (update)
- cupsdSaveAllSubscriptions();
+ cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
}
case CUPSD_EVENT_PRINTER_MODIFIED :
return ("printer-modified");
+ case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
+ return ("printer-queue-order-changed");
+
+ case CUPSD_EVENT_PRINTER_STATE :
case CUPSD_EVENT_PRINTER_STATE_CHANGED :
return ("printer-state-changed");
+ case CUPSD_EVENT_PRINTER_CONFIG :
case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
return ("printer-config-changed");
case CUPSD_EVENT_JOB_PROGRESS :
return ("job-progress");
+ case CUPSD_EVENT_JOB_STATE :
case CUPSD_EVENT_JOB_STATE_CHANGED :
return ("job-state-changed");
return (CUPSD_EVENT_PRINTER_DELETED);
else if (!strcmp(name, "printer-modified"))
return (CUPSD_EVENT_PRINTER_MODIFIED);
+ else if (!strcmp(name, "printer-queue-order-changed"))
+ return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
else if (!strcmp(name, "printer-state-changed"))
return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
else if (!strcmp(name, "printer-config-changed"))
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
sub;
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
- if (sub->expire <= curtime ||
+ if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
(dest && sub->dest == dest) ||
(job && sub->job == job))
{
- cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...", sub->id);
+ cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
+ sub->id);
cupsdDeleteSubscription(sub, 0);
}
if (update)
- cupsdSaveAllSubscriptions();
+ cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
}
*/
snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
- if ((fp = cupsFileOpen(line, "r")) == NULL)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "LoadAllSubscriptions: Unable to open %s - %s", line,
- strerror(errno));
+ if ((fp = cupsdOpenConfFile(line)) == NULL)
return;
- }
/*
* Read all of the lines from the file...
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
{
- if (!strcasecmp(line, "<Subscription"))
+ if (!_cups_strcasecmp(line, "NextSubscriptionId") && value)
+ {
+ /*
+ * NextSubscriptionId NNN
+ */
+
+ i = atoi(value);
+ if (i >= NextSubscriptionId && i > 0)
+ NextSubscriptionId = i;
+ }
+ else if (!_cups_strcasecmp(line, "<Subscription"))
{
/*
* <Subscription #>
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "</Subscription>"))
+ else if (!_cups_strcasecmp(line, "</Subscription>"))
{
if (!sub)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
if (delete_sub)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
}
- else if (!strcasecmp(line, "Events"))
+ else if (!_cups_strcasecmp(line, "Events"))
{
/*
* Events name
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
while (*value)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unknown event name \'%s\' on line %d of subscriptions.conf.",
value, linenum);
- return;
+ break;
}
value = valueptr;
}
}
- else if (!strcasecmp(line, "Owner"))
+ else if (!_cups_strcasecmp(line, "Owner"))
{
/*
* Owner
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "Recipient"))
+ else if (!_cups_strcasecmp(line, "Recipient"))
{
/*
* Recipient uri
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "JobId"))
+ else if (!_cups_strcasecmp(line, "JobId"))
{
/*
* JobId #
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "PrinterName"))
+ else if (!_cups_strcasecmp(line, "PrinterName"))
{
/*
* PrinterName name
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "UserData"))
+ else if (!_cups_strcasecmp(line, "UserData"))
{
/*
* UserData encoded-string
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "LeaseDuration"))
+ else if (!_cups_strcasecmp(line, "LeaseDuration"))
{
/*
* LeaseDuration #
*/
if (value && isdigit(*value & 255))
- sub->lease = atoi(value);
+ {
+ sub->lease = atoi(value);
+ sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
+ }
else
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "Interval"))
+ else if (!_cups_strcasecmp(line, "Interval"))
{
/*
* Interval #
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "ExpirationTime"))
+ else if (!_cups_strcasecmp(line, "ExpirationTime"))
{
/*
* ExpirationTime #
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
- else if (!strcasecmp(line, "NextEventId"))
+ else if (!_cups_strcasecmp(line, "NextEventId"))
{
/*
* NextEventId #
cupsdLogMessage(CUPSD_LOG_ERROR,
"Syntax error on line %d of subscriptions.conf.",
linenum);
- return;
+ break;
}
}
else
{
int i; /* Looping var */
cups_file_t *fp; /* subscriptions.conf file */
- char temp[1024]; /* Temporary string */
- char backup[1024]; /* subscriptions.conf.O file */
+ char filename[1024], /* subscriptions.conf filename */
+ temp[1024]; /* Temporary string */
cupsd_subscription_t *sub; /* Current subscription */
time_t curtime; /* Current time */
struct tm *curdate; /* Current date */
* Create the subscriptions.conf file...
*/
- snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
- snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
-
- if (rename(temp, backup))
- {
- if (errno != ENOENT)
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
- strerror(errno));
- }
+ snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot);
- if ((fp = cupsFileOpen(temp, "w")) == NULL)
- {
- cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
- strerror(errno));
-
- if (rename(backup, temp))
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to restore subscriptions.conf - %s",
- strerror(errno));
+ if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
return;
- }
- else
- cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
-
- /*
- * Restrict access to the file...
- */
- fchown(cupsFileNumber(fp), getuid(), Group);
- fchmod(cupsFileNumber(fp), ConfigFilePerm);
+ cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
/*
* Write a small header to the file...
cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
+ cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
+
/*
* Write every subscription known to the system...
*/
cupsFilePuts(fp, "</Subscription>\n");
}
- cupsFileClose(fp);
-}
-
-
-/*
- * 'cupsdSendNotification()' - Send a notification for the specified event.
- */
-
-void
-cupsdSendNotification(
- cupsd_subscription_t *sub, /* I - Subscription object */
- cupsd_event_t *event) /* I - Event to send */
-{
- ipp_state_t state; /* IPP event state */
-
-
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdSendNotification(sub=%p(%d), event=%p(%s))\n",
- sub, sub->id, event, cupsdEventName(event->event));
-
- /*
- * Allocate the events array as needed...
- */
-
- if (!sub->events)
- {
- sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
-
- if (!sub->events)
- {
- cupsdLogMessage(CUPSD_LOG_CRIT,
- "Unable to allocate memory for subscription #%d!",
- sub->id);
- return;
- }
- }
-
- /*
- * Add the event to the subscription. Since the events array is
- * always MaxEvents in length, and since we will have already
- * removed an event from the subscription cache if we hit the
- * event cache limit, we don't need to check for overflow here...
- */
-
- sub->events[sub->num_events] = event;
- sub->num_events ++;
-
- /*
- * Deliver the event...
- */
-
- if (sub->recipient)
- {
- if (sub->pipe < 0)
- cupsd_start_notifier(sub);
-
- if (sub->pipe >= 0)
- {
- event->attrs->state = IPP_IDLE;
-
- while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
- if (state == IPP_ERROR)
- break;
-
- if (state == IPP_ERROR)
- cupsdLogMessage(CUPSD_LOG_ERROR,
- "Unable to send event for subscription %d (%s)!",
- sub->id, sub->recipient);
- }
- }
-
- /*
- * Bump the event sequence number...
- */
-
- sub->next_event_id ++;
+ cupsdCloseCreatedConfFile(fp, filename);
}
return;
/*
- * Yes, kill and processes that are left...
+ * Yes, kill any processes that are left...
*/
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
if (NotifierPipes[0] >= 0)
{
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
- NotifierPipes[0]);
- FD_CLR(NotifierPipes[0], InputSet);
+ cupsdRemoveSelect(NotifierPipes[0]);
cupsdStatBufDelete(NotifierStatusBuffer);
}
-/*
- * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
- */
-
-void
-cupsdUpdateNotifierStatus(void)
-{
- char *ptr, /* Pointer to end of line in buffer */
- message[1024]; /* Pointer to message text */
- int loglevel; /* Log level for message */
-
-
- while ((ptr = cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
- message, sizeof(message))) != NULL)
- if (!strchr(NotifierStatusBuffer->buffer, '\n'))
- break;
-}
-
-
/*
* 'cupsd_compare_subscriptions()' - Compare two subscriptions.
*/
static void
cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
{
- cupsd_subscription_t *sub; /* Current subscription */
+ /*
+ * Free memory...
+ */
+
+ ippDelete(event->attrs);
+ free(event);
+}
+
+
+#ifdef HAVE_DBUS
+/*
+ * 'cupsd_send_dbus()' - Send a DBUS notification...
+ */
+
+static void
+cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
+ cupsd_printer_t *dest,/* I - Destination, if any */
+ cupsd_job_t *job) /* I - Job, if any */
+{
+ DBusError error; /* Error, if any */
+ DBusMessage *message; /* Message to send */
+ DBusMessageIter iter; /* Iterator for message data */
+ const char *what; /* What to send */
+ static DBusConnection *con = NULL; /* Connection to DBUS server */
/*
- * Loop through the subscriptions and look for the event in the cache...
+ * Figure out what to send, if anything...
*/
- for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
- sub;
- sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
+ if (event & CUPSD_EVENT_PRINTER_ADDED)
+ what = "PrinterAdded";
+ else if (event & CUPSD_EVENT_PRINTER_DELETED)
+ what = "PrinterRemoved";
+ else if (event & CUPSD_EVENT_PRINTER_CHANGED)
+ what = "QueueChanged";
+ else if (event & CUPSD_EVENT_JOB_CREATED)
+ what = "JobQueuedLocal";
+ else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
+ job->state_value == IPP_JOB_PROCESSING)
+ what = "JobStartedLocal";
+ else
+ return;
+
+ /*
+ * Verify connection to DBUS server...
+ */
+
+ if (con && !dbus_connection_get_is_connected(con))
+ {
+ dbus_connection_unref(con);
+ con = NULL;
+ }
+
+ if (!con)
+ {
+ dbus_error_init(&error);
+
+ con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
+ if (!con)
+ {
+ dbus_error_free(&error);
+ return;
+ }
+ }
+
+ /*
+ * Create and send the new message...
+ */
+
+ message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
+ "com.redhat.PrinterSpooler", what);
+
+ dbus_message_append_iter_init(message, &iter);
+ if (dest)
+ dbus_message_iter_append_string(&iter, dest->name);
+ if (job)
+ {
+ dbus_message_iter_append_uint32(&iter, job->id);
+ dbus_message_iter_append_string(&iter, job->username);
+ }
+
+ dbus_connection_send(con, message, NULL);
+ dbus_connection_flush(con);
+ dbus_message_unref(message);
+}
+#endif /* HAVE_DBUS */
+
+
+/*
+ * 'cupsd_send_notification()' - Send a notification for the specified event.
+ */
+
+static void
+cupsd_send_notification(
+ cupsd_subscription_t *sub, /* I - Subscription object */
+ cupsd_event_t *event) /* I - Event to send */
+{
+ ipp_state_t state; /* IPP event state */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
+ sub, sub->id, event, cupsdEventName(event->event));
+
+ /*
+ * Allocate the events array as needed...
+ */
+
+ if (!sub->events)
+ {
+ sub->events = cupsArrayNew3((cups_array_func_t)NULL, NULL,
+ (cups_ahash_func_t)NULL, 0,
+ (cups_acopy_func_t)NULL,
+ (cups_afree_func_t)cupsd_delete_event);
+
+ if (!sub->events)
+ {
+ cupsdLogMessage(CUPSD_LOG_CRIT,
+ "Unable to allocate memory for subscription #%d!",
+ sub->id);
+ return;
+ }
+ }
+
+ /*
+ * Purge an old event as needed...
+ */
+
+ if (cupsArrayCount(sub->events) >= MaxEvents)
{
/*
- * Only check the first event in the subscription cache, since the
- * caller will only delete the oldest event in the cache...
+ * Purge the oldest event in the cache...
*/
- if (sub->num_events > 0 && sub->events[0] == event)
+ cupsArrayRemove(sub->events, cupsArrayFirst(sub->events));
+
+ sub->first_event_id ++;
+ }
+
+ /*
+ * Add the event to the subscription. Since the events array is
+ * always MaxEvents in length, and since we will have already
+ * removed an event from the subscription cache if we hit the
+ * event cache limit, we don't need to check for overflow here...
+ */
+
+ cupsArrayAdd(sub->events, event);
+
+ /*
+ * Deliver the event...
+ */
+
+ if (sub->recipient)
+ {
+ for (;;)
{
- /*
- * Remove this event...
- */
+ if (sub->pipe < 0)
+ cupsd_start_notifier(sub);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
- sub->num_events --;
- sub->first_event_id ++;
+ if (sub->pipe < 0)
+ break;
- if (sub->num_events > 0)
+ event->attrs->state = IPP_IDLE;
+
+ while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
+ if (state == IPP_ERROR)
+ break;
+
+ if (state == IPP_ERROR)
{
- /*
- * Shift other events upward in cache...
- */
+ if (errno == EPIPE)
+ {
+ /*
+ * Notifier died, try restarting it...
+ */
- memmove(sub->events, sub->events + 1,
- sub->num_events * sizeof(cupsd_event_t *));
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "Notifier for subscription %d (%s) went away, "
+ "retrying!",
+ sub->id, sub->recipient);
+ cupsdEndProcess(sub->pid, 0);
+
+ close(sub->pipe);
+ sub->pipe = -1;
+ continue;
+ }
+
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to send event for subscription %d (%s)!",
+ sub->id, sub->recipient);
}
+
+ /*
+ * If we get this far, break out of the loop...
+ */
+
+ break;
}
}
/*
- * Free memory...
+ * Bump the event sequence number...
*/
- ippDelete(event->attrs);
- free(event);
+ sub->next_event_id ++;
}
{
int pid; /* Notifier process ID */
int fds[2]; /* Pipe file descriptors */
- int envc; /* Number of environment variables */
char *argv[4], /* Command-line arguments */
- *envp[100], /* Environment variables */
+ *envp[MAX_ENV], /* Environment variables */
user_data[128], /* Base-64 encoded user data */
scheme[256], /* notify-recipient-uri scheme */
*ptr, /* Pointer into scheme */
* Setup the environment...
*/
- envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+ cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
/*
* Create pipes as needed...
NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
- cupsdLogMessage(CUPSD_LOG_DEBUG2,
- "start_notifier: Adding fd %d to InputSet...",
- NotifierPipes[0]);
-
- FD_SET(NotifierPipes[0], InputSet);
+ cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
+ NULL, NULL);
}
if (cupsdOpenPipe(fds))
*/
if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
- -1, 0, &pid) < 0)
+ -1, -1, 0, DefaultProfile, NULL, &pid) < 0)
{
/*
* Error - can't fork!
/*
- * End of "$Id: subscriptions.c 4840 2005-11-14 21:53:30Z mike $".
+ * 'cupsd_update_notifier()' - Read messages from notifiers.
+ */
+
+void
+cupsd_update_notifier(void)
+{
+ char message[1024]; /* Pointer to message text */
+ int loglevel; /* Log level for message */
+
+
+ while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
+ message, sizeof(message)))
+ {
+ if (loglevel == CUPSD_LOG_INFO)
+ cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
+
+ if (!strchr(NotifierStatusBuffer->buffer, '\n'))
+ break;
+ }
+}
+
+
+/*
+ * End of "$Id$".
*/