]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/subscriptions.c
Resolve new compiler warnings.
[thirdparty/cups.git] / scheduler / subscriptions.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Subscription routines for the CUPS scheduler.
ef416fc2 3 *
90d3999e
MS
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
90d3999e
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
15#include "cupsd.h"
e00b005a 16#ifdef HAVE_DBUS
17# include <dbus/dbus.h>
2abf387c 18# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
19# define dbus_message_append_iter_init dbus_message_iter_init_append
f7deaa1a 20# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
21# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
2abf387c 22# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
e00b005a 23#endif /* HAVE_DBUS */
ef416fc2 24
25
26/*
27 * Local functions...
28 */
29
30static int cupsd_compare_subscriptions(cupsd_subscription_t *first,
90d3999e
MS
31 cupsd_subscription_t *second,
32 void *unused);
ef416fc2 33static void cupsd_delete_event(cupsd_event_t *event);
e00b005a 34#ifdef HAVE_DBUS
35static void cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
90d3999e 36 cupsd_job_t *job);
e00b005a 37#endif /* HAVE_DBUS */
e1d6a774 38static void cupsd_send_notification(cupsd_subscription_t *sub,
90d3999e 39 cupsd_event_t *event);
ef416fc2 40static void cupsd_start_notifier(cupsd_subscription_t *sub);
f899b121 41static void cupsd_update_notifier(void);
ef416fc2 42
43
44/*
45 * 'cupsdAddEvent()' - Add an event to the global event cache.
46 */
47
48void
49cupsdAddEvent(
50 cupsd_eventmask_t event, /* I - Event */
51 cupsd_printer_t *dest, /* I - Printer associated with event */
90d3999e
MS
52 cupsd_job_t *job, /* I - Job associated with event */
53 const char *text, /* I - Notification text */
ef416fc2 54 ...) /* I - Additional arguments as needed */
55{
56 va_list ap; /* Pointer to additional arguments */
57 char ftext[1024]; /* Formatted text buffer */
fa73b229 58 ipp_attribute_t *attr; /* Printer/job attribute */
ef416fc2 59 cupsd_event_t *temp; /* New event pointer */
60 cupsd_subscription_t *sub; /* Current subscription */
61
62
8ca02f3c 63 cupsdLogMessage(CUPSD_LOG_DEBUG2,
90d3999e 64 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
8ca02f3c 65 cupsdEventName(event), dest, dest ? dest->name : "",
66 job, job ? job->id : 0, text);
67
e00b005a 68 /*
69 * Keep track of events with any OS-supplied notification mechanisms...
70 */
71
fa73b229 72 LastEvent |= event;
73
e00b005a 74#ifdef HAVE_DBUS
75 cupsd_send_dbus(event, dest, job);
76#endif /* HAVE_DBUS */
77
ef416fc2 78 /*
79 * Return if we aren't keeping events...
80 */
81
82 if (MaxEvents <= 0)
83 {
84 cupsdLogMessage(CUPSD_LOG_WARN,
90d3999e
MS
85 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
86 cupsdEventName(event), MaxEvents);
ef416fc2 87 return;
88 }
89
ef416fc2 90 /*
91 * Then loop through the subscriptions and add the event to the corresponding
92 * caches...
93 */
94
ed486911 95 for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
ef416fc2 96 sub;
97 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
98 {
99 /*
100 * Check if this subscription requires this event...
101 */
102
9924fab8 103 if ((sub->mask & event) != 0 && (sub->dest == dest || !sub->dest || sub->job == job))
ef416fc2 104 {
105 /*
ed486911 106 * Need this event, so create a new event record...
ef416fc2 107 */
108
ed486911 109 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
ef416fc2 110 {
ed486911 111 cupsdLogMessage(CUPSD_LOG_CRIT,
90d3999e
MS
112 "Unable to allocate memory for event - %s",
113 strerror(errno));
ed486911 114 return;
115 }
ef416fc2 116
ed486911 117 temp->event = event;
118 temp->time = time(NULL);
119 temp->attrs = ippNew();
90d3999e 120 temp->job = job;
12f89d24
MS
121
122 if (dest)
90d3999e 123 temp->dest = dest;
12f89d24 124 else if (job)
90d3999e 125 temp->dest = dest = cupsdFindPrinter(job->dest);
ef416fc2 126
ed486911 127 /*
128 * Add common event notification attributes...
129 */
ef416fc2 130
ed486911 131 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
90d3999e 132 "notify-charset", NULL, "utf-8");
ef416fc2 133
ed486911 134 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
90d3999e 135 "notify-natural-language", NULL, "en-US");
ef416fc2 136
ed486911 137 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
90d3999e 138 "notify-subscription-id", sub->id);
ef416fc2 139
ed486911 140 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
90d3999e 141 "notify-sequence-number", sub->next_event_id);
ef416fc2 142
ed486911 143 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
90d3999e 144 "notify-subscribed-event", NULL, cupsdEventName(event));
ef416fc2 145
ed486911 146 if (sub->user_data_len > 0)
90d3999e
MS
147 ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
148 "notify-user-data", sub->user_data,
ed486911 149 sub->user_data_len);
ef416fc2 150
ed486911 151 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
90d3999e 152 "printer-up-time", time(NULL));
ef416fc2 153
ed486911 154 va_start(ap, text);
155 vsnprintf(ftext, sizeof(ftext), text, ap);
156 va_end(ap);
ef416fc2 157
ed486911 158 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
90d3999e 159 "notify-text", NULL, ftext);
ef416fc2 160
ed486911 161 if (dest)
162 {
163 /*
164 * Add printer attributes...
165 */
ef416fc2 166
8ef4f507 167 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-printer-uri", NULL, dest->uri);
ef416fc2 168
8ef4f507 169 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "printer-name", NULL, dest->name);
ef416fc2 170
8ef4f507 171 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "printer-state", (int)dest->state);
ef416fc2 172
ed486911 173 if (dest->num_reasons == 0)
8ef4f507 174 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
ed486911 175 else
8ef4f507 176 ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "printer-state-reasons", dest->num_reasons, NULL, (const char * const *)dest->reasons);
ef416fc2 177
8ef4f507 178 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, "printer-is-accepting-jobs", (char)dest->accepting);
ed486911 179 }
fa73b229 180
ed486911 181 if (job)
182 {
183 /*
184 * Add job attributes...
185 */
ef416fc2 186
8ef4f507
MS
187 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-job-id", job->id);
188 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "job-state", (int)job->state_value);
ed486911 189
90d3999e 190 if ((attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
8ef4f507 191 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-name", NULL, attr->values[0].string.text);
ed486911 192
193 switch (job->state_value)
194 {
195 case IPP_JOB_PENDING :
90d3999e
MS
196 if (dest && dest->state == IPP_PRINTER_STOPPED)
197 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "printer-stopped");
198 else
199 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "none");
200 break;
ef416fc2 201
ed486911 202 case IPP_JOB_HELD :
90d3999e 203 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
ed486911 204 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
90d3999e
MS
205 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-hold-until-specified");
206 else
207 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-incoming");
208 break;
ed486911 209
210 case IPP_JOB_PROCESSING :
90d3999e
MS
211 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-printing");
212 break;
ed486911 213
214 case IPP_JOB_STOPPED :
90d3999e
MS
215 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-stopped");
216 break;
ed486911 217
d09495fa 218 case IPP_JOB_CANCELED :
90d3999e
MS
219 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user");
220 break;
ed486911 221
222 case IPP_JOB_ABORTED :
90d3999e
MS
223 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "aborted-by-system");
224 break;
ed486911 225
226 case IPP_JOB_COMPLETED :
90d3999e
MS
227 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully");
228 break;
ef416fc2 229 }
230
8ef4f507 231 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "job-impressions-completed", job->sheets ? job->sheets->values[0].integer : 0);
8ca02f3c 232 }
ef416fc2 233
8ca02f3c 234 /*
235 * Send the notification for this subscription...
236 */
ef416fc2 237
8ca02f3c 238 cupsd_send_notification(sub, temp);
ef416fc2 239 }
240 }
241
242 if (temp)
3dfe78b3 243 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 244 else
8ef4f507 245 cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...", cupsdEventName(event));
ef416fc2 246}
247
248
249/*
250 * 'cupsdAddSubscription()' - Add a new subscription object.
251 */
252
253cupsd_subscription_t * /* O - New subscription object */
254cupsdAddSubscription(
90d3999e 255 unsigned mask, /* I - Event mask */
ef416fc2 256 cupsd_printer_t *dest, /* I - Printer, if any */
90d3999e
MS
257 cupsd_job_t *job, /* I - Job, if any */
258 const char *uri, /* I - notify-recipient-uri, if any */
259 int sub_id) /* I - notify-subscription-id or 0 */
ef416fc2 260{
261 cupsd_subscription_t *temp; /* New subscription object */
262
263
264 cupsdLogMessage(CUPSD_LOG_DEBUG,
90d3999e 265 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
e1d6a774 266 "uri=\"%s\")",
90d3999e 267 mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
d9bca400 268 uri ? uri : "(null)");
ef416fc2 269
270 if (!Subscriptions)
271 Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
90d3999e 272 NULL);
ef416fc2 273
274 if (!Subscriptions)
275 {
276 cupsdLogMessage(CUPSD_LOG_CRIT,
90d3999e
MS
277 "Unable to allocate memory for subscriptions - %s",
278 strerror(errno));
ef416fc2 279 return (NULL);
280 }
281
282 /*
283 * Limit the number of subscriptions...
284 */
285
52f6f666
MS
286 if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
287 {
288 cupsdLogMessage(CUPSD_LOG_DEBUG,
90d3999e 289 "cupsdAddSubscription: Reached MaxSubscriptions %d "
52f6f666
MS
290 "(count=%d)", MaxSubscriptions,
291 cupsArrayCount(Subscriptions));
ef416fc2 292 return (NULL);
52f6f666
MS
293 }
294
295 if (MaxSubscriptionsPerJob > 0 && job)
296 {
90d3999e 297 int count; /* Number of job subscriptions */
52f6f666
MS
298
299 for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
90d3999e
MS
300 count = 0;
301 temp;
52f6f666
MS
302 temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
303 if (temp->job == job)
90d3999e 304 count ++;
52f6f666
MS
305
306 if (count >= MaxSubscriptionsPerJob)
307 {
308 cupsdLogMessage(CUPSD_LOG_DEBUG,
309 "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
310 "for job #%d (count=%d)", MaxSubscriptionsPerJob,
311 job->id, count);
312 return (NULL);
313 }
314 }
315
316 if (MaxSubscriptionsPerPrinter > 0 && dest)
317 {
90d3999e 318 int count; /* Number of printer subscriptions */
52f6f666
MS
319
320 for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
90d3999e
MS
321 count = 0;
322 temp;
52f6f666
MS
323 temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
324 if (temp->dest == dest)
90d3999e 325 count ++;
52f6f666
MS
326
327 if (count >= MaxSubscriptionsPerPrinter)
328 {
329 cupsdLogMessage(CUPSD_LOG_DEBUG,
330 "cupsdAddSubscription: Reached "
331 "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
332 MaxSubscriptionsPerPrinter, dest->name, count);
333 return (NULL);
334 }
335 }
ef416fc2 336
337 /*
338 * Allocate memory for this subscription...
339 */
340
341 if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
342 {
343 cupsdLogMessage(CUPSD_LOG_CRIT,
90d3999e
MS
344 "Unable to allocate memory for subscription object - %s",
345 strerror(errno));
ef416fc2 346 return (NULL);
347 }
348
349 /*
350 * Fill in common data...
351 */
352
353 if (sub_id)
354 {
355 temp->id = sub_id;
356
357 if (sub_id >= NextSubscriptionId)
358 NextSubscriptionId = sub_id + 1;
359 }
360 else
361 {
362 temp->id = NextSubscriptionId;
363
364 NextSubscriptionId ++;
365 }
366
90d3999e
MS
367 temp->mask = mask;
368 temp->dest = dest;
369 temp->job = job;
370 temp->pipe = -1;
ef416fc2 371 temp->first_event_id = 1;
372 temp->next_event_id = 1;
373
374 cupsdSetString(&(temp->recipient), uri);
375
376 /*
377 * Add the subscription to the array...
378 */
379
380 cupsArrayAdd(Subscriptions, temp);
381
ee571f26
MS
382 /*
383 * For RSS subscriptions, run the notifier immediately...
384 */
385
386 if (uri && !strncmp(uri, "rss:", 4))
387 cupsd_start_notifier(temp);
388
ef416fc2 389 return (temp);
390}
391
392
ef416fc2 393/*
394 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
395 */
396
397void
398cupsdDeleteAllSubscriptions(void)
399{
400 cupsd_subscription_t *sub; /* Subscription */
401
402
403 if (!Subscriptions)
404 return;
405
406 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
407 sub;
408 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
409 cupsdDeleteSubscription(sub, 0);
410
411 cupsArrayDelete(Subscriptions);
412 Subscriptions = NULL;
413}
414
415
416/*
417 * 'cupsdDeleteSubscription()' - Delete a subscription object.
418 */
419
420void
421cupsdDeleteSubscription(
422 cupsd_subscription_t *sub, /* I - Subscription object */
90d3999e 423 int update) /* I - 1 = update subscriptions.conf */
ef416fc2 424{
425 /*
426 * Close the pipe to the notifier as needed...
427 */
428
429 if (sub->pipe >= 0)
430 close(sub->pipe);
431
432 /*
433 * Remove subscription from array...
434 */
435
436 cupsArrayRemove(Subscriptions, sub);
437
438 /*
439 * Free memory...
440 */
441
442 cupsdClearString(&(sub->owner));
443 cupsdClearString(&(sub->recipient));
444
10d09e33 445 cupsArrayDelete(sub->events);
ef416fc2 446
447 free(sub);
448
449 /*
450 * Update the subscriptions as needed...
451 */
452
453 if (update)
3dfe78b3 454 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 455}
456
457
458/*
459 * 'cupsdEventName()' - Return a single event name.
460 */
461
462const char * /* O - Event name */
463cupsdEventName(
464 cupsd_eventmask_t event) /* I - Event value */
465{
466 switch (event)
467 {
468 default :
90d3999e 469 return (NULL);
ef416fc2 470
471 case CUPSD_EVENT_PRINTER_RESTARTED :
90d3999e 472 return ("printer-restarted");
ef416fc2 473
474 case CUPSD_EVENT_PRINTER_SHUTDOWN :
90d3999e 475 return ("printer-shutdown");
ef416fc2 476
477 case CUPSD_EVENT_PRINTER_STOPPED :
90d3999e 478 return ("printer-stopped");
ef416fc2 479
480 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
90d3999e 481 return ("printer-finishings-changed");
ef416fc2 482
483 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
90d3999e 484 return ("printer-media-changed");
ef416fc2 485
486 case CUPSD_EVENT_PRINTER_ADDED :
90d3999e 487 return ("printer-added");
ef416fc2 488
489 case CUPSD_EVENT_PRINTER_DELETED :
90d3999e 490 return ("printer-deleted");
ef416fc2 491
492 case CUPSD_EVENT_PRINTER_MODIFIED :
90d3999e 493 return ("printer-modified");
ef416fc2 494
d9bca400 495 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
90d3999e 496 return ("printer-queue-order-changed");
d9bca400
MS
497
498 case CUPSD_EVENT_PRINTER_STATE :
ef416fc2 499 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
90d3999e 500 return ("printer-state-changed");
ef416fc2 501
d9bca400 502 case CUPSD_EVENT_PRINTER_CONFIG :
ef416fc2 503 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
90d3999e 504 return ("printer-config-changed");
ef416fc2 505
506 case CUPSD_EVENT_PRINTER_CHANGED :
90d3999e 507 return ("printer-changed");
ef416fc2 508
509 case CUPSD_EVENT_JOB_CREATED :
90d3999e 510 return ("job-created");
ef416fc2 511
512 case CUPSD_EVENT_JOB_COMPLETED :
90d3999e 513 return ("job-completed");
ef416fc2 514
515 case CUPSD_EVENT_JOB_STOPPED :
90d3999e 516 return ("job-stopped");
ef416fc2 517
518 case CUPSD_EVENT_JOB_CONFIG_CHANGED :
90d3999e 519 return ("job-config-changed");
ef416fc2 520
521 case CUPSD_EVENT_JOB_PROGRESS :
90d3999e 522 return ("job-progress");
ef416fc2 523
e1d6a774 524 case CUPSD_EVENT_JOB_STATE :
ef416fc2 525 case CUPSD_EVENT_JOB_STATE_CHANGED :
90d3999e 526 return ("job-state-changed");
ef416fc2 527
528 case CUPSD_EVENT_SERVER_RESTARTED :
90d3999e 529 return ("server-restarted");
ef416fc2 530
531 case CUPSD_EVENT_SERVER_STARTED :
90d3999e 532 return ("server-started");
ef416fc2 533
534 case CUPSD_EVENT_SERVER_STOPPED :
90d3999e 535 return ("server-stopped");
ef416fc2 536
537 case CUPSD_EVENT_SERVER_AUDIT :
90d3999e 538 return ("server-audit");
ef416fc2 539
540 case CUPSD_EVENT_ALL :
90d3999e 541 return ("all");
ef416fc2 542 }
543}
544
545
546/*
547 * 'cupsdEventValue()' - Return the event mask value for a name.
548 */
549
550cupsd_eventmask_t /* O - Event mask value */
551cupsdEventValue(const char *name) /* I - Name of event */
552{
553 if (!strcmp(name, "all"))
554 return (CUPSD_EVENT_ALL);
555 else if (!strcmp(name, "printer-restarted"))
556 return (CUPSD_EVENT_PRINTER_RESTARTED);
557 else if (!strcmp(name, "printer-shutdown"))
558 return (CUPSD_EVENT_PRINTER_SHUTDOWN);
559 else if (!strcmp(name, "printer-stopped"))
560 return (CUPSD_EVENT_PRINTER_STOPPED);
561 else if (!strcmp(name, "printer-finishings-changed"))
562 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
563 else if (!strcmp(name, "printer-media-changed"))
564 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
565 else if (!strcmp(name, "printer-added"))
566 return (CUPSD_EVENT_PRINTER_ADDED);
567 else if (!strcmp(name, "printer-deleted"))
568 return (CUPSD_EVENT_PRINTER_DELETED);
569 else if (!strcmp(name, "printer-modified"))
570 return (CUPSD_EVENT_PRINTER_MODIFIED);
d9bca400
MS
571 else if (!strcmp(name, "printer-queue-order-changed"))
572 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
ef416fc2 573 else if (!strcmp(name, "printer-state-changed"))
574 return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
575 else if (!strcmp(name, "printer-config-changed"))
576 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
577 else if (!strcmp(name, "printer-changed"))
578 return (CUPSD_EVENT_PRINTER_CHANGED);
579 else if (!strcmp(name, "job-created"))
580 return (CUPSD_EVENT_JOB_CREATED);
581 else if (!strcmp(name, "job-completed"))
582 return (CUPSD_EVENT_JOB_COMPLETED);
583 else if (!strcmp(name, "job-stopped"))
584 return (CUPSD_EVENT_JOB_STOPPED);
585 else if (!strcmp(name, "job-config-changed"))
586 return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
587 else if (!strcmp(name, "job-progress"))
588 return (CUPSD_EVENT_JOB_PROGRESS);
589 else if (!strcmp(name, "job-state-changed"))
590 return (CUPSD_EVENT_JOB_STATE_CHANGED);
591 else if (!strcmp(name, "server-restarted"))
592 return (CUPSD_EVENT_SERVER_RESTARTED);
593 else if (!strcmp(name, "server-started"))
594 return (CUPSD_EVENT_SERVER_STARTED);
595 else if (!strcmp(name, "server-stopped"))
596 return (CUPSD_EVENT_SERVER_STOPPED);
597 else if (!strcmp(name, "server-audit"))
598 return (CUPSD_EVENT_SERVER_AUDIT);
599 else
600 return (CUPSD_EVENT_NONE);
601}
602
603
604/*
605 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
606 */
607
608void
609cupsdExpireSubscriptions(
610 cupsd_printer_t *dest, /* I - Printer, if any */
90d3999e 611 cupsd_job_t *job) /* I - Job, if any */
ef416fc2 612{
613 cupsd_subscription_t *sub; /* Current subscription */
614 int update; /* Update subscriptions.conf? */
615 time_t curtime; /* Current time */
616
617
47b3bd03
MS
618 if (cupsArrayCount(Subscriptions) == 0)
619 return;
620
ef416fc2 621 curtime = time(NULL);
622 update = 0;
623
79d3cd17
MS
624 cupsdLogMessage(CUPSD_LOG_INFO, "Expiring subscriptions...");
625
ef416fc2 626 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
627 sub;
628 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
bd7854cb 629 if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
90d3999e 630 (dest && sub->dest == dest) ||
ef416fc2 631 (job && sub->job == job))
632 {
bd7854cb 633 cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
90d3999e 634 sub->id);
ef416fc2 635
636 cupsdDeleteSubscription(sub, 0);
637
638 update = 1;
639 }
640
641 if (update)
3dfe78b3 642 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 643}
644
645
646/*
647 * 'cupsdFindSubscription()' - Find a subscription by ID.
648 */
649
650cupsd_subscription_t * /* O - Subscription object */
651cupsdFindSubscription(int id) /* I - Subscription ID */
652{
653 cupsd_subscription_t sub; /* Subscription template */
654
655
656 sub.id = id;
657
658 return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
659}
660
661
662/*
663 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
664 */
665
666void
667cupsdLoadAllSubscriptions(void)
668{
669 int i; /* Looping var */
670 cups_file_t *fp; /* subscriptions.conf file */
671 int linenum; /* Current line number */
672 char line[1024], /* Line from file */
673 *value, /* Pointer to value */
674 *valueptr; /* Pointer into value */
675 cupsd_subscription_t *sub; /* Current subscription */
676 int hex; /* Non-zero if reading hex data */
677 int delete_sub; /* Delete subscription? */
678
679
680 /*
681 * Open the subscriptions.conf file...
682 */
683
684 snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
321d8d57 685 if ((fp = cupsdOpenConfFile(line)) == NULL)
ef416fc2 686 return;
ef416fc2 687
688 /*
689 * Read all of the lines from the file...
690 */
691
692 linenum = 0;
90d3999e 693 sub = NULL;
ef416fc2 694 delete_sub = 0;
695
696 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
697 {
88f9aafc 698 if (!_cups_strcasecmp(line, "NextSubscriptionId") && value)
480ef0fe 699 {
700 /*
701 * NextSubscriptionId NNN
702 */
703
704 i = atoi(value);
705 if (i >= NextSubscriptionId && i > 0)
90d3999e 706 NextSubscriptionId = i;
480ef0fe 707 }
88f9aafc 708 else if (!_cups_strcasecmp(line, "<Subscription"))
ef416fc2 709 {
710 /*
711 * <Subscription #>
712 */
713
714 if (!sub && value && isdigit(value[0] & 255))
715 {
90d3999e
MS
716 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
717 atoi(value));
ef416fc2 718 }
719 else
720 {
90d3999e
MS
721 cupsdLogMessage(CUPSD_LOG_ERROR,
722 "Syntax error on line %d of subscriptions.conf.",
723 linenum);
724 break;
ef416fc2 725 }
726 }
88f9aafc 727 else if (!_cups_strcasecmp(line, "</Subscription>"))
ef416fc2 728 {
729 if (!sub)
730 {
90d3999e
MS
731 cupsdLogMessage(CUPSD_LOG_ERROR,
732 "Syntax error on line %d of subscriptions.conf.",
733 linenum);
734 break;
ef416fc2 735 }
736
737 if (delete_sub)
90d3999e 738 cupsdDeleteSubscription(sub, 0);
ef416fc2 739
90d3999e 740 sub = NULL;
ef416fc2 741 delete_sub = 0;
742 }
743 else if (!sub)
744 {
745 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
746 "Syntax error on line %d of subscriptions.conf.",
747 linenum);
ef416fc2 748 }
88f9aafc 749 else if (!_cups_strcasecmp(line, "Events"))
ef416fc2 750 {
751 /*
752 * Events name
753 * Events name name name ...
754 */
755
756 if (!value)
757 {
758 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
759 "Syntax error on line %d of subscriptions.conf.",
760 linenum);
91c84a35 761 break;
ef416fc2 762 }
763
764 while (*value)
765 {
766 /*
90d3999e 767 * Separate event names...
ef416fc2 768 */
769
90d3999e 770 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
ef416fc2 771
772 while (isspace(*valueptr & 255))
773 *valueptr++ = '\0';
774
775 /*
90d3999e 776 * See if the name exists...
ef416fc2 777 */
778
90d3999e 779 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
ef416fc2 780 {
781 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
782 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
783 value, linenum);
91c84a35 784 break;
ef416fc2 785 }
786
787 value = valueptr;
788 }
789 }
88f9aafc 790 else if (!_cups_strcasecmp(line, "Owner"))
ef416fc2 791 {
792 /*
793 * Owner
794 */
795
796 if (value)
797 cupsdSetString(&sub->owner, value);
798 else
799 {
800 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
801 "Syntax error on line %d of subscriptions.conf.",
802 linenum);
91c84a35 803 break;
ef416fc2 804 }
805 }
88f9aafc 806 else if (!_cups_strcasecmp(line, "Recipient"))
ef416fc2 807 {
808 /*
809 * Recipient uri
810 */
811
812 if (value)
813 cupsdSetString(&sub->recipient, value);
814 else
815 {
816 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
817 "Syntax error on line %d of subscriptions.conf.",
818 linenum);
91c84a35 819 break;
ef416fc2 820 }
821 }
88f9aafc 822 else if (!_cups_strcasecmp(line, "JobId"))
ef416fc2 823 {
824 /*
825 * JobId #
826 */
827
828 if (value && isdigit(*value & 255))
829 {
90d3999e 830 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
ef416fc2 831 {
832 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
833 "Job %s not found on line %d of subscriptions.conf.",
834 value, linenum);
ef416fc2 835 delete_sub = 1;
836 }
837 }
838 else
839 {
840 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
841 "Syntax error on line %d of subscriptions.conf.",
842 linenum);
91c84a35 843 break;
ef416fc2 844 }
845 }
88f9aafc 846 else if (!_cups_strcasecmp(line, "PrinterName"))
ef416fc2 847 {
848 /*
849 * PrinterName name
850 */
851
852 if (value)
853 {
90d3999e 854 if ((sub->dest = cupsdFindDest(value)) == NULL)
ef416fc2 855 {
856 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
857 "Printer \'%s\' not found on line %d of subscriptions.conf.",
858 value, linenum);
ef416fc2 859 delete_sub = 1;
860 }
861 }
862 else
863 {
864 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
865 "Syntax error on line %d of subscriptions.conf.",
866 linenum);
91c84a35 867 break;
ef416fc2 868 }
869 }
88f9aafc 870 else if (!_cups_strcasecmp(line, "UserData"))
ef416fc2 871 {
872 /*
873 * UserData encoded-string
874 */
875
876 if (value)
877 {
90d3999e 878 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
ef416fc2 879 {
880 if (*valueptr == '<' && !hex)
881 {
882 hex = 1;
883 valueptr ++;
884 }
885
886 if (hex)
887 {
888 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
889 {
890 if (isdigit(valueptr[0]))
90d3999e 891 sub->user_data[i] = (unsigned char)((valueptr[0] - '0') << 4);
ef416fc2 892 else
90d3999e 893 sub->user_data[i] = (unsigned char)((tolower(valueptr[0]) - 'a' + 10) << 4);
ef416fc2 894
895 if (isdigit(valueptr[1]))
90d3999e 896 sub->user_data[i] |= valueptr[1] - '0';
ef416fc2 897 else
90d3999e 898 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
ef416fc2 899
90d3999e 900 valueptr += 2;
ef416fc2 901
902 if (*valueptr == '>')
903 {
90d3999e 904 hex = 0;
ef416fc2 905 valueptr ++;
906 }
907 }
908 else
909 break;
910 }
911 else
7e86f2f6 912 sub->user_data[i] = (unsigned char)*valueptr++;
ef416fc2 913 }
914
915 if (*valueptr)
916 {
917 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
918 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
919 value, linenum);
ef416fc2 920 }
921 else
922 sub->user_data_len = i;
923 }
924 else
925 {
926 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
927 "Syntax error on line %d of subscriptions.conf.",
928 linenum);
91c84a35 929 break;
ef416fc2 930 }
931 }
88f9aafc 932 else if (!_cups_strcasecmp(line, "LeaseDuration"))
ef416fc2 933 {
934 /*
935 * LeaseDuration #
936 */
937
938 if (value && isdigit(*value & 255))
bd7854cb 939 {
90d3999e
MS
940 sub->lease = atoi(value);
941 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
bd7854cb 942 }
ef416fc2 943 else
944 {
945 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
946 "Syntax error on line %d of subscriptions.conf.",
947 linenum);
91c84a35 948 break;
ef416fc2 949 }
950 }
88f9aafc 951 else if (!_cups_strcasecmp(line, "Interval"))
ef416fc2 952 {
953 /*
954 * Interval #
955 */
956
957 if (value && isdigit(*value & 255))
90d3999e 958 sub->interval = atoi(value);
ef416fc2 959 else
960 {
961 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
962 "Syntax error on line %d of subscriptions.conf.",
963 linenum);
91c84a35 964 break;
ef416fc2 965 }
966 }
88f9aafc 967 else if (!_cups_strcasecmp(line, "ExpirationTime"))
ef416fc2 968 {
969 /*
970 * ExpirationTime #
971 */
972
973 if (value && isdigit(*value & 255))
90d3999e 974 sub->expire = atoi(value);
ef416fc2 975 else
976 {
977 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
978 "Syntax error on line %d of subscriptions.conf.",
979 linenum);
91c84a35 980 break;
ef416fc2 981 }
982 }
88f9aafc 983 else if (!_cups_strcasecmp(line, "NextEventId"))
ef416fc2 984 {
985 /*
986 * NextEventId #
987 */
988
989 if (value && isdigit(*value & 255))
90d3999e 990 sub->next_event_id = sub->first_event_id = atoi(value);
ef416fc2 991 else
992 {
993 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
994 "Syntax error on line %d of subscriptions.conf.",
995 linenum);
91c84a35 996 break;
ef416fc2 997 }
998 }
999 else
1000 {
1001 /*
1002 * Something else we don't understand...
1003 */
1004
1005 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e
MS
1006 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1007 line, linenum);
ef416fc2 1008 }
1009 }
1010
1011 cupsFileClose(fp);
1012}
1013
1014
1015/*
1016 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1017 */
1018
1019void
1020cupsdSaveAllSubscriptions(void)
1021{
1022 int i; /* Looping var */
1023 cups_file_t *fp; /* subscriptions.conf file */
90d3999e 1024 char filename[1024], /* subscriptions.conf filename */
321d8d57 1025 temp[1024]; /* Temporary string */
ef416fc2 1026 cupsd_subscription_t *sub; /* Current subscription */
1027 time_t curtime; /* Current time */
1028 struct tm *curdate; /* Current date */
1029 unsigned mask; /* Current event mask */
1030 const char *name; /* Current event name */
1031 int hex; /* Non-zero if we are writing hex data */
1032
1033
1034 /*
1035 * Create the subscriptions.conf file...
1036 */
1037
321d8d57 1038 snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot);
ef416fc2 1039
321d8d57 1040 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
ef416fc2 1041 return;
ef416fc2 1042
321d8d57 1043 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
ef416fc2 1044
1045 /*
1046 * Write a small header to the file...
1047 */
1048
1049 curtime = time(NULL);
1050 curdate = localtime(&curtime);
1051 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1052
1053 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1054 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1055
480ef0fe 1056 cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
1057
ef416fc2 1058 /*
1059 * Write every subscription known to the system...
1060 */
1061
1062 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1063 sub;
1064 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1065 {
1066 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1067
1068 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1069 {
1070 /*
1071 * Simple event list...
1072 */
1073
1074 cupsFilePrintf(fp, "Events %s\n", name);
1075 }
1076 else
1077 {
1078 /*
1079 * Complex event list...
1080 */
1081
1082 cupsFilePuts(fp, "Events");
1083
1084 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
90d3999e 1085 if (sub->mask & mask)
ef416fc2 1086 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1087
1088 cupsFilePuts(fp, "\n");
1089 }
1090
1091 if (sub->owner)
1092 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1093 if (sub->recipient)
1094 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1095 if (sub->job)
1096 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1097 if (sub->dest)
1098 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1099
1100 if (sub->user_data_len > 0)
1101 {
1102 cupsFilePuts(fp, "UserData ");
1103
1104 for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1105 {
90d3999e 1106 if (sub->user_data[i] < ' ' ||
ef416fc2 1107 sub->user_data[i] > 0x7f ||
1108 sub->user_data[i] == '<')
1109 {
1110 if (!hex)
1111 {
1112 cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1113 hex = 1;
1114 }
1115 else
1116 cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1117 }
1118 else
1119 {
1120 if (hex)
1121 {
1122 cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1123 hex = 0;
1124 }
1125 else
1126 cupsFilePutChar(fp, sub->user_data[i]);
1127 }
1128 }
1129
1130 if (hex)
90d3999e 1131 cupsFilePuts(fp, ">\n");
ef416fc2 1132 else
90d3999e 1133 cupsFilePutChar(fp, '\n');
ef416fc2 1134 }
1135
1136 cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1137 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1138 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1139 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1140
1141 cupsFilePuts(fp, "</Subscription>\n");
1142 }
1143
321d8d57 1144 cupsdCloseCreatedConfFile(fp, filename);
ef416fc2 1145}
1146
1147
ef416fc2 1148/*
1149 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1150 */
1151
1152void
1153cupsdStopAllNotifiers(void)
1154{
1155 cupsd_subscription_t *sub; /* Current subscription */
1156
1157
1158 /*
1159 * See if we have started any notifiers...
1160 */
1161
1162 if (!NotifierStatusBuffer)
1163 return;
1164
1165 /*
fa73b229 1166 * Yes, kill any processes that are left...
ef416fc2 1167 */
1168
1169 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1170 sub;
1171 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1172 if (sub->pid)
1173 {
1174 cupsdEndProcess(sub->pid, 0);
1175
1176 close(sub->pipe);
1177 sub->pipe = -1;
1178 }
1179
1180 /*
1181 * Close the status pipes...
1182 */
1183
1184 if (NotifierPipes[0] >= 0)
1185 {
f7deaa1a 1186 cupsdRemoveSelect(NotifierPipes[0]);
ef416fc2 1187
1188 cupsdStatBufDelete(NotifierStatusBuffer);
1189
1190 close(NotifierPipes[0]);
1191 close(NotifierPipes[1]);
1192
1193 NotifierPipes[0] = -1;
1194 NotifierPipes[1] = -1;
1195 NotifierStatusBuffer = NULL;
1196 }
1197}
1198
1199
ef416fc2 1200/*
1201 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1202 */
1203
1204static int /* O - Result of comparison */
1205cupsd_compare_subscriptions(
1206 cupsd_subscription_t *first, /* I - First subscription object */
1207 cupsd_subscription_t *second, /* I - Second subscription object */
90d3999e 1208 void *unused) /* I - Unused user data pointer */
ef416fc2 1209{
1210 (void)unused;
1211
1212 return (first->id - second->id);
1213}
1214
1215
1216/*
1217 * 'cupsd_delete_event()' - Delete a single event...
1218 *
1219 * Oldest events must be deleted first, otherwise the subscription cache
1220 * flushing code will not work properly.
1221 */
1222
1223static void
1224cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1225{
ef416fc2 1226 /*
1227 * Free memory...
1228 */
1229
1230 ippDelete(event->attrs);
1231 free(event);
1232}
1233
1234
e00b005a 1235#ifdef HAVE_DBUS
1236/*
1237 * 'cupsd_send_dbus()' - Send a DBUS notification...
1238 */
1239
1240static void
1241cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
90d3999e
MS
1242 cupsd_printer_t *dest,/* I - Destination, if any */
1243 cupsd_job_t *job) /* I - Job, if any */
e00b005a 1244{
1245 DBusError error; /* Error, if any */
1246 DBusMessage *message; /* Message to send */
1247 DBusMessageIter iter; /* Iterator for message data */
1248 const char *what; /* What to send */
90d3999e 1249 static DBusConnection *con = NULL; /* Connection to DBUS server */
e00b005a 1250
1251
1252 /*
1253 * Figure out what to send, if anything...
1254 */
1255
1256 if (event & CUPSD_EVENT_PRINTER_ADDED)
1257 what = "PrinterAdded";
1258 else if (event & CUPSD_EVENT_PRINTER_DELETED)
1259 what = "PrinterRemoved";
1260 else if (event & CUPSD_EVENT_PRINTER_CHANGED)
1261 what = "QueueChanged";
1262 else if (event & CUPSD_EVENT_JOB_CREATED)
1263 what = "JobQueuedLocal";
1264 else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
90d3999e 1265 job->state_value == IPP_JOB_PROCESSING)
e00b005a 1266 what = "JobStartedLocal";
1267 else
1268 return;
1269
1270 /*
1271 * Verify connection to DBUS server...
1272 */
1273
1274 if (con && !dbus_connection_get_is_connected(con))
1275 {
1276 dbus_connection_unref(con);
1277 con = NULL;
1278 }
1279
1280 if (!con)
1281 {
1282 dbus_error_init(&error);
1283
1284 con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
1285 if (!con)
1286 {
1287 dbus_error_free(&error);
1288 return;
1289 }
1290 }
1291
1292 /*
1293 * Create and send the new message...
1294 */
1295
1296 message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
1297 "com.redhat.PrinterSpooler", what);
1298
2abf387c 1299 dbus_message_append_iter_init(message, &iter);
e00b005a 1300 if (dest)
f7deaa1a 1301 dbus_message_iter_append_string(&iter, dest->name);
e00b005a 1302 if (job)
1303 {
f7deaa1a 1304 dbus_message_iter_append_uint32(&iter, job->id);
1305 dbus_message_iter_append_string(&iter, job->username);
e00b005a 1306 }
1307
1308 dbus_connection_send(con, message, NULL);
1309 dbus_connection_flush(con);
1310 dbus_message_unref(message);
1311}
1312#endif /* HAVE_DBUS */
1313
1314
e1d6a774 1315/*
1316 * 'cupsd_send_notification()' - Send a notification for the specified event.
1317 */
1318
1319static void
1320cupsd_send_notification(
1321 cupsd_subscription_t *sub, /* I - Subscription object */
90d3999e 1322 cupsd_event_t *event) /* I - Event to send */
e1d6a774 1323{
1324 ipp_state_t state; /* IPP event state */
1325
1326
8ca02f3c 1327 cupsdLogMessage(CUPSD_LOG_DEBUG2,
90d3999e
MS
1328 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1329 sub, sub->id, event, cupsdEventName(event->event));
e1d6a774 1330
1331 /*
1332 * Allocate the events array as needed...
1333 */
1334
1335 if (!sub->events)
1336 {
10d09e33 1337 sub->events = cupsArrayNew3((cups_array_func_t)NULL, NULL,
90d3999e 1338 (cups_ahash_func_t)NULL, 0,
10d09e33
MS
1339 (cups_acopy_func_t)NULL,
1340 (cups_afree_func_t)cupsd_delete_event);
e1d6a774 1341
1342 if (!sub->events)
1343 {
1344 cupsdLogMessage(CUPSD_LOG_CRIT,
90d3999e
MS
1345 "Unable to allocate memory for subscription #%d!",
1346 sub->id);
e1d6a774 1347 return;
1348 }
1349 }
1350
ed486911 1351 /*
1352 * Purge an old event as needed...
1353 */
1354
10d09e33 1355 if (cupsArrayCount(sub->events) >= MaxEvents)
ed486911 1356 {
1357 /*
1358 * Purge the oldest event in the cache...
1359 */
1360
10d09e33 1361 cupsArrayRemove(sub->events, cupsArrayFirst(sub->events));
ed486911 1362
ed486911 1363 sub->first_event_id ++;
ed486911 1364 }
1365
e1d6a774 1366 /*
90d3999e 1367 * Add the event to the subscription. Since the events array is
e1d6a774 1368 * always MaxEvents in length, and since we will have already
1369 * removed an event from the subscription cache if we hit the
1370 * event cache limit, we don't need to check for overflow here...
1371 */
1372
10d09e33 1373 cupsArrayAdd(sub->events, event);
e1d6a774 1374
1375 /*
1376 * Deliver the event...
1377 */
1378
1379 if (sub->recipient)
1380 {
8ca02f3c 1381 for (;;)
1382 {
1383 if (sub->pipe < 0)
1384 cupsd_start_notifier(sub);
e1d6a774 1385
8ca02f3c 1386 cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
1387
1388 if (sub->pipe < 0)
1389 break;
e1d6a774 1390
e1d6a774 1391 event->attrs->state = IPP_IDLE;
1392
1393 while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
90d3999e 1394 if (state == IPP_ERROR)
e1d6a774 1395 break;
1396
1397 if (state == IPP_ERROR)
8ca02f3c 1398 {
90d3999e 1399 if (errno == EPIPE)
8ca02f3c 1400 {
1401 /*
1402 * Notifier died, try restarting it...
1403 */
1404
90d3999e
MS
1405 cupsdLogMessage(CUPSD_LOG_WARN,
1406 "Notifier for subscription %d (%s) went away, "
8ca02f3c 1407 "retrying!",
1408 sub->id, sub->recipient);
1409 cupsdEndProcess(sub->pid, 0);
1410
1411 close(sub->pipe);
1412 sub->pipe = -1;
90d3999e 1413 continue;
8ca02f3c 1414 }
1415
90d3999e
MS
1416 cupsdLogMessage(CUPSD_LOG_ERROR,
1417 "Unable to send event for subscription %d (%s)!",
e1d6a774 1418 sub->id, sub->recipient);
8ca02f3c 1419 }
1420
1421 /*
1422 * If we get this far, break out of the loop...
1423 */
1424
1425 break;
e1d6a774 1426 }
1427 }
1428
1429 /*
1430 * Bump the event sequence number...
1431 */
1432
1433 sub->next_event_id ++;
1434}
1435
1436
ef416fc2 1437/*
1438 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1439 */
1440
1441static void
1442cupsd_start_notifier(
1443 cupsd_subscription_t *sub) /* I - Subscription object */
1444{
1445 int pid; /* Notifier process ID */
1446 int fds[2]; /* Pipe file descriptors */
ef416fc2 1447 char *argv[4], /* Command-line arguments */
e00b005a 1448 *envp[MAX_ENV], /* Environment variables */
ef416fc2 1449 user_data[128], /* Base-64 encoded user data */
1450 scheme[256], /* notify-recipient-uri scheme */
1451 *ptr, /* Pointer into scheme */
1452 command[1024]; /* Notifier command */
1453
1454
1455 /*
1456 * Extract the scheme name from the recipient URI and point to the
1457 * notifier program...
1458 */
1459
1460 strlcpy(scheme, sub->recipient, sizeof(scheme));
1461 if ((ptr = strchr(scheme, ':')) != NULL)
1462 *ptr = '\0';
1463
1464 snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1465
1466 /*
1467 * Base-64 encode the user data...
1468 */
1469
1470 httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
90d3999e 1471 sub->user_data_len);
ef416fc2 1472
1473 /*
1474 * Setup the argument array...
1475 */
1476
1477 argv[0] = command;
1478 argv[1] = sub->recipient;
1479 argv[2] = user_data;
1480 argv[3] = NULL;
1481
1482 /*
1483 * Setup the environment...
1484 */
1485
d09495fa 1486 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
ef416fc2 1487
1488 /*
1489 * Create pipes as needed...
1490 */
1491
1492 if (!NotifierStatusBuffer)
1493 {
1494 /*
1495 * Create the status pipe...
1496 */
1497
1498 if (cupsdOpenPipe(NotifierPipes))
1499 {
1500 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e 1501 "Unable to create pipes for notifier status - %s",
ef416fc2 1502 strerror(errno));
1503 return;
1504 }
1505
1506 NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1507
f899b121 1508 cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
90d3999e 1509 NULL, NULL);
ef416fc2 1510 }
1511
1512 if (cupsdOpenPipe(fds))
1513 {
1514 cupsdLogMessage(CUPSD_LOG_ERROR,
90d3999e 1515 "Unable to create pipes for notifier %s - %s",
ef416fc2 1516 scheme, strerror(errno));
1517 return;
1518 }
1519
1520 /*
1521 * Make sure the delivery pipe is non-blocking...
1522 */
1523
1524 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1525
1526 /*
1527 * Create the notifier process...
1528 */
1529
1530 if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
38e73f87 1531 -1, -1, 0, DefaultProfile, NULL, &pid) < 0)
ef416fc2 1532 {
1533 /*
1534 * Error - can't fork!
1535 */
1536
1537 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
90d3999e 1538 scheme, strerror(errno));
ef416fc2 1539
1540 cupsdClosePipe(fds);
1541 }
1542 else
1543 {
1544 /*
1545 * Fork successful - return the PID...
1546 */
1547
1548 cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
90d3999e 1549 scheme, pid);
ef416fc2 1550
90d3999e
MS
1551 sub->pid = pid;
1552 sub->pipe = fds[1];
ef416fc2 1553 sub->status = 0;
1554
1555 close(fds[0]);
1556 }
1557}
1558
1559
1560/*
f899b121 1561 * 'cupsd_update_notifier()' - Read messages from notifiers.
1562 */
1563
1564void
1565cupsd_update_notifier(void)
1566{
1567 char message[1024]; /* Pointer to message text */
1568 int loglevel; /* Log level for message */
1569
1570
1571 while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
90d3999e 1572 message, sizeof(message)))
f0ab5bff
MS
1573 {
1574 if (loglevel == CUPSD_LOG_INFO)
1575 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
1576
f899b121 1577 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1578 break;
f0ab5bff 1579 }
f899b121 1580}