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