]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/subscriptions.c
Merge changes from CUPS 1.4svn-r7696.
[thirdparty/cups.git] / scheduler / subscriptions.c
1 /*
2 * "$Id: subscriptions.c 7673 2008-06-18 22:31:26Z mike $"
3 *
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 2007-2008 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 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
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 ? uri : "(null)");
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 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
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_QUEUE_ORDER_CHANGED :
508 return ("printer-queue-order-changed");
509
510 case CUPSD_EVENT_PRINTER_STATE :
511 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
512 return ("printer-state-changed");
513
514 case CUPSD_EVENT_PRINTER_CONFIG :
515 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
516 return ("printer-config-changed");
517
518 case CUPSD_EVENT_PRINTER_CHANGED :
519 return ("printer-changed");
520
521 case CUPSD_EVENT_JOB_CREATED :
522 return ("job-created");
523
524 case CUPSD_EVENT_JOB_COMPLETED :
525 return ("job-completed");
526
527 case CUPSD_EVENT_JOB_STOPPED :
528 return ("job-stopped");
529
530 case CUPSD_EVENT_JOB_CONFIG_CHANGED :
531 return ("job-config-changed");
532
533 case CUPSD_EVENT_JOB_PROGRESS :
534 return ("job-progress");
535
536 case CUPSD_EVENT_JOB_STATE :
537 case CUPSD_EVENT_JOB_STATE_CHANGED :
538 return ("job-state-changed");
539
540 case CUPSD_EVENT_SERVER_RESTARTED :
541 return ("server-restarted");
542
543 case CUPSD_EVENT_SERVER_STARTED :
544 return ("server-started");
545
546 case CUPSD_EVENT_SERVER_STOPPED :
547 return ("server-stopped");
548
549 case CUPSD_EVENT_SERVER_AUDIT :
550 return ("server-audit");
551
552 case CUPSD_EVENT_ALL :
553 return ("all");
554 }
555 }
556
557
558 /*
559 * 'cupsdEventValue()' - Return the event mask value for a name.
560 */
561
562 cupsd_eventmask_t /* O - Event mask value */
563 cupsdEventValue(const char *name) /* I - Name of event */
564 {
565 if (!strcmp(name, "all"))
566 return (CUPSD_EVENT_ALL);
567 else if (!strcmp(name, "printer-restarted"))
568 return (CUPSD_EVENT_PRINTER_RESTARTED);
569 else if (!strcmp(name, "printer-shutdown"))
570 return (CUPSD_EVENT_PRINTER_SHUTDOWN);
571 else if (!strcmp(name, "printer-stopped"))
572 return (CUPSD_EVENT_PRINTER_STOPPED);
573 else if (!strcmp(name, "printer-finishings-changed"))
574 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
575 else if (!strcmp(name, "printer-media-changed"))
576 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
577 else if (!strcmp(name, "printer-added"))
578 return (CUPSD_EVENT_PRINTER_ADDED);
579 else if (!strcmp(name, "printer-deleted"))
580 return (CUPSD_EVENT_PRINTER_DELETED);
581 else if (!strcmp(name, "printer-modified"))
582 return (CUPSD_EVENT_PRINTER_MODIFIED);
583 else if (!strcmp(name, "printer-queue-order-changed"))
584 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
585 else if (!strcmp(name, "printer-state-changed"))
586 return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
587 else if (!strcmp(name, "printer-config-changed"))
588 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
589 else if (!strcmp(name, "printer-changed"))
590 return (CUPSD_EVENT_PRINTER_CHANGED);
591 else if (!strcmp(name, "job-created"))
592 return (CUPSD_EVENT_JOB_CREATED);
593 else if (!strcmp(name, "job-completed"))
594 return (CUPSD_EVENT_JOB_COMPLETED);
595 else if (!strcmp(name, "job-stopped"))
596 return (CUPSD_EVENT_JOB_STOPPED);
597 else if (!strcmp(name, "job-config-changed"))
598 return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
599 else if (!strcmp(name, "job-progress"))
600 return (CUPSD_EVENT_JOB_PROGRESS);
601 else if (!strcmp(name, "job-state-changed"))
602 return (CUPSD_EVENT_JOB_STATE_CHANGED);
603 else if (!strcmp(name, "server-restarted"))
604 return (CUPSD_EVENT_SERVER_RESTARTED);
605 else if (!strcmp(name, "server-started"))
606 return (CUPSD_EVENT_SERVER_STARTED);
607 else if (!strcmp(name, "server-stopped"))
608 return (CUPSD_EVENT_SERVER_STOPPED);
609 else if (!strcmp(name, "server-audit"))
610 return (CUPSD_EVENT_SERVER_AUDIT);
611 else
612 return (CUPSD_EVENT_NONE);
613 }
614
615
616 /*
617 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
618 */
619
620 void
621 cupsdExpireSubscriptions(
622 cupsd_printer_t *dest, /* I - Printer, if any */
623 cupsd_job_t *job) /* I - Job, if any */
624 {
625 cupsd_subscription_t *sub; /* Current subscription */
626 int update; /* Update subscriptions.conf? */
627 time_t curtime; /* Current time */
628
629
630 curtime = time(NULL);
631 update = 0;
632
633 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
634 sub;
635 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
636 if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
637 (dest && sub->dest == dest) ||
638 (job && sub->job == job))
639 {
640 cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
641 sub->id);
642
643 cupsdDeleteSubscription(sub, 0);
644
645 update = 1;
646 }
647
648 if (update)
649 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
650 }
651
652
653 /*
654 * 'cupsdFindSubscription()' - Find a subscription by ID.
655 */
656
657 cupsd_subscription_t * /* O - Subscription object */
658 cupsdFindSubscription(int id) /* I - Subscription ID */
659 {
660 cupsd_subscription_t sub; /* Subscription template */
661
662
663 sub.id = id;
664
665 return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
666 }
667
668
669 /*
670 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
671 */
672
673 void
674 cupsdLoadAllSubscriptions(void)
675 {
676 int i; /* Looping var */
677 cups_file_t *fp; /* subscriptions.conf file */
678 int linenum; /* Current line number */
679 char line[1024], /* Line from file */
680 *value, /* Pointer to value */
681 *valueptr; /* Pointer into value */
682 cupsd_subscription_t *sub; /* Current subscription */
683 int hex; /* Non-zero if reading hex data */
684 int delete_sub; /* Delete subscription? */
685
686
687 /*
688 * Open the subscriptions.conf file...
689 */
690
691 snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
692 if ((fp = cupsFileOpen(line, "r")) == NULL)
693 {
694 if (errno != ENOENT)
695 cupsdLogMessage(CUPSD_LOG_ERROR,
696 "LoadAllSubscriptions: Unable to open %s - %s", line,
697 strerror(errno));
698 return;
699 }
700
701 /*
702 * Read all of the lines from the file...
703 */
704
705 linenum = 0;
706 sub = NULL;
707 delete_sub = 0;
708
709 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
710 {
711 if (!strcasecmp(line, "NextSubscriptionId") && value)
712 {
713 /*
714 * NextSubscriptionId NNN
715 */
716
717 i = atoi(value);
718 if (i >= NextSubscriptionId && i > 0)
719 NextSubscriptionId = i;
720 }
721 else if (!strcasecmp(line, "<Subscription"))
722 {
723 /*
724 * <Subscription #>
725 */
726
727 if (!sub && value && isdigit(value[0] & 255))
728 {
729 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
730 atoi(value));
731 }
732 else
733 {
734 cupsdLogMessage(CUPSD_LOG_ERROR,
735 "Syntax error on line %d of subscriptions.conf.",
736 linenum);
737 break;
738 }
739 }
740 else if (!strcasecmp(line, "</Subscription>"))
741 {
742 if (!sub)
743 {
744 cupsdLogMessage(CUPSD_LOG_ERROR,
745 "Syntax error on line %d of subscriptions.conf.",
746 linenum);
747 break;
748 }
749
750 if (delete_sub)
751 cupsdDeleteSubscription(sub, 0);
752
753 sub = NULL;
754 delete_sub = 0;
755 }
756 else if (!sub)
757 {
758 cupsdLogMessage(CUPSD_LOG_ERROR,
759 "Syntax error on line %d of subscriptions.conf.",
760 linenum);
761 break;
762 }
763 else if (!strcasecmp(line, "Events"))
764 {
765 /*
766 * Events name
767 * Events name name name ...
768 */
769
770 if (!value)
771 {
772 cupsdLogMessage(CUPSD_LOG_ERROR,
773 "Syntax error on line %d of subscriptions.conf.",
774 linenum);
775 break;
776 }
777
778 while (*value)
779 {
780 /*
781 * Separate event names...
782 */
783
784 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
785
786 while (isspace(*valueptr & 255))
787 *valueptr++ = '\0';
788
789 /*
790 * See if the name exists...
791 */
792
793 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
794 {
795 cupsdLogMessage(CUPSD_LOG_ERROR,
796 "Unknown event name \'%s\' on line %d of subscriptions.conf.",
797 value, linenum);
798 break;
799 }
800
801 value = valueptr;
802 }
803 }
804 else if (!strcasecmp(line, "Owner"))
805 {
806 /*
807 * Owner
808 */
809
810 if (value)
811 cupsdSetString(&sub->owner, value);
812 else
813 {
814 cupsdLogMessage(CUPSD_LOG_ERROR,
815 "Syntax error on line %d of subscriptions.conf.",
816 linenum);
817 break;
818 }
819 }
820 else if (!strcasecmp(line, "Recipient"))
821 {
822 /*
823 * Recipient uri
824 */
825
826 if (value)
827 cupsdSetString(&sub->recipient, value);
828 else
829 {
830 cupsdLogMessage(CUPSD_LOG_ERROR,
831 "Syntax error on line %d of subscriptions.conf.",
832 linenum);
833 break;
834 }
835 }
836 else if (!strcasecmp(line, "JobId"))
837 {
838 /*
839 * JobId #
840 */
841
842 if (value && isdigit(*value & 255))
843 {
844 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
845 {
846 cupsdLogMessage(CUPSD_LOG_ERROR,
847 "Job %s not found on line %d of subscriptions.conf.",
848 value, linenum);
849 delete_sub = 1;
850 }
851 }
852 else
853 {
854 cupsdLogMessage(CUPSD_LOG_ERROR,
855 "Syntax error on line %d of subscriptions.conf.",
856 linenum);
857 break;
858 }
859 }
860 else if (!strcasecmp(line, "PrinterName"))
861 {
862 /*
863 * PrinterName name
864 */
865
866 if (value)
867 {
868 if ((sub->dest = cupsdFindDest(value)) == NULL)
869 {
870 cupsdLogMessage(CUPSD_LOG_ERROR,
871 "Printer \'%s\' not found on line %d of subscriptions.conf.",
872 value, linenum);
873 delete_sub = 1;
874 }
875 }
876 else
877 {
878 cupsdLogMessage(CUPSD_LOG_ERROR,
879 "Syntax error on line %d of subscriptions.conf.",
880 linenum);
881 break;
882 }
883 }
884 else if (!strcasecmp(line, "UserData"))
885 {
886 /*
887 * UserData encoded-string
888 */
889
890 if (value)
891 {
892 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
893 {
894 if (*valueptr == '<' && !hex)
895 {
896 hex = 1;
897 valueptr ++;
898 }
899
900 if (hex)
901 {
902 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
903 {
904 if (isdigit(valueptr[0]))
905 sub->user_data[i] = (valueptr[0] - '0') << 4;
906 else
907 sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
908
909 if (isdigit(valueptr[1]))
910 sub->user_data[i] |= valueptr[1] - '0';
911 else
912 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
913
914 valueptr += 2;
915
916 if (*valueptr == '>')
917 {
918 hex = 0;
919 valueptr ++;
920 }
921 }
922 else
923 break;
924 }
925 else
926 sub->user_data[i] = *valueptr++;
927 }
928
929 if (*valueptr)
930 {
931 cupsdLogMessage(CUPSD_LOG_ERROR,
932 "Bad UserData \'%s\' on line %d of subscriptions.conf.",
933 value, linenum);
934 }
935 else
936 sub->user_data_len = i;
937 }
938 else
939 {
940 cupsdLogMessage(CUPSD_LOG_ERROR,
941 "Syntax error on line %d of subscriptions.conf.",
942 linenum);
943 break;
944 }
945 }
946 else if (!strcasecmp(line, "LeaseDuration"))
947 {
948 /*
949 * LeaseDuration #
950 */
951
952 if (value && isdigit(*value & 255))
953 {
954 sub->lease = atoi(value);
955 sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
956 }
957 else
958 {
959 cupsdLogMessage(CUPSD_LOG_ERROR,
960 "Syntax error on line %d of subscriptions.conf.",
961 linenum);
962 break;
963 }
964 }
965 else if (!strcasecmp(line, "Interval"))
966 {
967 /*
968 * Interval #
969 */
970
971 if (value && isdigit(*value & 255))
972 sub->interval = atoi(value);
973 else
974 {
975 cupsdLogMessage(CUPSD_LOG_ERROR,
976 "Syntax error on line %d of subscriptions.conf.",
977 linenum);
978 break;
979 }
980 }
981 else if (!strcasecmp(line, "ExpirationTime"))
982 {
983 /*
984 * ExpirationTime #
985 */
986
987 if (value && isdigit(*value & 255))
988 sub->expire = atoi(value);
989 else
990 {
991 cupsdLogMessage(CUPSD_LOG_ERROR,
992 "Syntax error on line %d of subscriptions.conf.",
993 linenum);
994 break;
995 }
996 }
997 else if (!strcasecmp(line, "NextEventId"))
998 {
999 /*
1000 * NextEventId #
1001 */
1002
1003 if (value && isdigit(*value & 255))
1004 sub->next_event_id = sub->first_event_id = atoi(value);
1005 else
1006 {
1007 cupsdLogMessage(CUPSD_LOG_ERROR,
1008 "Syntax error on line %d of subscriptions.conf.",
1009 linenum);
1010 break;
1011 }
1012 }
1013 else
1014 {
1015 /*
1016 * Something else we don't understand...
1017 */
1018
1019 cupsdLogMessage(CUPSD_LOG_ERROR,
1020 "Unknown configuration directive %s on line %d of subscriptions.conf.",
1021 line, linenum);
1022 }
1023 }
1024
1025 cupsFileClose(fp);
1026 }
1027
1028
1029 /*
1030 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1031 */
1032
1033 void
1034 cupsdSaveAllSubscriptions(void)
1035 {
1036 int i; /* Looping var */
1037 cups_file_t *fp; /* subscriptions.conf file */
1038 char temp[1024]; /* Temporary string */
1039 char backup[1024]; /* subscriptions.conf.O file */
1040 cupsd_subscription_t *sub; /* Current subscription */
1041 time_t curtime; /* Current time */
1042 struct tm *curdate; /* Current date */
1043 unsigned mask; /* Current event mask */
1044 const char *name; /* Current event name */
1045 int hex; /* Non-zero if we are writing hex data */
1046
1047
1048 /*
1049 * Create the subscriptions.conf file...
1050 */
1051
1052 snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
1053 snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
1054
1055 if (rename(temp, backup))
1056 {
1057 if (errno != ENOENT)
1058 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
1059 strerror(errno));
1060 }
1061
1062 if ((fp = cupsFileOpen(temp, "w")) == NULL)
1063 {
1064 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
1065 strerror(errno));
1066
1067 if (rename(backup, temp))
1068 cupsdLogMessage(CUPSD_LOG_ERROR,
1069 "Unable to restore subscriptions.conf - %s",
1070 strerror(errno));
1071 return;
1072 }
1073 else
1074 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
1075
1076 /*
1077 * Restrict access to the file...
1078 */
1079
1080 fchown(cupsFileNumber(fp), getuid(), Group);
1081 fchmod(cupsFileNumber(fp), ConfigFilePerm);
1082
1083 /*
1084 * Write a small header to the file...
1085 */
1086
1087 curtime = time(NULL);
1088 curdate = localtime(&curtime);
1089 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1090
1091 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1092 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1093
1094 cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
1095
1096 /*
1097 * Write every subscription known to the system...
1098 */
1099
1100 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1101 sub;
1102 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1103 {
1104 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1105
1106 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1107 {
1108 /*
1109 * Simple event list...
1110 */
1111
1112 cupsFilePrintf(fp, "Events %s\n", name);
1113 }
1114 else
1115 {
1116 /*
1117 * Complex event list...
1118 */
1119
1120 cupsFilePuts(fp, "Events");
1121
1122 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1123 if (sub->mask & mask)
1124 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1125
1126 cupsFilePuts(fp, "\n");
1127 }
1128
1129 if (sub->owner)
1130 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
1131 if (sub->recipient)
1132 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1133 if (sub->job)
1134 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1135 if (sub->dest)
1136 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1137
1138 if (sub->user_data_len > 0)
1139 {
1140 cupsFilePuts(fp, "UserData ");
1141
1142 for (i = 0, hex = 0; i < sub->user_data_len; i ++)
1143 {
1144 if (sub->user_data[i] < ' ' ||
1145 sub->user_data[i] > 0x7f ||
1146 sub->user_data[i] == '<')
1147 {
1148 if (!hex)
1149 {
1150 cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
1151 hex = 1;
1152 }
1153 else
1154 cupsFilePrintf(fp, "%02X", sub->user_data[i]);
1155 }
1156 else
1157 {
1158 if (hex)
1159 {
1160 cupsFilePrintf(fp, ">%c", sub->user_data[i]);
1161 hex = 0;
1162 }
1163 else
1164 cupsFilePutChar(fp, sub->user_data[i]);
1165 }
1166 }
1167
1168 if (hex)
1169 cupsFilePuts(fp, ">\n");
1170 else
1171 cupsFilePutChar(fp, '\n');
1172 }
1173
1174 cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
1175 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1176 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1177 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1178
1179 cupsFilePuts(fp, "</Subscription>\n");
1180 }
1181
1182 cupsFileClose(fp);
1183 }
1184
1185
1186 /*
1187 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1188 */
1189
1190 void
1191 cupsdStopAllNotifiers(void)
1192 {
1193 cupsd_subscription_t *sub; /* Current subscription */
1194
1195
1196 /*
1197 * See if we have started any notifiers...
1198 */
1199
1200 if (!NotifierStatusBuffer)
1201 return;
1202
1203 /*
1204 * Yes, kill any processes that are left...
1205 */
1206
1207 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
1208 sub;
1209 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
1210 if (sub->pid)
1211 {
1212 cupsdEndProcess(sub->pid, 0);
1213
1214 close(sub->pipe);
1215 sub->pipe = -1;
1216 }
1217
1218 /*
1219 * Close the status pipes...
1220 */
1221
1222 if (NotifierPipes[0] >= 0)
1223 {
1224 cupsdRemoveSelect(NotifierPipes[0]);
1225
1226 cupsdStatBufDelete(NotifierStatusBuffer);
1227
1228 close(NotifierPipes[0]);
1229 close(NotifierPipes[1]);
1230
1231 NotifierPipes[0] = -1;
1232 NotifierPipes[1] = -1;
1233 NotifierStatusBuffer = NULL;
1234 }
1235 }
1236
1237
1238 /*
1239 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
1240 */
1241
1242 static int /* O - Result of comparison */
1243 cupsd_compare_subscriptions(
1244 cupsd_subscription_t *first, /* I - First subscription object */
1245 cupsd_subscription_t *second, /* I - Second subscription object */
1246 void *unused) /* I - Unused user data pointer */
1247 {
1248 (void)unused;
1249
1250 return (first->id - second->id);
1251 }
1252
1253
1254 /*
1255 * 'cupsd_delete_event()' - Delete a single event...
1256 *
1257 * Oldest events must be deleted first, otherwise the subscription cache
1258 * flushing code will not work properly.
1259 */
1260
1261 static void
1262 cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1263 {
1264 /*
1265 * Free memory...
1266 */
1267
1268 ippDelete(event->attrs);
1269 free(event);
1270 }
1271
1272
1273 #ifdef HAVE_DBUS
1274 /*
1275 * 'cupsd_send_dbus()' - Send a DBUS notification...
1276 */
1277
1278 static void
1279 cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
1280 cupsd_printer_t *dest,/* I - Destination, if any */
1281 cupsd_job_t *job) /* I - Job, if any */
1282 {
1283 DBusError error; /* Error, if any */
1284 DBusMessage *message; /* Message to send */
1285 DBusMessageIter iter; /* Iterator for message data */
1286 const char *what; /* What to send */
1287 static DBusConnection *con = NULL; /* Connection to DBUS server */
1288
1289
1290 /*
1291 * Figure out what to send, if anything...
1292 */
1293
1294 if (event & CUPSD_EVENT_PRINTER_ADDED)
1295 what = "PrinterAdded";
1296 else if (event & CUPSD_EVENT_PRINTER_DELETED)
1297 what = "PrinterRemoved";
1298 else if (event & CUPSD_EVENT_PRINTER_CHANGED)
1299 what = "QueueChanged";
1300 else if (event & CUPSD_EVENT_JOB_CREATED)
1301 what = "JobQueuedLocal";
1302 else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
1303 job->state_value == IPP_JOB_PROCESSING)
1304 what = "JobStartedLocal";
1305 else
1306 return;
1307
1308 /*
1309 * Verify connection to DBUS server...
1310 */
1311
1312 if (con && !dbus_connection_get_is_connected(con))
1313 {
1314 dbus_connection_unref(con);
1315 con = NULL;
1316 }
1317
1318 if (!con)
1319 {
1320 dbus_error_init(&error);
1321
1322 con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
1323 if (!con)
1324 {
1325 dbus_error_free(&error);
1326 return;
1327 }
1328 }
1329
1330 /*
1331 * Create and send the new message...
1332 */
1333
1334 message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
1335 "com.redhat.PrinterSpooler", what);
1336
1337 dbus_message_append_iter_init(message, &iter);
1338 if (dest)
1339 dbus_message_iter_append_string(&iter, dest->name);
1340 if (job)
1341 {
1342 dbus_message_iter_append_uint32(&iter, job->id);
1343 dbus_message_iter_append_string(&iter, job->username);
1344 }
1345
1346 dbus_connection_send(con, message, NULL);
1347 dbus_connection_flush(con);
1348 dbus_message_unref(message);
1349 }
1350 #endif /* HAVE_DBUS */
1351
1352
1353 /*
1354 * 'cupsd_send_notification()' - Send a notification for the specified event.
1355 */
1356
1357 static void
1358 cupsd_send_notification(
1359 cupsd_subscription_t *sub, /* I - Subscription object */
1360 cupsd_event_t *event) /* I - Event to send */
1361 {
1362 ipp_state_t state; /* IPP event state */
1363
1364
1365 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1366 "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
1367 sub, sub->id, event, cupsdEventName(event->event));
1368
1369 /*
1370 * Allocate the events array as needed...
1371 */
1372
1373 if (!sub->events)
1374 {
1375 sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
1376
1377 if (!sub->events)
1378 {
1379 cupsdLogMessage(CUPSD_LOG_CRIT,
1380 "Unable to allocate memory for subscription #%d!",
1381 sub->id);
1382 return;
1383 }
1384 }
1385
1386 /*
1387 * Purge an old event as needed...
1388 */
1389
1390 if (sub->num_events >= MaxEvents)
1391 {
1392 /*
1393 * Purge the oldest event in the cache...
1394 */
1395
1396 cupsd_delete_event(sub->events[0]);
1397
1398 sub->num_events --;
1399 sub->first_event_id ++;
1400
1401 memmove(sub->events, sub->events + 1,
1402 sub->num_events * sizeof(cupsd_event_t *));
1403 }
1404
1405 /*
1406 * Add the event to the subscription. Since the events array is
1407 * always MaxEvents in length, and since we will have already
1408 * removed an event from the subscription cache if we hit the
1409 * event cache limit, we don't need to check for overflow here...
1410 */
1411
1412 sub->events[sub->num_events] = event;
1413 sub->num_events ++;
1414
1415 /*
1416 * Deliver the event...
1417 */
1418
1419 if (sub->recipient)
1420 {
1421 for (;;)
1422 {
1423 if (sub->pipe < 0)
1424 cupsd_start_notifier(sub);
1425
1426 cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
1427
1428 if (sub->pipe < 0)
1429 break;
1430
1431 event->attrs->state = IPP_IDLE;
1432
1433 while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
1434 if (state == IPP_ERROR)
1435 break;
1436
1437 if (state == IPP_ERROR)
1438 {
1439 if (errno == EPIPE)
1440 {
1441 /*
1442 * Notifier died, try restarting it...
1443 */
1444
1445 cupsdLogMessage(CUPSD_LOG_WARN,
1446 "Notifier for subscription %d (%s) went away, "
1447 "retrying!",
1448 sub->id, sub->recipient);
1449 cupsdEndProcess(sub->pid, 0);
1450
1451 close(sub->pipe);
1452 sub->pipe = -1;
1453 continue;
1454 }
1455
1456 cupsdLogMessage(CUPSD_LOG_ERROR,
1457 "Unable to send event for subscription %d (%s)!",
1458 sub->id, sub->recipient);
1459 }
1460
1461 /*
1462 * If we get this far, break out of the loop...
1463 */
1464
1465 break;
1466 }
1467 }
1468
1469 /*
1470 * Bump the event sequence number...
1471 */
1472
1473 sub->next_event_id ++;
1474 }
1475
1476
1477 /*
1478 * 'cupsd_start_notifier()' - Start a notifier subprocess...
1479 */
1480
1481 static void
1482 cupsd_start_notifier(
1483 cupsd_subscription_t *sub) /* I - Subscription object */
1484 {
1485 int pid; /* Notifier process ID */
1486 int fds[2]; /* Pipe file descriptors */
1487 char *argv[4], /* Command-line arguments */
1488 *envp[MAX_ENV], /* Environment variables */
1489 user_data[128], /* Base-64 encoded user data */
1490 scheme[256], /* notify-recipient-uri scheme */
1491 *ptr, /* Pointer into scheme */
1492 command[1024]; /* Notifier command */
1493
1494
1495 /*
1496 * Extract the scheme name from the recipient URI and point to the
1497 * notifier program...
1498 */
1499
1500 strlcpy(scheme, sub->recipient, sizeof(scheme));
1501 if ((ptr = strchr(scheme, ':')) != NULL)
1502 *ptr = '\0';
1503
1504 snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
1505
1506 /*
1507 * Base-64 encode the user data...
1508 */
1509
1510 httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
1511 sub->user_data_len);
1512
1513 /*
1514 * Setup the argument array...
1515 */
1516
1517 argv[0] = command;
1518 argv[1] = sub->recipient;
1519 argv[2] = user_data;
1520 argv[3] = NULL;
1521
1522 /*
1523 * Setup the environment...
1524 */
1525
1526 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1527
1528 /*
1529 * Create pipes as needed...
1530 */
1531
1532 if (!NotifierStatusBuffer)
1533 {
1534 /*
1535 * Create the status pipe...
1536 */
1537
1538 if (cupsdOpenPipe(NotifierPipes))
1539 {
1540 cupsdLogMessage(CUPSD_LOG_ERROR,
1541 "Unable to create pipes for notifier status - %s",
1542 strerror(errno));
1543 return;
1544 }
1545
1546 NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
1547
1548 cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
1549 NULL, NULL);
1550 }
1551
1552 if (cupsdOpenPipe(fds))
1553 {
1554 cupsdLogMessage(CUPSD_LOG_ERROR,
1555 "Unable to create pipes for notifier %s - %s",
1556 scheme, strerror(errno));
1557 return;
1558 }
1559
1560 /*
1561 * Make sure the delivery pipe is non-blocking...
1562 */
1563
1564 fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
1565
1566 /*
1567 * Create the notifier process...
1568 */
1569
1570 if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
1571 -1, -1, 0, DefaultProfile, &pid) < 0)
1572 {
1573 /*
1574 * Error - can't fork!
1575 */
1576
1577 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
1578 scheme, strerror(errno));
1579
1580 cupsdClosePipe(fds);
1581 }
1582 else
1583 {
1584 /*
1585 * Fork successful - return the PID...
1586 */
1587
1588 cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
1589 scheme, pid);
1590
1591 sub->pid = pid;
1592 sub->pipe = fds[1];
1593 sub->status = 0;
1594
1595 close(fds[0]);
1596 }
1597 }
1598
1599
1600 /*
1601 * 'cupsd_update_notifier()' - Read messages from notifiers.
1602 */
1603
1604 void
1605 cupsd_update_notifier(void)
1606 {
1607 char message[1024]; /* Pointer to message text */
1608 int loglevel; /* Log level for message */
1609
1610
1611 while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1612 message, sizeof(message)))
1613 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1614 break;
1615 }
1616
1617
1618 /*
1619 * End of "$Id: subscriptions.c 7673 2008-06-18 22:31:26Z mike $".
1620 */