]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/subscriptions.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / scheduler / subscriptions.c
1 /*
2 * Subscription routines for the CUPS scheduler.
3 *
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include "cupsd.h"
15 #ifdef HAVE_DBUS
16 # include <dbus/dbus.h>
17 # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
18 # define dbus_message_append_iter_init dbus_message_iter_init_append
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))
21 # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
22 #endif /* HAVE_DBUS */
23
24
25 /*
26 * Local functions...
27 */
28
29 static int cupsd_compare_subscriptions(cupsd_subscription_t *first,
30 cupsd_subscription_t *second,
31 void *unused);
32 static void cupsd_delete_event(cupsd_event_t *event);
33 #ifdef HAVE_DBUS
34 static void cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
35 cupsd_job_t *job);
36 #endif /* HAVE_DBUS */
37 static void cupsd_send_notification(cupsd_subscription_t *sub,
38 cupsd_event_t *event);
39 static void cupsd_start_notifier(cupsd_subscription_t *sub);
40 static void cupsd_update_notifier(void);
41
42
43 /*
44 * 'cupsdAddEvent()' - Add an event to the global event cache.
45 */
46
47 void
48 cupsdAddEvent(
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 */
57 ipp_attribute_t *attr; /* Printer/job attribute */
58 cupsd_event_t *temp; /* New event pointer */
59 cupsd_subscription_t *sub; /* Current subscription */
60
61
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
67 /*
68 * Keep track of events with any OS-supplied notification mechanisms...
69 */
70
71 LastEvent |= event;
72
73 #ifdef HAVE_DBUS
74 cupsd_send_dbus(event, dest, job);
75 #endif /* HAVE_DBUS */
76
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
89 /*
90 * Then loop through the subscriptions and add the event to the corresponding
91 * caches...
92 */
93
94 for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
95 sub;
96 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
97 {
98 /*
99 * Check if this subscription requires this event...
100 */
101
102 if ((sub->mask & event) != 0 && (sub->dest == dest || !sub->dest || sub->job == job))
103 {
104 /*
105 * Need this event, so create a new event record...
106 */
107
108 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
109 {
110 cupsdLogMessage(CUPSD_LOG_CRIT,
111 "Unable to allocate memory for event - %s",
112 strerror(errno));
113 return;
114 }
115
116 temp->event = event;
117 temp->time = time(NULL);
118 temp->attrs = ippNew();
119 temp->job = job;
120
121 if (dest)
122 temp->dest = dest;
123 else if (job)
124 temp->dest = dest = cupsdFindPrinter(job->dest);
125
126 /*
127 * Add common event notification attributes...
128 */
129
130 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
131 "notify-charset", NULL, "utf-8");
132
133 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
134 "notify-natural-language", NULL, "en-US");
135
136 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
137 "notify-subscription-id", sub->id);
138
139 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
140 "notify-sequence-number", sub->next_event_id);
141
142 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
143 "notify-subscribed-event", NULL, cupsdEventName(event));
144
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);
149
150 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
151 "printer-up-time", time(NULL));
152
153 va_start(ap, text);
154 vsnprintf(ftext, sizeof(ftext), text, ap);
155 va_end(ap);
156
157 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
158 "notify-text", NULL, ftext);
159
160 if (dest)
161 {
162 /*
163 * Add printer attributes...
164 */
165
166 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
167 "notify-printer-uri", NULL, dest->uri);
168
169 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
170 "printer-name", NULL, dest->name);
171
172 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
173 "printer-state", dest->state);
174
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);
184
185 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
186 "printer-is-accepting-jobs", (char)dest->accepting);
187 }
188
189 if (job)
190 {
191 /*
192 * Add job attributes...
193 */
194
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)
209 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
210 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
211 "printer-stopped");
212 else
213 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
214 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
215 "none");
216 break;
217
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)
221 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
222 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
223 "job-hold-until-specified");
224 else
225 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
226 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
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
242 case IPP_JOB_CANCELED :
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;
259 }
260
261 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
262 "job-impressions-completed",
263 job->sheets ? job->sheets->values[0].integer : 0);
264 }
265
266 /*
267 * Send the notification for this subscription...
268 */
269
270 cupsd_send_notification(sub, temp);
271 }
272 }
273
274 if (temp)
275 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
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
286 cupsd_subscription_t * /* O - New subscription object */
287 cupsdAddSubscription(
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,
298 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
299 "uri=\"%s\")",
300 mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
301 uri ? uri : "(null)");
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
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));
325 return (NULL);
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 }
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
415 /*
416 * For RSS subscriptions, run the notifier immediately...
417 */
418
419 if (uri && !strncmp(uri, "rss:", 4))
420 cupsd_start_notifier(temp);
421
422 return (temp);
423 }
424
425
426 /*
427 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
428 */
429
430 void
431 cupsdDeleteAllSubscriptions(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
453 void
454 cupsdDeleteSubscription(
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
478 cupsArrayDelete(sub->events);
479
480 free(sub);
481
482 /*
483 * Update the subscriptions as needed...
484 */
485
486 if (update)
487 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
488 }
489
490
491 /*
492 * 'cupsdEventName()' - Return a single event name.
493 */
494
495 const char * /* O - Event name */
496 cupsdEventName(
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
528 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
529 return ("printer-queue-order-changed");
530
531 case CUPSD_EVENT_PRINTER_STATE :
532 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
533 return ("printer-state-changed");
534
535 case CUPSD_EVENT_PRINTER_CONFIG :
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
557 case CUPSD_EVENT_JOB_STATE :
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
583 cupsd_eventmask_t /* O - Event mask value */
584 cupsdEventValue(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);
604 else if (!strcmp(name, "printer-queue-order-changed"))
605 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
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
641 void
642 cupsdExpireSubscriptions(
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
651 curtime = time(NULL);
652 update = 0;
653
654 cupsdLogMessage(CUPSD_LOG_INFO, "Expiring subscriptions...");
655
656 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
657 sub;
658 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
659 if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
660 (dest && sub->dest == dest) ||
661 (job && sub->job == job))
662 {
663 cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
664 sub->id);
665
666 cupsdDeleteSubscription(sub, 0);
667
668 update = 1;
669 }
670
671 if (update)
672 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
673 }
674
675
676 /*
677 * 'cupsdFindSubscription()' - Find a subscription by ID.
678 */
679
680 cupsd_subscription_t * /* O - Subscription object */
681 cupsdFindSubscription(int id) /* I - Subscription ID */
682 {
683 cupsd_subscription_t sub; /* Subscription template */
684
685
686 sub.id = id;
687
688 return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
689 }
690
691
692 /*
693 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
694 */
695
696 void
697 cupsdLoadAllSubscriptions(void)
698 {
699 int i; /* Looping var */
700 cups_file_t *fp; /* subscriptions.conf file */
701 int linenum; /* Current line number */
702 char line[1024], /* Line from file */
703 *value, /* Pointer to value */
704 *valueptr; /* Pointer into value */
705 cupsd_subscription_t *sub; /* Current subscription */
706 int hex; /* Non-zero if reading hex data */
707 int delete_sub; /* Delete subscription? */
708
709
710 /*
711 * Open the subscriptions.conf file...
712 */
713
714 snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
715 if ((fp = cupsdOpenConfFile(line)) == NULL)
716 return;
717
718 /*
719 * Read all of the lines from the file...
720 */
721
722 linenum = 0;
723 sub = NULL;
724 delete_sub = 0;
725
726 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
727 {
728 if (!_cups_strcasecmp(line, "NextSubscriptionId") && value)
729 {
730 /*
731 * NextSubscriptionId NNN
732 */
733
734 i = atoi(value);
735 if (i >= NextSubscriptionId && i > 0)
736 NextSubscriptionId = i;
737 }
738 else if (!_cups_strcasecmp(line, "<Subscription"))
739 {
740 /*
741 * <Subscription #>
742 */
743
744 if (!sub && value && isdigit(value[0] & 255))
745 {
746 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
747 atoi(value));
748 }
749 else
750 {
751 cupsdLogMessage(CUPSD_LOG_ERROR,
752 "Syntax error on line %d of subscriptions.conf.",
753 linenum);
754 break;
755 }
756 }
757 else if (!_cups_strcasecmp(line, "</Subscription>"))
758 {
759 if (!sub)
760 {
761 cupsdLogMessage(CUPSD_LOG_ERROR,
762 "Syntax error on line %d of subscriptions.conf.",
763 linenum);
764 break;
765 }
766
767 if (delete_sub)
768 cupsdDeleteSubscription(sub, 0);
769
770 sub = NULL;
771 delete_sub = 0;
772 }
773 else if (!sub)
774 {
775 cupsdLogMessage(CUPSD_LOG_ERROR,
776 "Syntax error on line %d of subscriptions.conf.",
777 linenum);
778 }
779 else if (!_cups_strcasecmp(line, "Events"))
780 {
781 /*
782 * Events name
783 * Events name name name ...
784 */
785
786 if (!value)
787 {
788 cupsdLogMessage(CUPSD_LOG_ERROR,
789 "Syntax error on line %d of subscriptions.conf.",
790 linenum);
791 break;
792 }
793
794 while (*value)
795 {
796 /*
797 * Separate event names...
798 */
799
800 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
801
802 while (isspace(*valueptr & 255))
803 *valueptr++ = '\0';
804
805 /*
806 * See if the name exists...
807 */
808
809 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
810 {
811 cupsdLogMessage(CUPSD_LOG_ERROR,
812 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
813 value, linenum);
814 break;
815 }
816
817 value = valueptr;
818 }
819 }
820 else if (!_cups_strcasecmp(line, "Owner"))
821 {
822 /*
823 * Owner
824 */
825
826 if (value)
827 cupsdSetString(&sub->owner, value);
828 else
829 {
830 cupsdLogMessage(CUPSD_LOG_ERROR,
831 "Syntax error on line %d of subscriptions.conf.",
832 linenum);
833 break;
834 }
835 }
836 else if (!_cups_strcasecmp(line, "Recipient"))
837 {
838 /*
839 * Recipient uri
840 */
841
842 if (value)
843 cupsdSetString(&sub->recipient, value);
844 else
845 {
846 cupsdLogMessage(CUPSD_LOG_ERROR,
847 "Syntax error on line %d of subscriptions.conf.",
848 linenum);
849 break;
850 }
851 }
852 else if (!_cups_strcasecmp(line, "JobId"))
853 {
854 /*
855 * JobId #
856 */
857
858 if (value && isdigit(*value & 255))
859 {
860 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
861 {
862 cupsdLogMessage(CUPSD_LOG_ERROR,
863 "Job %s not found on line %d of subscriptions.conf.",
864 value, linenum);
865 delete_sub = 1;
866 }
867 }
868 else
869 {
870 cupsdLogMessage(CUPSD_LOG_ERROR,
871 "Syntax error on line %d of subscriptions.conf.",
872 linenum);
873 break;
874 }
875 }
876 else if (!_cups_strcasecmp(line, "PrinterName"))
877 {
878 /*
879 * PrinterName name
880 */
881
882 if (value)
883 {
884 if ((sub->dest = cupsdFindDest(value)) == NULL)
885 {
886 cupsdLogMessage(CUPSD_LOG_ERROR,
887 "Printer \'%s\' not found on line %d of subscriptions.conf.",
888 value, linenum);
889 delete_sub = 1;
890 }
891 }
892 else
893 {
894 cupsdLogMessage(CUPSD_LOG_ERROR,
895 "Syntax error on line %d of subscriptions.conf.",
896 linenum);
897 break;
898 }
899 }
900 else if (!_cups_strcasecmp(line, "UserData"))
901 {
902 /*
903 * UserData encoded-string
904 */
905
906 if (value)
907 {
908 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
909 {
910 if (*valueptr == '<' && !hex)
911 {
912 hex = 1;
913 valueptr ++;
914 }
915
916 if (hex)
917 {
918 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
919 {
920 if (isdigit(valueptr[0]))
921 sub->user_data[i] = (unsigned char)((valueptr[0] - '0') << 4);
922 else
923 sub->user_data[i] = (unsigned char)((tolower(valueptr[0]) - 'a' + 10) << 4);
924
925 if (isdigit(valueptr[1]))
926 sub->user_data[i] |= valueptr[1] - '0';
927 else
928 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
929
930 valueptr += 2;
931
932 if (*valueptr == '>')
933 {
934 hex = 0;
935 valueptr ++;
936 }
937 }
938 else
939 break;
940 }
941 else
942 sub->user_data[i] = (unsigned char)*valueptr++;
943 }
944
945 if (*valueptr)
946 {
947 cupsdLogMessage(CUPSD_LOG_ERROR,
948 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
949 value, linenum);
950 }
951 else
952 sub->user_data_len = i;
953 }
954 else
955 {
956 cupsdLogMessage(CUPSD_LOG_ERROR,
957 "Syntax error on line %d of subscriptions.conf.",
958 linenum);
959 break;
960 }
961 }
962 else if (!_cups_strcasecmp(line, "LeaseDuration"))
963 {
964 /*
965 * LeaseDuration #
966 */
967
968 if (value && isdigit(*value & 255))
969 {
970 sub->lease = atoi(value);
971 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
972 }
973 else
974 {
975 cupsdLogMessage(CUPSD_LOG_ERROR,
976 "Syntax error on line %d of subscriptions.conf.",
977 linenum);
978 break;
979 }
980 }
981 else if (!_cups_strcasecmp(line, "Interval"))
982 {
983 /*
984 * Interval #
985 */
986
987 if (value && isdigit(*value & 255))
988 sub->interval = atoi(value);
989 else
990 {
991 cupsdLogMessage(CUPSD_LOG_ERROR,
992 "Syntax error on line %d of subscriptions.conf.",
993 linenum);
994 break;
995 }
996 }
997 else if (!_cups_strcasecmp(line, "ExpirationTime"))
998 {
999 /*
1000 * ExpirationTime #
1001 */
1002
1003 if (value && isdigit(*value & 255))
1004 sub->expire = atoi(value);
1005 else
1006 {
1007 cupsdLogMessage(CUPSD_LOG_ERROR,
1008 "Syntax error on line %d of subscriptions.conf.",
1009 linenum);
1010 break;
1011 }
1012 }
1013 else if (!_cups_strcasecmp(line, "NextEventId"))
1014 {
1015 /*
1016 * NextEventId #
1017 */
1018
1019 if (value && isdigit(*value & 255))
1020 sub->next_event_id = sub->first_event_id = atoi(value);
1021 else
1022 {
1023 cupsdLogMessage(CUPSD_LOG_ERROR,
1024 "Syntax error on line %d of subscriptions.conf.",
1025 linenum);
1026 break;
1027 }
1028 }
1029 else
1030 {
1031 /*
1032 * Something else we don't understand...
1033 */
1034
1035 cupsdLogMessage(CUPSD_LOG_ERROR,
1036 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1037 line, linenum);
1038 }
1039 }
1040
1041 cupsFileClose(fp);
1042 }
1043
1044
1045 /*
1046 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1047 */
1048
1049 void
1050 cupsdSaveAllSubscriptions(void)
1051 {
1052 int i; /* Looping var */
1053 cups_file_t *fp; /* subscriptions.conf file */
1054 char filename[1024], /* subscriptions.conf filename */
1055 temp[1024]; /* Temporary string */
1056 cupsd_subscription_t *sub; /* Current subscription */
1057 time_t curtime; /* Current time */
1058 struct tm *curdate; /* Current date */
1059 unsigned mask; /* Current event mask */
1060 const char *name; /* Current event name */
1061 int hex; /* Non-zero if we are writing hex data */
1062
1063
1064 /*
1065 * Create the subscriptions.conf file...
1066 */
1067
1068 snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot);
1069
1070 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
1071 return;
1072
1073 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
1074
1075 /*
1076 * Write a small header to the file...
1077 */
1078
1079 curtime = time(NULL);
1080 curdate = localtime(&curtime);
1081 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1082
1083 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1084 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1085
1086 cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
1087
1088 /*
1089 * Write every subscription known to the system...
1090 */
1091
1092 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1093 sub;
1094 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1095 {
1096 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1097
1098 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1099 {
1100 /*
1101 * Simple event list...
1102 */
1103
1104 cupsFilePrintf(fp, "Events %s\n", name);
1105 }
1106 else
1107 {
1108 /*
1109 * Complex event list...
1110 */
1111
1112 cupsFilePuts(fp, "Events");
1113
1114 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1115 if (sub->mask & mask)
1116 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1117
1118 cupsFilePuts(fp, "\n");
1119 }
1120
1121 if (sub->owner)
1122 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1123 if (sub->recipient)
1124 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1125 if (sub->job)
1126 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1127 if (sub->dest)
1128 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1129
1130 if (sub->user_data_len > 0)
1131 {
1132 cupsFilePuts(fp, "UserData ");
1133
1134 for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1135 {
1136 if (sub->user_data[i] < ' ' ||
1137 sub->user_data[i] > 0x7f ||
1138 sub->user_data[i] == '<')
1139 {
1140 if (!hex)
1141 {
1142 cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1143 hex = 1;
1144 }
1145 else
1146 cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1147 }
1148 else
1149 {
1150 if (hex)
1151 {
1152 cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1153 hex = 0;
1154 }
1155 else
1156 cupsFilePutChar(fp, sub->user_data[i]);
1157 }
1158 }
1159
1160 if (hex)
1161 cupsFilePuts(fp, ">\n");
1162 else
1163 cupsFilePutChar(fp, '\n');
1164 }
1165
1166 cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1167 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1168 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1169 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1170
1171 cupsFilePuts(fp, "</Subscription>\n");
1172 }
1173
1174 cupsdCloseCreatedConfFile(fp, filename);
1175 }
1176
1177
1178 /*
1179 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1180 */
1181
1182 void
1183 cupsdStopAllNotifiers(void)
1184 {
1185 cupsd_subscription_t *sub; /* Current subscription */
1186
1187
1188 /*
1189 * See if we have started any notifiers...
1190 */
1191
1192 if (!NotifierStatusBuffer)
1193 return;
1194
1195 /*
1196 * Yes, kill any processes that are left...
1197 */
1198
1199 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1200 sub;
1201 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1202 if (sub->pid)
1203 {
1204 cupsdEndProcess(sub->pid, 0);
1205
1206 close(sub->pipe);
1207 sub->pipe = -1;
1208 }
1209
1210 /*
1211 * Close the status pipes...
1212 */
1213
1214 if (NotifierPipes[0] >= 0)
1215 {
1216 cupsdRemoveSelect(NotifierPipes[0]);
1217
1218 cupsdStatBufDelete(NotifierStatusBuffer);
1219
1220 close(NotifierPipes[0]);
1221 close(NotifierPipes[1]);
1222
1223 NotifierPipes[0] = -1;
1224 NotifierPipes[1] = -1;
1225 NotifierStatusBuffer = NULL;
1226 }
1227 }
1228
1229
1230 /*
1231 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1232 */
1233
1234 static int /* O - Result of comparison */
1235 cupsd_compare_subscriptions(
1236 cupsd_subscription_t *first, /* I - First subscription object */
1237 cupsd_subscription_t *second, /* I - Second subscription object */
1238 void *unused) /* I - Unused user data pointer */
1239 {
1240 (void)unused;
1241
1242 return (first->id - second->id);
1243 }
1244
1245
1246 /*
1247 * 'cupsd_delete_event()' - Delete a single event...
1248 *
1249 * Oldest events must be deleted first, otherwise the subscription cache
1250 * flushing code will not work properly.
1251 */
1252
1253 static void
1254 cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1255 {
1256 /*
1257 * Free memory...
1258 */
1259
1260 ippDelete(event->attrs);
1261 free(event);
1262 }
1263
1264
1265 #ifdef HAVE_DBUS
1266 /*
1267 * 'cupsd_send_dbus()' - Send a DBUS notification...
1268 */
1269
1270 static void
1271 cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
1272 cupsd_printer_t *dest,/* I - Destination, if any */
1273 cupsd_job_t *job) /* I - Job, if any */
1274 {
1275 DBusError error; /* Error, if any */
1276 DBusMessage *message; /* Message to send */
1277 DBusMessageIter iter; /* Iterator for message data */
1278 const char *what; /* What to send */
1279 static DBusConnection *con = NULL; /* Connection to DBUS server */
1280
1281
1282 /*
1283 * Figure out what to send, if anything...
1284 */
1285
1286 if (event & CUPSD_EVENT_PRINTER_ADDED)
1287 what = "PrinterAdded";
1288 else if (event & CUPSD_EVENT_PRINTER_DELETED)
1289 what = "PrinterRemoved";
1290 else if (event & CUPSD_EVENT_PRINTER_CHANGED)
1291 what = "QueueChanged";
1292 else if (event & CUPSD_EVENT_JOB_CREATED)
1293 what = "JobQueuedLocal";
1294 else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
1295 job->state_value == IPP_JOB_PROCESSING)
1296 what = "JobStartedLocal";
1297 else
1298 return;
1299
1300 /*
1301 * Verify connection to DBUS server...
1302 */
1303
1304 if (con && !dbus_connection_get_is_connected(con))
1305 {
1306 dbus_connection_unref(con);
1307 con = NULL;
1308 }
1309
1310 if (!con)
1311 {
1312 dbus_error_init(&error);
1313
1314 con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
1315 if (!con)
1316 {
1317 dbus_error_free(&error);
1318 return;
1319 }
1320 }
1321
1322 /*
1323 * Create and send the new message...
1324 */
1325
1326 message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
1327 "com.redhat.PrinterSpooler", what);
1328
1329 dbus_message_append_iter_init(message, &iter);
1330 if (dest)
1331 dbus_message_iter_append_string(&iter, dest->name);
1332 if (job)
1333 {
1334 dbus_message_iter_append_uint32(&iter, job->id);
1335 dbus_message_iter_append_string(&iter, job->username);
1336 }
1337
1338 dbus_connection_send(con, message, NULL);
1339 dbus_connection_flush(con);
1340 dbus_message_unref(message);
1341 }
1342 #endif /* HAVE_DBUS */
1343
1344
1345 /*
1346 * 'cupsd_send_notification()' - Send a notification for the specified event.
1347 */
1348
1349 static void
1350 cupsd_send_notification(
1351 cupsd_subscription_t *sub, /* I - Subscription object */
1352 cupsd_event_t *event) /* I - Event to send */
1353 {
1354 ipp_state_t state; /* IPP event state */
1355
1356
1357 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1358 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1359 sub, sub->id, event, cupsdEventName(event->event));
1360
1361 /*
1362 * Allocate the events array as needed...
1363 */
1364
1365 if (!sub->events)
1366 {
1367 sub->events = cupsArrayNew3((cups_array_func_t)NULL, NULL,
1368 (cups_ahash_func_t)NULL, 0,
1369 (cups_acopy_func_t)NULL,
1370 (cups_afree_func_t)cupsd_delete_event);
1371
1372 if (!sub->events)
1373 {
1374 cupsdLogMessage(CUPSD_LOG_CRIT,
1375 "Unable to allocate memory for subscription #%d!",
1376 sub->id);
1377 return;
1378 }
1379 }
1380
1381 /*
1382 * Purge an old event as needed...
1383 */
1384
1385 if (cupsArrayCount(sub->events) >= MaxEvents)
1386 {
1387 /*
1388 * Purge the oldest event in the cache...
1389 */
1390
1391 cupsArrayRemove(sub->events, cupsArrayFirst(sub->events));
1392
1393 sub->first_event_id ++;
1394 }
1395
1396 /*
1397 * Add the event to the subscription. Since the events array is
1398 * always MaxEvents in length, and since we will have already
1399 * removed an event from the subscription cache if we hit the
1400 * event cache limit, we don't need to check for overflow here...
1401 */
1402
1403 cupsArrayAdd(sub->events, event);
1404
1405 /*
1406 * Deliver the event...
1407 */
1408
1409 if (sub->recipient)
1410 {
1411 for (;;)
1412 {
1413 if (sub->pipe < 0)
1414 cupsd_start_notifier(sub);
1415
1416 cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
1417
1418 if (sub->pipe < 0)
1419 break;
1420
1421 event->attrs->state = IPP_IDLE;
1422
1423 while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
1424 if (state == IPP_ERROR)
1425 break;
1426
1427 if (state == IPP_ERROR)
1428 {
1429 if (errno == EPIPE)
1430 {
1431 /*
1432 * Notifier died, try restarting it...
1433 */
1434
1435 cupsdLogMessage(CUPSD_LOG_WARN,
1436 "Notifier for subscription %d (%s) went away, "
1437 "retrying!",
1438 sub->id, sub->recipient);
1439 cupsdEndProcess(sub->pid, 0);
1440
1441 close(sub->pipe);
1442 sub->pipe = -1;
1443 continue;
1444 }
1445
1446 cupsdLogMessage(CUPSD_LOG_ERROR,
1447 "Unable to send event for subscription %d (%s)!",
1448 sub->id, sub->recipient);
1449 }
1450
1451 /*
1452 * If we get this far, break out of the loop...
1453 */
1454
1455 break;
1456 }
1457 }
1458
1459 /*
1460 * Bump the event sequence number...
1461 */
1462
1463 sub->next_event_id ++;
1464 }
1465
1466
1467 /*
1468 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1469 */
1470
1471 static void
1472 cupsd_start_notifier(
1473 cupsd_subscription_t *sub) /* I - Subscription object */
1474 {
1475 int pid; /* Notifier process ID */
1476 int fds[2]; /* Pipe file descriptors */
1477 char *argv[4], /* Command-line arguments */
1478 *envp[MAX_ENV], /* Environment variables */
1479 user_data[128], /* Base-64 encoded user data */
1480 scheme[256], /* notify-recipient-uri scheme */
1481 *ptr, /* Pointer into scheme */
1482 command[1024]; /* Notifier command */
1483
1484
1485 /*
1486 * Extract the scheme name from the recipient URI and point to the
1487 * notifier program...
1488 */
1489
1490 strlcpy(scheme, sub->recipient, sizeof(scheme));
1491 if ((ptr = strchr(scheme, ':')) != NULL)
1492 *ptr = '\0';
1493
1494 snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1495
1496 /*
1497 * Base-64 encode the user data...
1498 */
1499
1500 httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
1501 sub->user_data_len);
1502
1503 /*
1504 * Setup the argument array...
1505 */
1506
1507 argv[0] = command;
1508 argv[1] = sub->recipient;
1509 argv[2] = user_data;
1510 argv[3] = NULL;
1511
1512 /*
1513 * Setup the environment...
1514 */
1515
1516 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1517
1518 /*
1519 * Create pipes as needed...
1520 */
1521
1522 if (!NotifierStatusBuffer)
1523 {
1524 /*
1525 * Create the status pipe...
1526 */
1527
1528 if (cupsdOpenPipe(NotifierPipes))
1529 {
1530 cupsdLogMessage(CUPSD_LOG_ERROR,
1531 "Unable to create pipes for notifier status - %s",
1532 strerror(errno));
1533 return;
1534 }
1535
1536 NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1537
1538 cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
1539 NULL, NULL);
1540 }
1541
1542 if (cupsdOpenPipe(fds))
1543 {
1544 cupsdLogMessage(CUPSD_LOG_ERROR,
1545 "Unable to create pipes for notifier %s - %s",
1546 scheme, strerror(errno));
1547 return;
1548 }
1549
1550 /*
1551 * Make sure the delivery pipe is non-blocking...
1552 */
1553
1554 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1555
1556 /*
1557 * Create the notifier process...
1558 */
1559
1560 if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
1561 -1, -1, 0, DefaultProfile, NULL, &pid) < 0)
1562 {
1563 /*
1564 * Error - can't fork!
1565 */
1566
1567 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
1568 scheme, strerror(errno));
1569
1570 cupsdClosePipe(fds);
1571 }
1572 else
1573 {
1574 /*
1575 * Fork successful - return the PID...
1576 */
1577
1578 cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
1579 scheme, pid);
1580
1581 sub->pid = pid;
1582 sub->pipe = fds[1];
1583 sub->status = 0;
1584
1585 close(fds[0]);
1586 }
1587 }
1588
1589
1590 /*
1591 * 'cupsd_update_notifier()' - Read messages from notifiers.
1592 */
1593
1594 void
1595 cupsd_update_notifier(void)
1596 {
1597 char message[1024]; /* Pointer to message text */
1598 int loglevel; /* Log level for message */
1599
1600
1601 while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1602 message, sizeof(message)))
1603 {
1604 if (loglevel == CUPSD_LOG_INFO)
1605 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
1606
1607 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1608 break;
1609 }
1610 }