]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/subscriptions.c
Merge branch 'master' of https://github.com/apple/cups
[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
9924fab8 108 if ((sub->mask & event) != 0 && (sub->dest == dest || !sub->dest || sub->job == job))
ef416fc2 109 {
110 /*
ed486911 111 * Need this event, so create a new event record...
ef416fc2 112 */
113
ed486911 114 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
ef416fc2 115 {
ed486911 116 cupsdLogMessage(CUPSD_LOG_CRIT,
117 "Unable to allocate memory for event - %s",
118 strerror(errno));
119 return;
120 }
ef416fc2 121
ed486911 122 temp->event = event;
123 temp->time = time(NULL);
124 temp->attrs = ippNew();
125 temp->job = job;
12f89d24
MS
126
127 if (dest)
128 temp->dest = dest;
129 else if (job)
130 temp->dest = dest = cupsdFindPrinter(job->dest);
ef416fc2 131
ed486911 132 /*
133 * Add common event notification attributes...
134 */
ef416fc2 135
ed486911 136 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
137 "notify-charset", NULL, "utf-8");
ef416fc2 138
ed486911 139 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
0268488e 140 "notify-natural-language", NULL, "en-US");
ef416fc2 141
ed486911 142 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
143 "notify-subscription-id", sub->id);
ef416fc2 144
ed486911 145 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
146 "notify-sequence-number", sub->next_event_id);
ef416fc2 147
ed486911 148 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
149 "notify-subscribed-event", NULL, cupsdEventName(event));
ef416fc2 150
ed486911 151 if (sub->user_data_len > 0)
152 ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
153 "notify-user-data", sub->user_data,
154 sub->user_data_len);
ef416fc2 155
ed486911 156 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
157 "printer-up-time", time(NULL));
ef416fc2 158
ed486911 159 va_start(ap, text);
160 vsnprintf(ftext, sizeof(ftext), text, ap);
161 va_end(ap);
ef416fc2 162
ed486911 163 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
164 "notify-text", NULL, ftext);
ef416fc2 165
ed486911 166 if (dest)
167 {
168 /*
169 * Add printer attributes...
170 */
ef416fc2 171
ed486911 172 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
173 "notify-printer-uri", NULL, dest->uri);
ef416fc2 174
ed486911 175 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
176 "printer-name", NULL, dest->name);
ef416fc2 177
ed486911 178 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
179 "printer-state", dest->state);
ef416fc2 180
ed486911 181 if (dest->num_reasons == 0)
182 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
183 IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
184 dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
185 else
186 ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
187 IPP_TAG_KEYWORD, "printer-state-reasons",
188 dest->num_reasons, NULL,
189 (const char * const *)dest->reasons);
ef416fc2 190
ed486911 191 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
7e86f2f6 192 "printer-is-accepting-jobs", (char)dest->accepting);
ed486911 193 }
fa73b229 194
ed486911 195 if (job)
196 {
197 /*
198 * Add job attributes...
199 */
ef416fc2 200
ed486911 201 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
202 "notify-job-id", job->id);
203 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
204 "job-state", job->state_value);
205
206 if ((attr = ippFindAttribute(job->attrs, "job-name",
207 IPP_TAG_NAME)) != NULL)
208 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
209 "job-name", NULL, attr->values[0].string.text);
210
211 switch (job->state_value)
212 {
213 case IPP_JOB_PENDING :
214 if (dest && dest->state == IPP_PRINTER_STOPPED)
ef416fc2 215 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
216 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
ed486911 217 "printer-stopped");
218 else
ef416fc2 219 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
220 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
ed486911 221 "none");
222 break;
ef416fc2 223
ed486911 224 case IPP_JOB_HELD :
225 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
226 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
ef416fc2 227 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
228 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
ed486911 229 "job-hold-until-specified");
230 else
ef416fc2 231 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
232 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
ed486911 233 "job-incoming");
234 break;
235
236 case IPP_JOB_PROCESSING :
237 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
238 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
239 "job-printing");
240 break;
241
242 case IPP_JOB_STOPPED :
243 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
244 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
245 "job-stopped");
246 break;
247
d09495fa 248 case IPP_JOB_CANCELED :
ed486911 249 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
250 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
251 "job-canceled-by-user");
252 break;
253
254 case IPP_JOB_ABORTED :
255 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
256 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
257 "aborted-by-system");
258 break;
259
260 case IPP_JOB_COMPLETED :
261 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
262 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
263 "job-completed-successfully");
264 break;
ef416fc2 265 }
266
ed486911 267 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
268 "job-impressions-completed",
269 job->sheets ? job->sheets->values[0].integer : 0);
8ca02f3c 270 }
ef416fc2 271
8ca02f3c 272 /*
273 * Send the notification for this subscription...
274 */
ef416fc2 275
8ca02f3c 276 cupsd_send_notification(sub, temp);
ef416fc2 277 }
278 }
279
280 if (temp)
3dfe78b3 281 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 282 else
283 cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...",
284 cupsdEventName(event));
285}
286
287
288/*
289 * 'cupsdAddSubscription()' - Add a new subscription object.
290 */
291
292cupsd_subscription_t * /* O - New subscription object */
293cupsdAddSubscription(
294 unsigned mask, /* I - Event mask */
295 cupsd_printer_t *dest, /* I - Printer, if any */
296 cupsd_job_t *job, /* I - Job, if any */
297 const char *uri, /* I - notify-recipient-uri, if any */
298 int sub_id) /* I - notify-subscription-id or 0 */
299{
300 cupsd_subscription_t *temp; /* New subscription object */
301
302
303 cupsdLogMessage(CUPSD_LOG_DEBUG,
e1d6a774 304 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
305 "uri=\"%s\")",
306 mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
d9bca400 307 uri ? uri : "(null)");
ef416fc2 308
309 if (!Subscriptions)
310 Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
311 NULL);
312
313 if (!Subscriptions)
314 {
315 cupsdLogMessage(CUPSD_LOG_CRIT,
316 "Unable to allocate memory for subscriptions - %s",
317 strerror(errno));
318 return (NULL);
319 }
320
321 /*
322 * Limit the number of subscriptions...
323 */
324
52f6f666
MS
325 if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
326 {
327 cupsdLogMessage(CUPSD_LOG_DEBUG,
328 "cupsdAddSubscription: Reached MaxSubscriptions %d "
329 "(count=%d)", MaxSubscriptions,
330 cupsArrayCount(Subscriptions));
ef416fc2 331 return (NULL);
52f6f666
MS
332 }
333
334 if (MaxSubscriptionsPerJob > 0 && job)
335 {
336 int count; /* Number of job subscriptions */
337
338 for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
339 count = 0;
340 temp;
341 temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
342 if (temp->job == job)
343 count ++;
344
345 if (count >= MaxSubscriptionsPerJob)
346 {
347 cupsdLogMessage(CUPSD_LOG_DEBUG,
348 "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
349 "for job #%d (count=%d)", MaxSubscriptionsPerJob,
350 job->id, count);
351 return (NULL);
352 }
353 }
354
355 if (MaxSubscriptionsPerPrinter > 0 && dest)
356 {
357 int count; /* Number of printer subscriptions */
358
359 for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
360 count = 0;
361 temp;
362 temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
363 if (temp->dest == dest)
364 count ++;
365
366 if (count >= MaxSubscriptionsPerPrinter)
367 {
368 cupsdLogMessage(CUPSD_LOG_DEBUG,
369 "cupsdAddSubscription: Reached "
370 "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
371 MaxSubscriptionsPerPrinter, dest->name, count);
372 return (NULL);
373 }
374 }
ef416fc2 375
376 /*
377 * Allocate memory for this subscription...
378 */
379
380 if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
381 {
382 cupsdLogMessage(CUPSD_LOG_CRIT,
383 "Unable to allocate memory for subscription object - %s",
384 strerror(errno));
385 return (NULL);
386 }
387
388 /*
389 * Fill in common data...
390 */
391
392 if (sub_id)
393 {
394 temp->id = sub_id;
395
396 if (sub_id >= NextSubscriptionId)
397 NextSubscriptionId = sub_id + 1;
398 }
399 else
400 {
401 temp->id = NextSubscriptionId;
402
403 NextSubscriptionId ++;
404 }
405
406 temp->mask = mask;
407 temp->dest = dest;
408 temp->job = job;
409 temp->pipe = -1;
410 temp->first_event_id = 1;
411 temp->next_event_id = 1;
412
413 cupsdSetString(&(temp->recipient), uri);
414
415 /*
416 * Add the subscription to the array...
417 */
418
419 cupsArrayAdd(Subscriptions, temp);
420
ee571f26
MS
421 /*
422 * For RSS subscriptions, run the notifier immediately...
423 */
424
425 if (uri && !strncmp(uri, "rss:", 4))
426 cupsd_start_notifier(temp);
427
ef416fc2 428 return (temp);
429}
430
431
ef416fc2 432/*
433 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
434 */
435
436void
437cupsdDeleteAllSubscriptions(void)
438{
439 cupsd_subscription_t *sub; /* Subscription */
440
441
442 if (!Subscriptions)
443 return;
444
445 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
446 sub;
447 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
448 cupsdDeleteSubscription(sub, 0);
449
450 cupsArrayDelete(Subscriptions);
451 Subscriptions = NULL;
452}
453
454
455/*
456 * 'cupsdDeleteSubscription()' - Delete a subscription object.
457 */
458
459void
460cupsdDeleteSubscription(
461 cupsd_subscription_t *sub, /* I - Subscription object */
462 int update) /* I - 1 = update subscriptions.conf */
463{
464 /*
465 * Close the pipe to the notifier as needed...
466 */
467
468 if (sub->pipe >= 0)
469 close(sub->pipe);
470
471 /*
472 * Remove subscription from array...
473 */
474
475 cupsArrayRemove(Subscriptions, sub);
476
477 /*
478 * Free memory...
479 */
480
481 cupsdClearString(&(sub->owner));
482 cupsdClearString(&(sub->recipient));
483
10d09e33 484 cupsArrayDelete(sub->events);
ef416fc2 485
486 free(sub);
487
488 /*
489 * Update the subscriptions as needed...
490 */
491
492 if (update)
3dfe78b3 493 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
ef416fc2 494}
495
496
497/*
498 * 'cupsdEventName()' - Return a single event name.
499 */
500
501const char * /* O - Event name */
502cupsdEventName(
503 cupsd_eventmask_t event) /* I - Event value */
504{
505 switch (event)
506 {
507 default :
508 return (NULL);
509
510 case CUPSD_EVENT_PRINTER_RESTARTED :
511 return ("printer-restarted");
512
513 case CUPSD_EVENT_PRINTER_SHUTDOWN :
514 return ("printer-shutdown");
515
516 case CUPSD_EVENT_PRINTER_STOPPED :
517 return ("printer-stopped");
518
519 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
520 return ("printer-finishings-changed");
521
522 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
523 return ("printer-media-changed");
524
525 case CUPSD_EVENT_PRINTER_ADDED :
526 return ("printer-added");
527
528 case CUPSD_EVENT_PRINTER_DELETED :
529 return ("printer-deleted");
530
531 case CUPSD_EVENT_PRINTER_MODIFIED :
532 return ("printer-modified");
533
d9bca400
MS
534 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
535 return ("printer-queue-order-changed");
536
537 case CUPSD_EVENT_PRINTER_STATE :
ef416fc2 538 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
539 return ("printer-state-changed");
540
d9bca400 541 case CUPSD_EVENT_PRINTER_CONFIG :
ef416fc2 542 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
543 return ("printer-config-changed");
544
545 case CUPSD_EVENT_PRINTER_CHANGED :
546 return ("printer-changed");
547
548 case CUPSD_EVENT_JOB_CREATED :
549 return ("job-created");
550
551 case CUPSD_EVENT_JOB_COMPLETED :
552 return ("job-completed");
553
554 case CUPSD_EVENT_JOB_STOPPED :
555 return ("job-stopped");
556
557 case CUPSD_EVENT_JOB_CONFIG_CHANGED :
558 return ("job-config-changed");
559
560 case CUPSD_EVENT_JOB_PROGRESS :
561 return ("job-progress");
562
e1d6a774 563 case CUPSD_EVENT_JOB_STATE :
ef416fc2 564 case CUPSD_EVENT_JOB_STATE_CHANGED :
565 return ("job-state-changed");
566
567 case CUPSD_EVENT_SERVER_RESTARTED :
568 return ("server-restarted");
569
570 case CUPSD_EVENT_SERVER_STARTED :
571 return ("server-started");
572
573 case CUPSD_EVENT_SERVER_STOPPED :
574 return ("server-stopped");
575
576 case CUPSD_EVENT_SERVER_AUDIT :
577 return ("server-audit");
578
579 case CUPSD_EVENT_ALL :
580 return ("all");
581 }
582}
583
584
585/*
586 * 'cupsdEventValue()' - Return the event mask value for a name.
587 */
588
589cupsd_eventmask_t /* O - Event mask value */
590cupsdEventValue(const char *name) /* I - Name of event */
591{
592 if (!strcmp(name, "all"))
593 return (CUPSD_EVENT_ALL);
594 else if (!strcmp(name, "printer-restarted"))
595 return (CUPSD_EVENT_PRINTER_RESTARTED);
596 else if (!strcmp(name, "printer-shutdown"))
597 return (CUPSD_EVENT_PRINTER_SHUTDOWN);
598 else if (!strcmp(name, "printer-stopped"))
599 return (CUPSD_EVENT_PRINTER_STOPPED);
600 else if (!strcmp(name, "printer-finishings-changed"))
601 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
602 else if (!strcmp(name, "printer-media-changed"))
603 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
604 else if (!strcmp(name, "printer-added"))
605 return (CUPSD_EVENT_PRINTER_ADDED);
606 else if (!strcmp(name, "printer-deleted"))
607 return (CUPSD_EVENT_PRINTER_DELETED);
608 else if (!strcmp(name, "printer-modified"))
609 return (CUPSD_EVENT_PRINTER_MODIFIED);
d9bca400
MS
610 else if (!strcmp(name, "printer-queue-order-changed"))
611 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
ef416fc2 612 else if (!strcmp(name, "printer-state-changed"))
613 return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
614 else if (!strcmp(name, "printer-config-changed"))
615 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
616 else if (!strcmp(name, "printer-changed"))
617 return (CUPSD_EVENT_PRINTER_CHANGED);
618 else if (!strcmp(name, "job-created"))
619 return (CUPSD_EVENT_JOB_CREATED);
620 else if (!strcmp(name, "job-completed"))
621 return (CUPSD_EVENT_JOB_COMPLETED);
622 else if (!strcmp(name, "job-stopped"))
623 return (CUPSD_EVENT_JOB_STOPPED);
624 else if (!strcmp(name, "job-config-changed"))
625 return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
626 else if (!strcmp(name, "job-progress"))
627 return (CUPSD_EVENT_JOB_PROGRESS);
628 else if (!strcmp(name, "job-state-changed"))
629 return (CUPSD_EVENT_JOB_STATE_CHANGED);
630 else if (!strcmp(name, "server-restarted"))
631 return (CUPSD_EVENT_SERVER_RESTARTED);
632 else if (!strcmp(name, "server-started"))
633 return (CUPSD_EVENT_SERVER_STARTED);
634 else if (!strcmp(name, "server-stopped"))
635 return (CUPSD_EVENT_SERVER_STOPPED);
636 else if (!strcmp(name, "server-audit"))
637 return (CUPSD_EVENT_SERVER_AUDIT);
638 else
639 return (CUPSD_EVENT_NONE);
640}
641
642
643/*
644 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
645 */
646
647void
648cupsdExpireSubscriptions(
649 cupsd_printer_t *dest, /* I - Printer, if any */
650 cupsd_job_t *job) /* I - Job, if any */
651{
652 cupsd_subscription_t *sub; /* Current subscription */
653 int update; /* Update subscriptions.conf? */
654 time_t curtime; /* Current time */
655
656
657 curtime = time(NULL);
658 update = 0;
659
79d3cd17
MS
660 cupsdLogMessage(CUPSD_LOG_INFO, "Expiring subscriptions...");
661
ef416fc2 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 */