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