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