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