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