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