2
* "$Id: subscriptions.c 9445 2011-01-08 00:03:51Z mike $"
4
* Subscription routines for the CUPS scheduler.
6
* Copyright 2007-2011 by Apple Inc.
7
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
9
* These coded instructions, statements, and computer programs are the
10
* property of Apple Inc. and are protected by Federal copyright
11
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
12
* which should have been included with this file. If this file is
13
* file is missing or damaged, see the license at "http://www.cups.org/".
17
* cupsdAddEvent() - Add an event to the global event cache.
18
* cupsdAddSubscription() - Add a new subscription object.
19
* cupsdDeleteAllSubscriptions() - Delete all subscriptions.
20
* cupsdDeleteSubscription() - Delete a subscription object.
21
* cupsdEventName() - Return a single event name.
22
* cupsdEventValue() - Return the event mask value for a name.
23
* cupsdExpireSubscriptions() - Expire old subscription objects.
24
* cupsdFindSubscription() - Find a subscription by ID.
25
* cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
26
* cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
27
* cupsdStopAllNotifiers() - Stop all notifier processes.
28
* cupsd_compare_subscriptions() - Compare two subscriptions.
29
* cupsd_delete_event() - Delete a single event...
30
* cupsd_send_dbus() - Send a DBUS notification...
31
* cupsd_send_notification() - Send a notification for the specified
33
* cupsd_start_notifier() - Start a notifier subprocess...
34
* cupsd_update_notifier() - Read messages from notifiers.
38
* Include necessary headers...
43
# include <dbus/dbus.h>
44
# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
45
# define dbus_message_append_iter_init dbus_message_iter_init_append
46
# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
47
# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
48
# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
49
#endif /* HAVE_DBUS */
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);
61
static void cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
63
#endif /* HAVE_DBUS */
64
static void cupsd_send_notification(cupsd_subscription_t *sub,
65
cupsd_event_t *event);
66
static void cupsd_start_notifier(cupsd_subscription_t *sub);
67
static void cupsd_update_notifier(void);
71
* 'cupsdAddEvent()' - Add an event to the global event cache.
76
cupsd_eventmask_t event, /* I - Event */
77
cupsd_printer_t *dest, /* I - Printer associated with event */
78
cupsd_job_t *job, /* I - Job associated with event */
79
const char *text, /* I - Notification text */
80
...) /* I - Additional arguments as needed */
82
va_list ap; /* Pointer to additional arguments */
83
char ftext[1024]; /* Formatted text buffer */
84
ipp_attribute_t *attr; /* Printer/job attribute */
85
cupsd_event_t *temp; /* New event pointer */
86
cupsd_subscription_t *sub; /* Current subscription */
89
cupsdLogMessage(CUPSD_LOG_DEBUG2,
90
"cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
91
cupsdEventName(event), dest, dest ? dest->name : "",
92
job, job ? job->id : 0, text);
95
* Keep track of events with any OS-supplied notification mechanisms...
101
cupsd_send_dbus(event, dest, job);
102
#endif /* HAVE_DBUS */
105
* Return if we aren't keeping events...
110
cupsdLogMessage(CUPSD_LOG_WARN,
111
"cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
112
cupsdEventName(event), MaxEvents);
117
* Then loop through the subscriptions and add the event to the corresponding
121
for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
123
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
126
* Check if this subscription requires this event...
129
if ((sub->mask & event) != 0 &&
130
(sub->dest == dest || !sub->dest) &&
131
(sub->job == job || !sub->job))
134
* Need this event, so create a new event record...
137
if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
139
cupsdLogMessage(CUPSD_LOG_CRIT,
140
"Unable to allocate memory for event - %s",
146
temp->time = time(NULL);
147
temp->attrs = ippNew();
152
* Add common event notification attributes...
155
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
156
"notify-charset", NULL, "utf-8");
158
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
159
"notify-natural-language", NULL, "en-US");
161
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
162
"notify-subscription-id", sub->id);
164
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
165
"notify-sequence-number", sub->next_event_id);
167
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
168
"notify-subscribed-event", NULL, cupsdEventName(event));
170
if (sub->user_data_len > 0)
171
ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
172
"notify-user-data", sub->user_data,
175
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
176
"printer-up-time", time(NULL));
179
vsnprintf(ftext, sizeof(ftext), text, ap);
182
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
183
"notify-text", NULL, ftext);
188
* Add printer attributes...
191
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
192
"notify-printer-uri", NULL, dest->uri);
194
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
195
"printer-name", NULL, dest->name);
197
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
198
"printer-state", dest->state);
200
if (dest->num_reasons == 0)
201
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
202
IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
203
dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
205
ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
206
IPP_TAG_KEYWORD, "printer-state-reasons",
207
dest->num_reasons, NULL,
208
(const char * const *)dest->reasons);
210
ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
211
"printer-is-accepting-jobs", dest->accepting);
217
* Add job attributes...
220
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
221
"notify-job-id", job->id);
222
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
223
"job-state", job->state_value);
225
if ((attr = ippFindAttribute(job->attrs, "job-name",
226
IPP_TAG_NAME)) != NULL)
227
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
228
"job-name", NULL, attr->values[0].string.text);
230
switch (job->state_value)
232
case IPP_JOB_PENDING :
233
if (dest && dest->state == IPP_PRINTER_STOPPED)
234
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
235
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
238
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
239
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
244
if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
245
ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
246
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
247
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
248
"job-hold-until-specified");
250
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
251
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
255
case IPP_JOB_PROCESSING :
256
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
257
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
261
case IPP_JOB_STOPPED :
262
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
263
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
267
case IPP_JOB_CANCELED :
268
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
269
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
270
"job-canceled-by-user");
273
case IPP_JOB_ABORTED :
274
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
275
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
276
"aborted-by-system");
279
case IPP_JOB_COMPLETED :
280
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
281
IPP_TAG_KEYWORD, "job-state-reasons", NULL,
282
"job-completed-successfully");
286
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
287
"job-impressions-completed",
288
job->sheets ? job->sheets->values[0].integer : 0);
292
* Send the notification for this subscription...
295
cupsd_send_notification(sub, temp);
300
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
302
cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...",
303
cupsdEventName(event));
308
* 'cupsdAddSubscription()' - Add a new subscription object.
311
cupsd_subscription_t * /* O - New subscription object */
312
cupsdAddSubscription(
313
unsigned mask, /* I - Event mask */
314
cupsd_printer_t *dest, /* I - Printer, if any */
315
cupsd_job_t *job, /* I - Job, if any */
316
const char *uri, /* I - notify-recipient-uri, if any */
317
int sub_id) /* I - notify-subscription-id or 0 */
319
cupsd_subscription_t *temp; /* New subscription object */
322
cupsdLogMessage(CUPSD_LOG_DEBUG,
323
"cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
325
mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
326
uri ? uri : "(null)");
329
Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
334
cupsdLogMessage(CUPSD_LOG_CRIT,
335
"Unable to allocate memory for subscriptions - %s",
341
* Limit the number of subscriptions...
344
if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
346
cupsdLogMessage(CUPSD_LOG_DEBUG,
347
"cupsdAddSubscription: Reached MaxSubscriptions %d "
348
"(count=%d)", MaxSubscriptions,
349
cupsArrayCount(Subscriptions));
353
if (MaxSubscriptionsPerJob > 0 && job)
355
int count; /* Number of job subscriptions */
357
for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
360
temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
361
if (temp->job == job)
364
if (count >= MaxSubscriptionsPerJob)
366
cupsdLogMessage(CUPSD_LOG_DEBUG,
367
"cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
368
"for job #%d (count=%d)", MaxSubscriptionsPerJob,
374
if (MaxSubscriptionsPerPrinter > 0 && dest)
376
int count; /* Number of printer subscriptions */
378
for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
381
temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
382
if (temp->dest == dest)
385
if (count >= MaxSubscriptionsPerPrinter)
387
cupsdLogMessage(CUPSD_LOG_DEBUG,
388
"cupsdAddSubscription: Reached "
389
"MaxSubscriptionsPerPrinter %d for %s (count=%d)",
390
MaxSubscriptionsPerPrinter, dest->name, count);
396
* Allocate memory for this subscription...
399
if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
401
cupsdLogMessage(CUPSD_LOG_CRIT,
402
"Unable to allocate memory for subscription object - %s",
408
* Fill in common data...
415
if (sub_id >= NextSubscriptionId)
416
NextSubscriptionId = sub_id + 1;
420
temp->id = NextSubscriptionId;
422
NextSubscriptionId ++;
429
temp->first_event_id = 1;
430
temp->next_event_id = 1;
432
cupsdSetString(&(temp->recipient), uri);
435
* Add the subscription to the array...
438
cupsArrayAdd(Subscriptions, temp);
441
* For RSS subscriptions, run the notifier immediately...
444
if (uri && !strncmp(uri, "rss:", 4))
445
cupsd_start_notifier(temp);
452
* 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
456
cupsdDeleteAllSubscriptions(void)
458
cupsd_subscription_t *sub; /* Subscription */
464
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
466
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
467
cupsdDeleteSubscription(sub, 0);
469
cupsArrayDelete(Subscriptions);
470
Subscriptions = NULL;
475
* 'cupsdDeleteSubscription()' - Delete a subscription object.
479
cupsdDeleteSubscription(
480
cupsd_subscription_t *sub, /* I - Subscription object */
481
int update) /* I - 1 = update subscriptions.conf */
483
int i; /* Looping var */
487
* Close the pipe to the notifier as needed...
494
* Remove subscription from array...
497
cupsArrayRemove(Subscriptions, sub);
503
cupsdClearString(&(sub->owner));
504
cupsdClearString(&(sub->recipient));
508
for (i = 0; i < sub->num_events; i ++)
509
cupsd_delete_event(sub->events[i]);
517
* Update the subscriptions as needed...
521
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
526
* 'cupsdEventName()' - Return a single event name.
529
const char * /* O - Event name */
531
cupsd_eventmask_t event) /* I - Event value */
538
case CUPSD_EVENT_PRINTER_RESTARTED :
539
return ("printer-restarted");
541
case CUPSD_EVENT_PRINTER_SHUTDOWN :
542
return ("printer-shutdown");
544
case CUPSD_EVENT_PRINTER_STOPPED :
545
return ("printer-stopped");
547
case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
548
return ("printer-finishings-changed");
550
case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
551
return ("printer-media-changed");
553
case CUPSD_EVENT_PRINTER_ADDED :
554
return ("printer-added");
556
case CUPSD_EVENT_PRINTER_DELETED :
557
return ("printer-deleted");
559
case CUPSD_EVENT_PRINTER_MODIFIED :
560
return ("printer-modified");
562
case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
563
return ("printer-queue-order-changed");
565
case CUPSD_EVENT_PRINTER_STATE :
566
case CUPSD_EVENT_PRINTER_STATE_CHANGED :
567
return ("printer-state-changed");
569
case CUPSD_EVENT_PRINTER_CONFIG :
570
case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
571
return ("printer-config-changed");
573
case CUPSD_EVENT_PRINTER_CHANGED :
574
return ("printer-changed");
576
case CUPSD_EVENT_JOB_CREATED :
577
return ("job-created");
579
case CUPSD_EVENT_JOB_COMPLETED :
580
return ("job-completed");
582
case CUPSD_EVENT_JOB_STOPPED :
583
return ("job-stopped");
585
case CUPSD_EVENT_JOB_CONFIG_CHANGED :
586
return ("job-config-changed");
588
case CUPSD_EVENT_JOB_PROGRESS :
589
return ("job-progress");
591
case CUPSD_EVENT_JOB_STATE :
592
case CUPSD_EVENT_JOB_STATE_CHANGED :
593
return ("job-state-changed");
595
case CUPSD_EVENT_SERVER_RESTARTED :
596
return ("server-restarted");
598
case CUPSD_EVENT_SERVER_STARTED :
599
return ("server-started");
601
case CUPSD_EVENT_SERVER_STOPPED :
602
return ("server-stopped");
604
case CUPSD_EVENT_SERVER_AUDIT :
605
return ("server-audit");
607
case CUPSD_EVENT_ALL :
614
* 'cupsdEventValue()' - Return the event mask value for a name.
617
cupsd_eventmask_t /* O - Event mask value */
618
cupsdEventValue(const char *name) /* I - Name of event */
620
if (!strcmp(name, "all"))
621
return (CUPSD_EVENT_ALL);
622
else if (!strcmp(name, "printer-restarted"))
623
return (CUPSD_EVENT_PRINTER_RESTARTED);
624
else if (!strcmp(name, "printer-shutdown"))
625
return (CUPSD_EVENT_PRINTER_SHUTDOWN);
626
else if (!strcmp(name, "printer-stopped"))
627
return (CUPSD_EVENT_PRINTER_STOPPED);
628
else if (!strcmp(name, "printer-finishings-changed"))
629
return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
630
else if (!strcmp(name, "printer-media-changed"))
631
return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
632
else if (!strcmp(name, "printer-added"))
633
return (CUPSD_EVENT_PRINTER_ADDED);
634
else if (!strcmp(name, "printer-deleted"))
635
return (CUPSD_EVENT_PRINTER_DELETED);
636
else if (!strcmp(name, "printer-modified"))
637
return (CUPSD_EVENT_PRINTER_MODIFIED);
638
else if (!strcmp(name, "printer-queue-order-changed"))
639
return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
640
else if (!strcmp(name, "printer-state-changed"))
641
return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
642
else if (!strcmp(name, "printer-config-changed"))
643
return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
644
else if (!strcmp(name, "printer-changed"))
645
return (CUPSD_EVENT_PRINTER_CHANGED);
646
else if (!strcmp(name, "job-created"))
647
return (CUPSD_EVENT_JOB_CREATED);
648
else if (!strcmp(name, "job-completed"))
649
return (CUPSD_EVENT_JOB_COMPLETED);
650
else if (!strcmp(name, "job-stopped"))
651
return (CUPSD_EVENT_JOB_STOPPED);
652
else if (!strcmp(name, "job-config-changed"))
653
return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
654
else if (!strcmp(name, "job-progress"))
655
return (CUPSD_EVENT_JOB_PROGRESS);
656
else if (!strcmp(name, "job-state-changed"))
657
return (CUPSD_EVENT_JOB_STATE_CHANGED);
658
else if (!strcmp(name, "server-restarted"))
659
return (CUPSD_EVENT_SERVER_RESTARTED);
660
else if (!strcmp(name, "server-started"))
661
return (CUPSD_EVENT_SERVER_STARTED);
662
else if (!strcmp(name, "server-stopped"))
663
return (CUPSD_EVENT_SERVER_STOPPED);
664
else if (!strcmp(name, "server-audit"))
665
return (CUPSD_EVENT_SERVER_AUDIT);
667
return (CUPSD_EVENT_NONE);
672
* 'cupsdExpireSubscriptions()' - Expire old subscription objects.
676
cupsdExpireSubscriptions(
677
cupsd_printer_t *dest, /* I - Printer, if any */
678
cupsd_job_t *job) /* I - Job, if any */
680
cupsd_subscription_t *sub; /* Current subscription */
681
int update; /* Update subscriptions.conf? */
682
time_t curtime; /* Current time */
685
curtime = time(NULL);
688
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
690
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
691
if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
692
(dest && sub->dest == dest) ||
693
(job && sub->job == job))
695
cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
698
cupsdDeleteSubscription(sub, 0);
704
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
709
* 'cupsdFindSubscription()' - Find a subscription by ID.
712
cupsd_subscription_t * /* O - Subscription object */
713
cupsdFindSubscription(int id) /* I - Subscription ID */
715
cupsd_subscription_t sub; /* Subscription template */
720
return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
725
* 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
729
cupsdLoadAllSubscriptions(void)
731
int i; /* Looping var */
732
cups_file_t *fp; /* subscriptions.conf file */
733
int linenum; /* Current line number */
734
char line[1024], /* Line from file */
735
*value, /* Pointer to value */
736
*valueptr; /* Pointer into value */
737
cupsd_subscription_t *sub; /* Current subscription */
738
int hex; /* Non-zero if reading hex data */
739
int delete_sub; /* Delete subscription? */
743
* Open the subscriptions.conf file...
746
snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
747
if ((fp = cupsFileOpen(line, "r")) == NULL)
750
cupsdLogMessage(CUPSD_LOG_ERROR,
751
"LoadAllSubscriptions: Unable to open %s - %s", line,
757
* Read all of the lines from the file...
764
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
766
if (!strcasecmp(line, "NextSubscriptionId") && value)
769
* NextSubscriptionId NNN
773
if (i >= NextSubscriptionId && i > 0)
774
NextSubscriptionId = i;
776
else if (!strcasecmp(line, "<Subscription"))
782
if (!sub && value && isdigit(value[0] & 255))
784
sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
789
cupsdLogMessage(CUPSD_LOG_ERROR,
790
"Syntax error on line %d of subscriptions.conf.",
795
else if (!strcasecmp(line, "</Subscription>"))
799
cupsdLogMessage(CUPSD_LOG_ERROR,
800
"Syntax error on line %d of subscriptions.conf.",
806
cupsdDeleteSubscription(sub, 0);
813
cupsdLogMessage(CUPSD_LOG_ERROR,
814
"Syntax error on line %d of subscriptions.conf.",
817
else if (!strcasecmp(line, "Events"))
821
* Events name name name ...
826
cupsdLogMessage(CUPSD_LOG_ERROR,
827
"Syntax error on line %d of subscriptions.conf.",
835
* Separate event names...
838
for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
840
while (isspace(*valueptr & 255))
844
* See if the name exists...
847
if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
849
cupsdLogMessage(CUPSD_LOG_ERROR,
850
"Unknown event name \'%s\' on line %d of subscriptions.conf.",
858
else if (!strcasecmp(line, "Owner"))
865
cupsdSetString(&sub->owner, value);
868
cupsdLogMessage(CUPSD_LOG_ERROR,
869
"Syntax error on line %d of subscriptions.conf.",
874
else if (!strcasecmp(line, "Recipient"))
881
cupsdSetString(&sub->recipient, value);
884
cupsdLogMessage(CUPSD_LOG_ERROR,
885
"Syntax error on line %d of subscriptions.conf.",
890
else if (!strcasecmp(line, "JobId"))
896
if (value && isdigit(*value & 255))
898
if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
900
cupsdLogMessage(CUPSD_LOG_ERROR,
901
"Job %s not found on line %d of subscriptions.conf.",
908
cupsdLogMessage(CUPSD_LOG_ERROR,
909
"Syntax error on line %d of subscriptions.conf.",
914
else if (!strcasecmp(line, "PrinterName"))
922
if ((sub->dest = cupsdFindDest(value)) == NULL)
924
cupsdLogMessage(CUPSD_LOG_ERROR,
925
"Printer \'%s\' not found on line %d of subscriptions.conf.",
932
cupsdLogMessage(CUPSD_LOG_ERROR,
933
"Syntax error on line %d of subscriptions.conf.",
938
else if (!strcasecmp(line, "UserData"))
941
* UserData encoded-string
946
for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
948
if (*valueptr == '<' && !hex)
956
if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
958
if (isdigit(valueptr[0]))
959
sub->user_data[i] = (valueptr[0] - '0') << 4;
961
sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
963
if (isdigit(valueptr[1]))
964
sub->user_data[i] |= valueptr[1] - '0';
966
sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
970
if (*valueptr == '>')
980
sub->user_data[i] = *valueptr++;
985
cupsdLogMessage(CUPSD_LOG_ERROR,
986
"Bad UserData \'%s\' on line %d of subscriptions.conf.",
990
sub->user_data_len = i;
994
cupsdLogMessage(CUPSD_LOG_ERROR,
995
"Syntax error on line %d of subscriptions.conf.",
1000
else if (!strcasecmp(line, "LeaseDuration"))
1006
if (value && isdigit(*value & 255))
1008
sub->lease = atoi(value);
1009
sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
1013
cupsdLogMessage(CUPSD_LOG_ERROR,
1014
"Syntax error on line %d of subscriptions.conf.",
1019
else if (!strcasecmp(line, "Interval"))
1025
if (value && isdigit(*value & 255))
1026
sub->interval = atoi(value);
1029
cupsdLogMessage(CUPSD_LOG_ERROR,
1030
"Syntax error on line %d of subscriptions.conf.",
1035
else if (!strcasecmp(line, "ExpirationTime"))
1041
if (value && isdigit(*value & 255))
1042
sub->expire = atoi(value);
1045
cupsdLogMessage(CUPSD_LOG_ERROR,
1046
"Syntax error on line %d of subscriptions.conf.",
1051
else if (!strcasecmp(line, "NextEventId"))
1057
if (value && isdigit(*value & 255))
1058
sub->next_event_id = sub->first_event_id = atoi(value);
1061
cupsdLogMessage(CUPSD_LOG_ERROR,
1062
"Syntax error on line %d of subscriptions.conf.",
1070
* Something else we don't understand...
1073
cupsdLogMessage(CUPSD_LOG_ERROR,
1074
"Unknown configuration directive %s on line %d of subscriptions.conf.",
1084
* 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1088
cupsdSaveAllSubscriptions(void)
1090
int i; /* Looping var */
1091
cups_file_t *fp; /* subscriptions.conf file */
1092
char temp[1024]; /* Temporary string */
1093
char backup[1024]; /* subscriptions.conf.O file */
1094
cupsd_subscription_t *sub; /* Current subscription */
1095
time_t curtime; /* Current time */
1096
struct tm *curdate; /* Current date */
1097
unsigned mask; /* Current event mask */
1098
const char *name; /* Current event name */
1099
int hex; /* Non-zero if we are writing hex data */
1103
* Create the subscriptions.conf file...
1106
snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
1107
snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
1109
if (rename(temp, backup))
1111
if (errno != ENOENT)
1112
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
1116
if ((fp = cupsFileOpen(temp, "w")) == NULL)
1118
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
1121
if (rename(backup, temp))
1122
cupsdLogMessage(CUPSD_LOG_ERROR,
1123
"Unable to restore subscriptions.conf - %s",
1128
cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
1131
* Restrict access to the file...
1134
fchown(cupsFileNumber(fp), getuid(), Group);
1135
fchmod(cupsFileNumber(fp), ConfigFilePerm);
1138
* Write a small header to the file...
1141
curtime = time(NULL);
1142
curdate = localtime(&curtime);
1143
strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1145
cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1146
cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1148
cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
1151
* Write every subscription known to the system...
1154
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1156
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1158
cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1160
if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1163
* Simple event list...
1166
cupsFilePrintf(fp, "Events %s\n", name);
1171
* Complex event list...
1174
cupsFilePuts(fp, "Events");
1176
for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1177
if (sub->mask & mask)
1178
cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1180
cupsFilePuts(fp, "\n");
1184
cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1186
cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1188
cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1190
cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1192
if (sub->user_data_len > 0)
1194
cupsFilePuts(fp, "UserData ");
1196
for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1198
if (sub->user_data[i] < ' ' ||
1199
sub->user_data[i] > 0x7f ||
1200
sub->user_data[i] == '<')
1204
cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1208
cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1214
cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1218
cupsFilePutChar(fp, sub->user_data[i]);
1223
cupsFilePuts(fp, ">\n");
1225
cupsFilePutChar(fp, '\n');
1228
cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1229
cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1230
cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1231
cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1233
cupsFilePuts(fp, "</Subscription>\n");
1241
* 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1245
cupsdStopAllNotifiers(void)
1247
cupsd_subscription_t *sub; /* Current subscription */
1251
* See if we have started any notifiers...
1254
if (!NotifierStatusBuffer)
1258
* Yes, kill any processes that are left...
1261
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1263
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1266
cupsdEndProcess(sub->pid, 0);
1273
* Close the status pipes...
1276
if (NotifierPipes[0] >= 0)
1278
cupsdRemoveSelect(NotifierPipes[0]);
1280
cupsdStatBufDelete(NotifierStatusBuffer);
1282
close(NotifierPipes[0]);
1283
close(NotifierPipes[1]);
1285
NotifierPipes[0] = -1;
1286
NotifierPipes[1] = -1;
1287
NotifierStatusBuffer = NULL;
1293
* 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1296
static int /* O - Result of comparison */
1297
cupsd_compare_subscriptions(
1298
cupsd_subscription_t *first, /* I - First subscription object */
1299
cupsd_subscription_t *second, /* I - Second subscription object */
1300
void *unused) /* I - Unused user data pointer */
1304
return (first->id - second->id);
1309
* 'cupsd_delete_event()' - Delete a single event...
1311
* Oldest events must be deleted first, otherwise the subscription cache
1312
* flushing code will not work properly.
1316
cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1322
ippDelete(event->attrs);
1329
* 'cupsd_send_dbus()' - Send a DBUS notification...
1333
cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
1334
cupsd_printer_t *dest,/* I - Destination, if any */
1335
cupsd_job_t *job) /* I - Job, if any */
1337
DBusError error; /* Error, if any */
1338
DBusMessage *message; /* Message to send */
1339
DBusMessageIter iter; /* Iterator for message data */
1340
const char *what; /* What to send */
1341
static DBusConnection *con = NULL; /* Connection to DBUS server */
1345
* Figure out what to send, if anything...
1348
if (event & CUPSD_EVENT_PRINTER_ADDED)
1349
what = "PrinterAdded";
1350
else if (event & CUPSD_EVENT_PRINTER_DELETED)
1351
what = "PrinterRemoved";
1352
else if (event & CUPSD_EVENT_PRINTER_CHANGED)
1353
what = "QueueChanged";
1354
else if (event & CUPSD_EVENT_JOB_CREATED)
1355
what = "JobQueuedLocal";
1356
else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
1357
job->state_value == IPP_JOB_PROCESSING)
1358
what = "JobStartedLocal";
1363
* Verify connection to DBUS server...
1366
if (con && !dbus_connection_get_is_connected(con))
1368
dbus_connection_unref(con);
1374
dbus_error_init(&error);
1376
con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
1379
dbus_error_free(&error);
1385
* Create and send the new message...
1388
message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
1389
"com.redhat.PrinterSpooler", what);
1391
dbus_message_append_iter_init(message, &iter);
1393
dbus_message_iter_append_string(&iter, dest->name);
1396
dbus_message_iter_append_uint32(&iter, job->id);
1397
dbus_message_iter_append_string(&iter, job->username);
1400
dbus_connection_send(con, message, NULL);
1401
dbus_connection_flush(con);
1402
dbus_message_unref(message);
1404
#endif /* HAVE_DBUS */
1408
* 'cupsd_send_notification()' - Send a notification for the specified event.
1412
cupsd_send_notification(
1413
cupsd_subscription_t *sub, /* I - Subscription object */
1414
cupsd_event_t *event) /* I - Event to send */
1416
ipp_state_t state; /* IPP event state */
1419
cupsdLogMessage(CUPSD_LOG_DEBUG2,
1420
"cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1421
sub, sub->id, event, cupsdEventName(event->event));
1424
* Allocate the events array as needed...
1429
sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
1433
cupsdLogMessage(CUPSD_LOG_CRIT,
1434
"Unable to allocate memory for subscription #%d!",
1441
* Purge an old event as needed...
1444
if (sub->num_events >= MaxEvents)
1447
* Purge the oldest event in the cache...
1450
cupsd_delete_event(sub->events[0]);
1453
sub->first_event_id ++;
1455
memmove(sub->events, sub->events + 1,
1456
sub->num_events * sizeof(cupsd_event_t *));
1460
* Add the event to the subscription. Since the events array is
1461
* always MaxEvents in length, and since we will have already
1462
* removed an event from the subscription cache if we hit the
1463
* event cache limit, we don't need to check for overflow here...
1466
sub->events[sub->num_events] = event;
1470
* Deliver the event...
1478
cupsd_start_notifier(sub);
1480
cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
1485
event->attrs->state = IPP_IDLE;
1487
while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
1488
if (state == IPP_ERROR)
1491
if (state == IPP_ERROR)
1496
* Notifier died, try restarting it...
1499
cupsdLogMessage(CUPSD_LOG_WARN,
1500
"Notifier for subscription %d (%s) went away, "
1502
sub->id, sub->recipient);
1503
cupsdEndProcess(sub->pid, 0);
1510
cupsdLogMessage(CUPSD_LOG_ERROR,
1511
"Unable to send event for subscription %d (%s)!",
1512
sub->id, sub->recipient);
1516
* If we get this far, break out of the loop...
1524
* Bump the event sequence number...
1527
sub->next_event_id ++;
1532
* 'cupsd_start_notifier()' - Start a notifier subprocess...
1536
cupsd_start_notifier(
1537
cupsd_subscription_t *sub) /* I - Subscription object */
1539
int pid; /* Notifier process ID */
1540
int fds[2]; /* Pipe file descriptors */
1541
char *argv[4], /* Command-line arguments */
1542
*envp[MAX_ENV], /* Environment variables */
1543
user_data[128], /* Base-64 encoded user data */
1544
scheme[256], /* notify-recipient-uri scheme */
1545
*ptr, /* Pointer into scheme */
1546
command[1024]; /* Notifier command */
1550
* Extract the scheme name from the recipient URI and point to the
1551
* notifier program...
1554
strlcpy(scheme, sub->recipient, sizeof(scheme));
1555
if ((ptr = strchr(scheme, ':')) != NULL)
1558
snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1561
* Base-64 encode the user data...
1564
httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
1565
sub->user_data_len);
1568
* Setup the argument array...
1572
argv[1] = sub->recipient;
1573
argv[2] = user_data;
1577
* Setup the environment...
1580
cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1583
* Create pipes as needed...
1586
if (!NotifierStatusBuffer)
1589
* Create the status pipe...
1592
if (cupsdOpenPipe(NotifierPipes))
1594
cupsdLogMessage(CUPSD_LOG_ERROR,
1595
"Unable to create pipes for notifier status - %s",
1600
NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1602
cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
1606
if (cupsdOpenPipe(fds))
1608
cupsdLogMessage(CUPSD_LOG_ERROR,
1609
"Unable to create pipes for notifier %s - %s",
1610
scheme, strerror(errno));
1615
* Make sure the delivery pipe is non-blocking...
1618
fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1621
* Create the notifier process...
1624
if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
1625
-1, -1, 0, DefaultProfile, NULL, &pid) < 0)
1628
* Error - can't fork!
1631
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
1632
scheme, strerror(errno));
1634
cupsdClosePipe(fds);
1639
* Fork successful - return the PID...
1642
cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
1655
* 'cupsd_update_notifier()' - Read messages from notifiers.
1659
cupsd_update_notifier(void)
1661
char message[1024]; /* Pointer to message text */
1662
int loglevel; /* Log level for message */
1665
while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1666
message, sizeof(message)))
1668
if (loglevel == CUPSD_LOG_INFO)
1669
cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
1671
if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1678
* End of "$Id: subscriptions.c 9445 2011-01-08 00:03:51Z mike $".