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