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