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