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