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