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