]> 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 5249 2006-03-08 16:45:28Z 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, "NextSubscriptionId") && value)
747 {
748 /*
749 * NextSubscriptionId NNN
750 */
751
752 i = atoi(value);
753 if (i >= NextSubscriptionId && i > 0)
754 NextSubscriptionId = i;
755 }
756 else if (!strcasecmp(line, "<Subscription"))
757 {
758 /*
759 * <Subscription #>
760 */
761
762 if (!sub && value && isdigit(value[0] & 255))
763 {
764 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
765 atoi(value));
766 }
767 else
768 {
769 cupsdLogMessage(CUPSD_LOG_ERROR,
770 "Syntax error on line %d of subscriptions.conf.",
771 linenum);
772 return;
773 }
774 }
775 else if (!strcasecmp(line, "</Subscription>"))
776 {
777 if (!sub)
778 {
779 cupsdLogMessage(CUPSD_LOG_ERROR,
780 "Syntax error on line %d of subscriptions.conf.",
781 linenum);
782 return;
783 }
784
785 if (delete_sub)
786 cupsdDeleteSubscription(sub, 0);
787
788 sub = NULL;
789 delete_sub = 0;
790 }
791 else if (!sub)
792 {
793 cupsdLogMessage(CUPSD_LOG_ERROR,
794 "Syntax error on line %d of subscriptions.conf.",
795 linenum);
796 return;
797 }
798 else if (!strcasecmp(line, "Events"))
799 {
800 /*
801 * Events name
802 * Events name name name ...
803 */
804
805 if (!value)
806 {
807 cupsdLogMessage(CUPSD_LOG_ERROR,
808 "Syntax error on line %d of subscriptions.conf.",
809 linenum);
810 return;
811 }
812
813 while (*value)
814 {
815 /*
816 * Separate event names...
817 */
818
819 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
820
821 while (isspace(*valueptr & 255))
822 *valueptr++ = '\0';
823
824 /*
825 * See if the name exists...
826 */
827
828 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
829 {
830 cupsdLogMessage(CUPSD_LOG_ERROR,
831 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
832 value, linenum);
833 return;
834 }
835
836 value = valueptr;
837 }
838 }
839 else if (!strcasecmp(line, "Owner"))
840 {
841 /*
842 * Owner
843 */
844
845 if (value)
846 cupsdSetString(&sub->owner, value);
847 else
848 {
849 cupsdLogMessage(CUPSD_LOG_ERROR,
850 "Syntax error on line %d of subscriptions.conf.",
851 linenum);
852 return;
853 }
854 }
855 else if (!strcasecmp(line, "Recipient"))
856 {
857 /*
858 * Recipient uri
859 */
860
861 if (value)
862 cupsdSetString(&sub->recipient, value);
863 else
864 {
865 cupsdLogMessage(CUPSD_LOG_ERROR,
866 "Syntax error on line %d of subscriptions.conf.",
867 linenum);
868 return;
869 }
870 }
871 else if (!strcasecmp(line, "JobId"))
872 {
873 /*
874 * JobId #
875 */
876
877 if (value && isdigit(*value & 255))
878 {
879 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
880 {
881 cupsdLogMessage(CUPSD_LOG_ERROR,
882 "Job %s not found on line %d of subscriptions.conf.",
883 value, linenum);
884 delete_sub = 1;
885 }
886 }
887 else
888 {
889 cupsdLogMessage(CUPSD_LOG_ERROR,
890 "Syntax error on line %d of subscriptions.conf.",
891 linenum);
892 return;
893 }
894 }
895 else if (!strcasecmp(line, "PrinterName"))
896 {
897 /*
898 * PrinterName name
899 */
900
901 if (value)
902 {
903 if ((sub->dest = cupsdFindDest(value)) == NULL)
904 {
905 cupsdLogMessage(CUPSD_LOG_ERROR,
906 "Printer \'%s\' not found on line %d of subscriptions.conf.",
907 value, linenum);
908 delete_sub = 1;
909 }
910 }
911 else
912 {
913 cupsdLogMessage(CUPSD_LOG_ERROR,
914 "Syntax error on line %d of subscriptions.conf.",
915 linenum);
916 return;
917 }
918 }
919 else if (!strcasecmp(line, "UserData"))
920 {
921 /*
922 * UserData encoded-string
923 */
924
925 if (value)
926 {
927 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
928 {
929 if (*valueptr == '<' && !hex)
930 {
931 hex = 1;
932 valueptr ++;
933 }
934
935 if (hex)
936 {
937 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
938 {
939 if (isdigit(valueptr[0]))
940 sub->user_data[i] = (valueptr[0] - '0') << 4;
941 else
942 sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
943
944 if (isdigit(valueptr[1]))
945 sub->user_data[i] |= valueptr[1] - '0';
946 else
947 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
948
949 valueptr += 2;
950
951 if (*valueptr == '>')
952 {
953 hex = 0;
954 valueptr ++;
955 }
956 }
957 else
958 break;
959 }
960 else
961 sub->user_data[i] = *valueptr++;
962 }
963
964 if (*valueptr)
965 {
966 cupsdLogMessage(CUPSD_LOG_ERROR,
967 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
968 value, linenum);
969 }
970 else
971 sub->user_data_len = i;
972 }
973 else
974 {
975 cupsdLogMessage(CUPSD_LOG_ERROR,
976 "Syntax error on line %d of subscriptions.conf.",
977 linenum);
978 return;
979 }
980 }
981 else if (!strcasecmp(line, "LeaseDuration"))
982 {
983 /*
984 * LeaseDuration #
985 */
986
987 if (value && isdigit(*value & 255))
988 {
989 sub->lease = atoi(value);
990 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
991 }
992 else
993 {
994 cupsdLogMessage(CUPSD_LOG_ERROR,
995 "Syntax error on line %d of subscriptions.conf.",
996 linenum);
997 return;
998 }
999 }
1000 else if (!strcasecmp(line, "Interval"))
1001 {
1002 /*
1003 * Interval #
1004 */
1005
1006 if (value && isdigit(*value & 255))
1007 sub->interval = atoi(value);
1008 else
1009 {
1010 cupsdLogMessage(CUPSD_LOG_ERROR,
1011 "Syntax error on line %d of subscriptions.conf.",
1012 linenum);
1013 return;
1014 }
1015 }
1016 else if (!strcasecmp(line, "ExpirationTime"))
1017 {
1018 /*
1019 * ExpirationTime #
1020 */
1021
1022 if (value && isdigit(*value & 255))
1023 sub->expire = atoi(value);
1024 else
1025 {
1026 cupsdLogMessage(CUPSD_LOG_ERROR,
1027 "Syntax error on line %d of subscriptions.conf.",
1028 linenum);
1029 return;
1030 }
1031 }
1032 else if (!strcasecmp(line, "NextEventId"))
1033 {
1034 /*
1035 * NextEventId #
1036 */
1037
1038 if (value && isdigit(*value & 255))
1039 sub->next_event_id = sub->first_event_id = atoi(value);
1040 else
1041 {
1042 cupsdLogMessage(CUPSD_LOG_ERROR,
1043 "Syntax error on line %d of subscriptions.conf.",
1044 linenum);
1045 return;
1046 }
1047 }
1048 else
1049 {
1050 /*
1051 * Something else we don't understand...
1052 */
1053
1054 cupsdLogMessage(CUPSD_LOG_ERROR,
1055 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1056 line, linenum);
1057 }
1058 }
1059
1060 cupsFileClose(fp);
1061 }
1062
1063
1064 /*
1065 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1066 */
1067
1068 void
1069 cupsdSaveAllSubscriptions(void)
1070 {
1071 int i; /* Looping var */
1072 cups_file_t *fp; /* subscriptions.conf file */
1073 char temp[1024]; /* Temporary string */
1074 char backup[1024]; /* subscriptions.conf.O file */
1075 cupsd_subscription_t *sub; /* Current subscription */
1076 time_t curtime; /* Current time */
1077 struct tm *curdate; /* Current date */
1078 unsigned mask; /* Current event mask */
1079 const char *name; /* Current event name */
1080 int hex; /* Non-zero if we are writing hex data */
1081
1082
1083 /*
1084 * Create the subscriptions.conf file...
1085 */
1086
1087 snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
1088 snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
1089
1090 if (rename(temp, backup))
1091 {
1092 if (errno != ENOENT)
1093 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
1094 strerror(errno));
1095 }
1096
1097 if ((fp = cupsFileOpen(temp, "w")) == NULL)
1098 {
1099 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
1100 strerror(errno));
1101
1102 if (rename(backup, temp))
1103 cupsdLogMessage(CUPSD_LOG_ERROR,
1104 "Unable to restore subscriptions.conf - %s",
1105 strerror(errno));
1106 return;
1107 }
1108 else
1109 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
1110
1111 /*
1112 * Restrict access to the file...
1113 */
1114
1115 fchown(cupsFileNumber(fp), getuid(), Group);
1116 fchmod(cupsFileNumber(fp), ConfigFilePerm);
1117
1118 /*
1119 * Write a small header to the file...
1120 */
1121
1122 curtime = time(NULL);
1123 curdate = localtime(&curtime);
1124 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1125
1126 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1127 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1128
1129 cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
1130
1131 /*
1132 * Write every subscription known to the system...
1133 */
1134
1135 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1136 sub;
1137 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1138 {
1139 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1140
1141 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1142 {
1143 /*
1144 * Simple event list...
1145 */
1146
1147 cupsFilePrintf(fp, "Events %s\n", name);
1148 }
1149 else
1150 {
1151 /*
1152 * Complex event list...
1153 */
1154
1155 cupsFilePuts(fp, "Events");
1156
1157 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1158 if (sub->mask & mask)
1159 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1160
1161 cupsFilePuts(fp, "\n");
1162 }
1163
1164 if (sub->owner)
1165 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1166 if (sub->recipient)
1167 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1168 if (sub->job)
1169 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1170 if (sub->dest)
1171 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1172
1173 if (sub->user_data_len > 0)
1174 {
1175 cupsFilePuts(fp, "UserData ");
1176
1177 for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1178 {
1179 if (sub->user_data[i] < ' ' ||
1180 sub->user_data[i] > 0x7f ||
1181 sub->user_data[i] == '<')
1182 {
1183 if (!hex)
1184 {
1185 cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1186 hex = 1;
1187 }
1188 else
1189 cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1190 }
1191 else
1192 {
1193 if (hex)
1194 {
1195 cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1196 hex = 0;
1197 }
1198 else
1199 cupsFilePutChar(fp, sub->user_data[i]);
1200 }
1201 }
1202
1203 if (hex)
1204 cupsFilePuts(fp, ">\n");
1205 else
1206 cupsFilePutChar(fp, '\n');
1207 }
1208
1209 cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1210 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1211 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1212 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1213
1214 cupsFilePuts(fp, "</Subscription>\n");
1215 }
1216
1217 cupsFileClose(fp);
1218 }
1219
1220
1221 /*
1222 * 'cupsdSendNotification()' - Send a notification for the specified event.
1223 */
1224
1225 void
1226 cupsdSendNotification(
1227 cupsd_subscription_t *sub, /* I - Subscription object */
1228 cupsd_event_t *event) /* I - Event to send */
1229 {
1230 ipp_state_t state; /* IPP event state */
1231
1232
1233 cupsdLogMessage(CUPSD_LOG_DEBUG,
1234 "cupsdSendNotification(sub=%p(%d), event=%p(%s))\n",
1235 sub, sub->id, event, cupsdEventName(event->event));
1236
1237 /*
1238 * Allocate the events array as needed...
1239 */
1240
1241 if (!sub->events)
1242 {
1243 sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
1244
1245 if (!sub->events)
1246 {
1247 cupsdLogMessage(CUPSD_LOG_CRIT,
1248 "Unable to allocate memory for subscription #%d!",
1249 sub->id);
1250 return;
1251 }
1252 }
1253
1254 /*
1255 * Add the event to the subscription. Since the events array is
1256 * always MaxEvents in length, and since we will have already
1257 * removed an event from the subscription cache if we hit the
1258 * event cache limit, we don't need to check for overflow here...
1259 */
1260
1261 sub->events[sub->num_events] = event;
1262 sub->num_events ++;
1263
1264 /*
1265 * Deliver the event...
1266 */
1267
1268 if (sub->recipient)
1269 {
1270 if (sub->pipe < 0)
1271 cupsd_start_notifier(sub);
1272
1273 cupsdLogMessage(CUPSD_LOG_DEBUG, "sub->pipe=%d", sub->pipe);
1274
1275 if (sub->pipe >= 0)
1276 {
1277 event->attrs->state = IPP_IDLE;
1278
1279 while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
1280 if (state == IPP_ERROR)
1281 break;
1282
1283 if (state == IPP_ERROR)
1284 cupsdLogMessage(CUPSD_LOG_ERROR,
1285 "Unable to send event for subscription %d (%s)!",
1286 sub->id, sub->recipient);
1287 }
1288 }
1289
1290 /*
1291 * Bump the event sequence number...
1292 */
1293
1294 sub->next_event_id ++;
1295 }
1296
1297
1298 /*
1299 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1300 */
1301
1302 void
1303 cupsdStopAllNotifiers(void)
1304 {
1305 cupsd_subscription_t *sub; /* Current subscription */
1306
1307
1308 /*
1309 * See if we have started any notifiers...
1310 */
1311
1312 if (!NotifierStatusBuffer)
1313 return;
1314
1315 /*
1316 * Yes, kill any processes that are left...
1317 */
1318
1319 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1320 sub;
1321 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1322 if (sub->pid)
1323 {
1324 cupsdEndProcess(sub->pid, 0);
1325
1326 close(sub->pipe);
1327 sub->pipe = -1;
1328 }
1329
1330 /*
1331 * Close the status pipes...
1332 */
1333
1334 if (NotifierPipes[0] >= 0)
1335 {
1336 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1337 "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
1338 NotifierPipes[0]);
1339 FD_CLR(NotifierPipes[0], InputSet);
1340
1341 cupsdStatBufDelete(NotifierStatusBuffer);
1342
1343 close(NotifierPipes[0]);
1344 close(NotifierPipes[1]);
1345
1346 NotifierPipes[0] = -1;
1347 NotifierPipes[1] = -1;
1348 NotifierStatusBuffer = NULL;
1349 }
1350 }
1351
1352
1353 /*
1354 * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
1355 */
1356
1357 void
1358 cupsdUpdateNotifierStatus(void)
1359 {
1360 char *ptr, /* Pointer to end of line in buffer */
1361 message[1024]; /* Pointer to message text */
1362 int loglevel; /* Log level for message */
1363
1364
1365 while ((ptr = cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1366 message, sizeof(message))) != NULL)
1367 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1368 break;
1369 }
1370
1371
1372 /*
1373 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1374 */
1375
1376 static int /* O - Result of comparison */
1377 cupsd_compare_subscriptions(
1378 cupsd_subscription_t *first, /* I - First subscription object */
1379 cupsd_subscription_t *second, /* I - Second subscription object */
1380 void *unused) /* I - Unused user data pointer */
1381 {
1382 (void)unused;
1383
1384 return (first->id - second->id);
1385 }
1386
1387
1388 /*
1389 * 'cupsd_delete_event()' - Delete a single event...
1390 *
1391 * Oldest events must be deleted first, otherwise the subscription cache
1392 * flushing code will not work properly.
1393 */
1394
1395 static void
1396 cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1397 {
1398 cupsd_subscription_t *sub; /* Current subscription */
1399
1400
1401 /*
1402 * Loop through the subscriptions and look for the event in the cache...
1403 */
1404
1405 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1406 sub;
1407 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1408 {
1409 /*
1410 * Only check the first event in the subscription cache, since the
1411 * caller will only delete the oldest event in the cache...
1412 */
1413
1414 if (sub->num_events > 0 && sub->events[0] == event)
1415 {
1416 /*
1417 * Remove this event...
1418 */
1419
1420 sub->num_events --;
1421 sub->first_event_id ++;
1422
1423 if (sub->num_events > 0)
1424 {
1425 /*
1426 * Shift other events upward in cache...
1427 */
1428
1429 memmove(sub->events, sub->events + 1,
1430 sub->num_events * sizeof(cupsd_event_t *));
1431 }
1432 }
1433 }
1434
1435 /*
1436 * Free memory...
1437 */
1438
1439 ippDelete(event->attrs);
1440 free(event);
1441 }
1442
1443
1444 #ifdef HAVE_DBUS
1445 /*
1446 * 'cupsd_send_dbus()' - Send a DBUS notification...
1447 */
1448
1449 static void
1450 cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
1451 cupsd_printer_t *dest,/* I - Destination, if any */
1452 cupsd_job_t *job) /* I - Job, if any */
1453 {
1454 DBusError error; /* Error, if any */
1455 DBusMessage *message; /* Message to send */
1456 DBusMessageIter iter; /* Iterator for message data */
1457 const char *what; /* What to send */
1458 static DBusConnection *con = NULL; /* Connection to DBUS server */
1459
1460
1461 /*
1462 * Figure out what to send, if anything...
1463 */
1464
1465 if (event & CUPSD_EVENT_PRINTER_ADDED)
1466 what = "PrinterAdded";
1467 else if (event & CUPSD_EVENT_PRINTER_DELETED)
1468 what = "PrinterRemoved";
1469 else if (event & CUPSD_EVENT_PRINTER_CHANGED)
1470 what = "QueueChanged";
1471 else if (event & CUPSD_EVENT_JOB_CREATED)
1472 what = "JobQueuedLocal";
1473 else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
1474 job->state_value == IPP_JOB_PROCESSING)
1475 what = "JobStartedLocal";
1476 else
1477 return;
1478
1479 /*
1480 * Verify connection to DBUS server...
1481 */
1482
1483 if (con && !dbus_connection_get_is_connected(con))
1484 {
1485 dbus_connection_unref(con);
1486 con = NULL;
1487 }
1488
1489 if (!con)
1490 {
1491 dbus_error_init(&error);
1492
1493 con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
1494 if (!con)
1495 {
1496 dbus_error_free(&error);
1497 return;
1498 }
1499 }
1500
1501 /*
1502 * Create and send the new message...
1503 */
1504
1505 message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
1506 "com.redhat.PrinterSpooler", what);
1507
1508 dbus_message_iter_init_append(message, &iter);
1509 if (dest)
1510 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &(dest->name));
1511 if (job)
1512 {
1513 dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &(job->id));
1514 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &(job->username));
1515 }
1516
1517 dbus_connection_send(con, message, NULL);
1518 dbus_connection_flush(con);
1519 dbus_message_unref(message);
1520 }
1521 #endif /* HAVE_DBUS */
1522
1523
1524 /*
1525 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1526 */
1527
1528 static void
1529 cupsd_start_notifier(
1530 cupsd_subscription_t *sub) /* I - Subscription object */
1531 {
1532 int pid; /* Notifier process ID */
1533 int fds[2]; /* Pipe file descriptors */
1534 int envc; /* Number of environment variables */
1535 char *argv[4], /* Command-line arguments */
1536 *envp[MAX_ENV], /* Environment variables */
1537 user_data[128], /* Base-64 encoded user data */
1538 scheme[256], /* notify-recipient-uri scheme */
1539 *ptr, /* Pointer into scheme */
1540 command[1024]; /* Notifier command */
1541
1542
1543 /*
1544 * Extract the scheme name from the recipient URI and point to the
1545 * notifier program...
1546 */
1547
1548 strlcpy(scheme, sub->recipient, sizeof(scheme));
1549 if ((ptr = strchr(scheme, ':')) != NULL)
1550 *ptr = '\0';
1551
1552 snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1553
1554 /*
1555 * Base-64 encode the user data...
1556 */
1557
1558 httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
1559 sub->user_data_len);
1560
1561 /*
1562 * Setup the argument array...
1563 */
1564
1565 argv[0] = command;
1566 argv[1] = sub->recipient;
1567 argv[2] = user_data;
1568 argv[3] = NULL;
1569
1570 /*
1571 * Setup the environment...
1572 */
1573
1574 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1575
1576 /*
1577 * Create pipes as needed...
1578 */
1579
1580 if (!NotifierStatusBuffer)
1581 {
1582 /*
1583 * Create the status pipe...
1584 */
1585
1586 if (cupsdOpenPipe(NotifierPipes))
1587 {
1588 cupsdLogMessage(CUPSD_LOG_ERROR,
1589 "Unable to create pipes for notifier status - %s",
1590 strerror(errno));
1591 return;
1592 }
1593
1594 NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1595
1596 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1597 "start_notifier: Adding fd %d to InputSet...",
1598 NotifierPipes[0]);
1599
1600 FD_SET(NotifierPipes[0], InputSet);
1601 }
1602
1603 if (cupsdOpenPipe(fds))
1604 {
1605 cupsdLogMessage(CUPSD_LOG_ERROR,
1606 "Unable to create pipes for notifier %s - %s",
1607 scheme, strerror(errno));
1608 return;
1609 }
1610
1611 /*
1612 * Make sure the delivery pipe is non-blocking...
1613 */
1614
1615 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1616
1617 /*
1618 * Create the notifier process...
1619 */
1620
1621 if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
1622 -1, 0, &pid) < 0)
1623 {
1624 /*
1625 * Error - can't fork!
1626 */
1627
1628 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
1629 scheme, strerror(errno));
1630
1631 cupsdClosePipe(fds);
1632 }
1633 else
1634 {
1635 /*
1636 * Fork successful - return the PID...
1637 */
1638
1639 cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
1640 scheme, pid);
1641
1642 sub->pid = pid;
1643 sub->pipe = fds[1];
1644 sub->status = 0;
1645
1646 close(fds[0]);
1647 }
1648 }
1649
1650
1651 /*
1652 * End of "$Id: subscriptions.c 5249 2006-03-08 16:45:28Z mike $".
1653 */