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