]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/subscriptions.c
Rename all types and functions to use cupsd prefix.
[thirdparty/cups.git] / scheduler / subscriptions.c
CommitLineData
bf6409d1 1/*
2 * "$Id$"
3 *
4 * Subscription routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2005 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 * cupsdDeleteAllEvents() - Delete all cached events.
29 * cupsdDeleteAllSubscriptions() - Delete all subscriptions.
30 * cupsdDeleteSubscription() - Delete a subscription object.
31 * cupsdFindSubscription() - Find a subscription by ID.
32 * cupsdExpireSubscriptions() - Expire old subscription objects.
33 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file.
34 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file.
35 * cupsdSendNotification() - Send a notification for the specified event.
ddd3933d 36 * cupsdStopAllNotifiers() - Stop all notifier processes.
fd0624de 37 * cupsdUpdateNotifierStatus() - Read messages from notifiers.
38 * cupsd_delete_event() - Delete a single event...
bf6409d1 39 */
40
41/*
42 * Include necessary headers...
43 */
44
45#include "cupsd.h"
46
47
48/*
49 * Local functions...
50 */
51
52static void cupsd_delete_event(cupsd_event_t *event);
53
54
55/*
56 * 'cupsdAddEvent()' - Add an event to the global event cache.
57 */
58
59void
60cupsdAddEvent(
61 cupsd_eventmask_t event, /* I - Event */
589eb420 62 cupsd_printer_t *dest, /* I - Printer associated with event */
63 cupsd_job_t *job, /* I - Job associated with event */
bf6409d1 64 const char *text, /* I - Notification text */
65 ...) /* I - Additional arguments as needed */
66{
67 va_list ap; /* Pointer to additional arguments */
68 char ftext[1024]; /* Formatted text buffer */
69 cupsd_event_t *temp; /* New event pointer */
70 int i; /* Looping var */
71 cupsd_subscription_t *sub; /* Current subscription */
72
73
74 /*
75 * Return if we aren't keeping events...
76 */
77
78 if (MaxEvents <= 0)
79 {
589eb420 80 cupsdLogMessage(L_WARN, "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
bf6409d1 81 cupsdEventName(event), MaxEvents);
82 return;
83 }
84
85 /*
86 * Allocate memory for the event cache as needed...
87 */
88
89 if (!Events)
90 {
91 Events = calloc(MaxEvents, sizeof(cupsd_event_t *));
92 NumEvents = 0;
93
94 if (!Events)
95 {
589eb420 96 cupsdLogMessage(L_CRIT, "Unable to allocate memory for event cache - %s",
bf6409d1 97 strerror(errno));
98 return;
99 }
100 }
101
102 /*
103 * Then loop through the subscriptions and add the event to the corresponding
104 * caches...
105 */
106
107 for (i = 0, temp = NULL; i < NumSubscriptions; i ++)
108 {
109 /*
110 * Check if this subscription requires this event...
111 */
112
113 sub = Subscriptions[i];
114
115 if ((sub->mask & event) != 0 &&
116 (sub->dest == dest || !sub->dest) &&
117 (sub->job == job || !sub->job))
118 {
119 /*
120 * Need this event...
121 */
122
123 if (!temp)
124 {
125 /*
126 * Create the new event record...
127 */
128
129 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
130 {
589eb420 131 cupsdLogMessage(L_CRIT, "Unable to allocate memory for event - %s",
bf6409d1 132 strerror(errno));
133 return;
134 }
135
136 temp->event = event;
137 temp->time = time(NULL);
138 temp->attrs = ippNew();
139 temp->job = job;
140 temp->dest = dest;
141
556f5ea5 142 /*
143 * Add common event notification attributes...
144 */
145
bf6409d1 146 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
147 "notify-subscription-id", sub->id);
148
149 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
150 "notify-subscribed-event", NULL, cupsdEventName(event));
151
152 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
153 "printer-up-time", time(NULL));
154
155 va_start(ap, text);
156 vsnprintf(ftext, sizeof(ftext), text, ap);
157 va_end(ap);
158
159 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
160 "notify-text", NULL, ftext);
161
162 if (dest)
163 {
556f5ea5 164 /*
165 * Add printer attributes...
166 */
167
bf6409d1 168 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
169 "notify-printer-uri", NULL, dest->uri);
170
171 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
172 "printer-name", NULL, dest->name);
173
174 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
175 "printer-state", dest->state);
176
177 if (dest->num_reasons == 0)
178 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
179 IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
180 dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
181 else
182 ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
183 IPP_TAG_KEYWORD, "printer-state-reasons",
184 dest->num_reasons, NULL,
185 (const char * const *)dest->reasons);
186
187 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
188 "printer-is-accepting-jobs", dest->accepting);
189 }
190
191 if (job)
192 {
556f5ea5 193 /*
194 * Add job attributes...
195 */
196
bf6409d1 197 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
198 "job-id", job->id);
199 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
200 "job-state", (int)job->state);
201
202 switch (job->state->values[0].integer)
203 {
204 case IPP_JOB_PENDING :
205 if (dest && dest->state == IPP_PRINTER_STOPPED)
206 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
207 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
208 "printer-stopped");
209 else
210 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
211 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
212 "none");
213 break;
214
215 case IPP_JOB_HELD :
216 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
217 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
218 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
219 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
220 "job-hold-until-specified");
221 else
222 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
223 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
224 "job-incoming");
225 break;
226
227 case IPP_JOB_PROCESSING :
228 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
229 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
230 "job-printing");
231 break;
232
233 case IPP_JOB_STOPPED :
234 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
235 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
236 "job-stopped");
237 break;
238
239 case IPP_JOB_CANCELLED :
240 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
241 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
242 "job-canceled-by-user");
243 break;
244
245 case IPP_JOB_ABORTED :
246 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
247 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
248 "aborted-by-system");
249 break;
250
251 case IPP_JOB_COMPLETED :
252 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
253 IPP_TAG_KEYWORD, "job-state-reasons", NULL,
254 "job-completed-successfully");
255 break;
256 }
257
258 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
259 "job-impressions-completed",
260 job->sheets ? job->sheets->values[0].integer : 0);
261 }
262
263 /*
264 * Purge an old event as needed...
265 */
266
267 if (NumEvents >= MaxEvents)
268 {
269 /*
270 * Purge the oldest event in the cache...
271 */
272
273 cupsd_delete_event(Events[0]);
274
275 NumEvents --;
276
277 memmove(Events, Events + 1, NumEvents * sizeof(cupsd_event_t *));
278 }
279
280 /*
281 * Add the new event to the main cache...
282 */
283
284 Events[NumEvents] = temp;
285 NumEvents ++;
286 }
287
288 /*
289 * Send the notification for this subscription...
290 */
291
292 cupsdSendNotification(sub, temp);
293 }
294 }
295
296 if (temp)
297 cupsdSaveAllSubscriptions();
298 else
589eb420 299 cupsdLogMessage(L_DEBUG, "Discarding unused %s event...",
bf6409d1 300 cupsdEventName(event));
301}
302
303
304/*
305 * 'cupsdAddSubscription()' - Add a new subscription object.
306 */
307
308cupsd_subscription_t * /* O - New subscription object */
309cupsdAddSubscription(
310 unsigned mask, /* I - Event mask */
589eb420 311 cupsd_printer_t *dest, /* I - Printer, if any */
312 cupsd_job_t *job, /* I - Job, if any */
bf6409d1 313 const char *uri) /* I - notify-recipient-uri, if any */
314{
315 cupsd_subscription_t *temp; /* New subscription object */
316
317
589eb420 318 cupsdLogMessage(L_DEBUG, "cupsdAddSubscription(mask=%x(%s), dest=%p(%s), job=%p(%d), uri=\"%s\")",
277a6a9b 319 mask, cupsdEventName(mask), dest, dest ? dest->name : "",
320 job, job ? job->id : 0, uri);
321
bf6409d1 322 if (!Subscriptions)
323 {
324 /*
325 * Allocate memory for the subscription array...
326 */
327
328 Subscriptions = calloc(MaxSubscriptions, sizeof(cupsd_subscription_t *));
329 NumSubscriptions = 0;
330
331 if (!Subscriptions)
332 {
589eb420 333 cupsdLogMessage(L_CRIT, "Unable to allocate memory for subscriptions - %s",
bf6409d1 334 strerror(errno));
335 return (NULL);
336 }
337 }
338
339 /*
340 * Limit the number of subscriptions...
341 */
342
343 if (NumSubscriptions >= MaxSubscriptions)
344 return (NULL);
345
346 /*
347 * Allocate memory for this subscription...
348 */
349
350 if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
351 {
589eb420 352 cupsdLogMessage(L_CRIT, "Unable to allocate memory for subscription object - %s",
bf6409d1 353 strerror(errno));
354 return (NULL);
355 }
356
357 /*
358 * Fill in common data...
359 */
360
361 temp->id = NextSubscriptionId;
362 temp->mask = mask;
363 temp->job = job;
364 temp->dest = dest;
365 temp->first_event_id = 1;
366 temp->next_event_id = 1;
367
589eb420 368 cupsdSetString(&(temp->recipient), uri);
bf6409d1 369
370 /*
371 * Add the subscription to the array...
372 */
373
374 Subscriptions[NumSubscriptions] = temp;
375 NumSubscriptions ++;
376 NextSubscriptionId ++;
377
378 return (temp);
379}
380
381
382/*
383 * 'cupsdDeleteAllEvents()' - Delete all cached events.
384 */
385
386void
387cupsdDeleteAllEvents(void)
388{
389 int i; /* Looping var */
390
391
392 if (MaxEvents <= 0 || !Events)
393 return;
394
395 for (i = 0; i < NumEvents; i ++)
396 cupsd_delete_event(Events[i]);
397
398 free(Events);
399 Events = NULL;
400}
401
402
403/*
404 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
405 */
406
407void
408cupsdDeleteAllSubscriptions(void)
409{
410 int i; /* Looping var */
411
412
413 if (!Subscriptions)
414 return;
415
416 for (i = 0; i < NumSubscriptions; i ++)
417 cupsdDeleteSubscription(Subscriptions[i], 0);
418
419 free(Subscriptions);
420 Subscriptions = NULL;
421}
422
423
424/*
425 * 'cupsdDeleteSubscription()' - Delete a subscription object.
426 */
427
428void
429cupsdDeleteSubscription(
430 cupsd_subscription_t *sub, /* I - Subscription object */
431 int update) /* I - 1 = update subscriptions.conf */
432{
433 int i; /* Looping var */
434
435
436 /*
437 * Free memory...
438 */
439
589eb420 440 cupsdClearString(&(sub->owner));
441 cupsdClearString(&(sub->recipient));
bf6409d1 442
443 if (sub->events)
444 free(sub->events);
445
446 free(sub);
447
448 /*
449 * Remove subscription from array...
450 */
451
452 for (i = 0; i < NumSubscriptions; i ++)
453 if (Subscriptions[i] == sub)
454 {
455 /*
456 * Remove from array and stop...
457 */
458
459 NumSubscriptions --;
460
461 if (i < NumSubscriptions)
462 memmove(Subscriptions + i, Subscriptions + i + 1,
463 (NumSubscriptions - i) * sizeof(cupsd_subscription_t *));
464 break;
465 }
466
467 /*
468 * Update the subscriptions as needed...
469 */
470
471 if (update)
472 cupsdSaveAllSubscriptions();
473}
474
475
476/*
477 * 'cupsdEventName()' - Return a single event name.
478 */
479
480const char * /* O - Event name */
481cupsdEventName(
482 cupsd_eventmask_t event) /* I - Event value */
483{
484 switch (event)
485 {
486 default :
487 return (NULL);
488
489 case CUPSD_EVENT_PRINTER_RESTARTED :
490 return ("printer-restarted");
491
492 case CUPSD_EVENT_PRINTER_SHUTDOWN :
493 return ("printer-shutdown");
494
495 case CUPSD_EVENT_PRINTER_STOPPED :
496 return ("printer-stopped");
497
498 case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
499 return ("printer-finishings-changed");
500
501 case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
502 return ("printer-media-changed");
503
504 case CUPSD_EVENT_PRINTER_ADDED :
505 return ("printer-added");
506
507 case CUPSD_EVENT_PRINTER_DELETED :
508 return ("printer-deleted");
509
510 case CUPSD_EVENT_PRINTER_MODIFIED :
511 return ("printer-modified");
512
513 case CUPSD_EVENT_PRINTER_STATE_CHANGED :
514 return ("printer-state-changed");
515
516 case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
517 return ("printer-config-changed");
518
519 case CUPSD_EVENT_PRINTER_CHANGED :
520 return ("printer-changed");
521
522 case CUPSD_EVENT_JOB_CREATED :
523 return ("job-created");
524
525 case CUPSD_EVENT_JOB_COMPLETED :
526 return ("job-completed");
527
528 case CUPSD_EVENT_JOB_STOPPED :
529 return ("job-stopped");
530
531 case CUPSD_EVENT_JOB_CONFIG_CHANGED :
532 return ("job-config-changed");
533
534 case CUPSD_EVENT_JOB_PROGRESS :
535 return ("job-progress");
536
537 case CUPSD_EVENT_JOB_STATE_CHANGED :
538 return ("job-state-changed");
539
540 case CUPSD_EVENT_SERVER_RESTARTED :
541 return ("server-restarted");
542
543 case CUPSD_EVENT_SERVER_STARTED :
544 return ("server-started");
545
546 case CUPSD_EVENT_SERVER_STOPPED :
547 return ("server-stopped");
548
549 case CUPSD_EVENT_SERVER_AUDIT :
550 return ("server-audit");
551
552 case CUPSD_EVENT_ALL :
553 return ("all");
554 }
555}
556
557
558/*
559 * 'cupsdEventValue()' - Return the event mask value for a name.
560 */
561
562cupsd_eventmask_t /* O - Event mask value */
563cupsdEventValue(const char *name) /* I - Name of event */
564{
565 if (!strcmp(name, "all"))
566 return (CUPSD_EVENT_ALL);
567 else if (!strcmp(name, "printer-restarted"))
568 return (CUPSD_EVENT_PRINTER_RESTARTED);
569 else if (!strcmp(name, "printer-shutdown"))
570 return (CUPSD_EVENT_PRINTER_SHUTDOWN);
571 else if (!strcmp(name, "printer-stopped"))
572 return (CUPSD_EVENT_PRINTER_STOPPED);
573 else if (!strcmp(name, "printer-finishings-changed"))
574 return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
575 else if (!strcmp(name, "printer-media-changed"))
576 return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
577 else if (!strcmp(name, "printer-added"))
578 return (CUPSD_EVENT_PRINTER_ADDED);
579 else if (!strcmp(name, "printer-deleted"))
580 return (CUPSD_EVENT_PRINTER_DELETED);
581 else if (!strcmp(name, "printer-modified"))
582 return (CUPSD_EVENT_PRINTER_MODIFIED);
583 else if (!strcmp(name, "printer-state-changed"))
584 return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
585 else if (!strcmp(name, "printer-config-changed"))
586 return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
587 else if (!strcmp(name, "printer-changed"))
588 return (CUPSD_EVENT_PRINTER_CHANGED);
589 else if (!strcmp(name, "job-created"))
590 return (CUPSD_EVENT_JOB_CREATED);
591 else if (!strcmp(name, "job-completed"))
592 return (CUPSD_EVENT_JOB_COMPLETED);
593 else if (!strcmp(name, "job-stopped"))
594 return (CUPSD_EVENT_JOB_STOPPED);
595 else if (!strcmp(name, "job-config-changed"))
596 return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
597 else if (!strcmp(name, "job-progress"))
598 return (CUPSD_EVENT_JOB_PROGRESS);
599 else if (!strcmp(name, "job-state-changed"))
600 return (CUPSD_EVENT_JOB_STATE_CHANGED);
601 else if (!strcmp(name, "server-restarted"))
602 return (CUPSD_EVENT_SERVER_RESTARTED);
603 else if (!strcmp(name, "server-started"))
604 return (CUPSD_EVENT_SERVER_STARTED);
605 else if (!strcmp(name, "server-stopped"))
606 return (CUPSD_EVENT_SERVER_STOPPED);
607 else if (!strcmp(name, "server-audit"))
608 return (CUPSD_EVENT_SERVER_AUDIT);
609 else
610 return (CUPSD_EVENT_NONE);
611}
612
613
614/*
615 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
616 */
617
618void
619cupsdExpireSubscriptions(
589eb420 620 cupsd_printer_t *dest, /* I - Printer, if any */
621 cupsd_job_t *job) /* I - Job, if any */
bf6409d1 622{
623 int i; /* Looping var */
624 cupsd_subscription_t *sub; /* Current subscription */
625 int update; /* Update subscriptions.conf? */
626 time_t curtime; /* Current time */
627
628
629 curtime = time(NULL);
630
631 for (i = 0, update = 0; i < NumSubscriptions; i ++)
632 {
633 sub = Subscriptions[i];
634
635 if (sub->expire <= curtime ||
636 (dest && sub->dest == dest) ||
637 (job && sub->job == job))
638 {
589eb420 639 cupsdLogMessage(L_INFO, "Subscription %d has expired...", sub->id);
bf6409d1 640
641 cupsdDeleteSubscription(sub, 0);
642
643 update = 1;
644 }
645 }
646
647 if (update)
648 cupsdSaveAllSubscriptions();
649}
650
651
652/*
653 * 'cupsdFindSubscription()' - Find a subscription by ID.
654 */
655
656cupsd_subscription_t * /* O - Subscription object */
657cupsdFindSubscription(int id) /* I - Subscription ID */
658{
659 int left, /* Left side of binary search */
660 center, /* Center of binary search */
661 right; /* Right side of binary search */
662 cupsd_subscription_t *sub; /* Current subscription */
663
664
665 /*
666 * Return early if we have no subscriptions...
667 */
668
669 if (NumSubscriptions == 0)
670 return (NULL);
671
672 /*
673 * Otherwise do a binary search for the subscription ID...
674 */
675
676 for (left = 0, right = NumSubscriptions - 1; (right - left) > 1;)
677 {
678 center = (left + right) / 2;
679 sub = Subscriptions[center];
680
681 if (sub->id == id)
682 return (sub);
683 else if (sub->id < id)
684 left = center;
685 else
686 right = center;
687 }
688
689 if (Subscriptions[left]->id == id)
690 return (Subscriptions[left]);
691 else if (Subscriptions[right]->id == id)
692 return (Subscriptions[right]);
693 else
694 return (NULL);
695}
696
697
698/*
699 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
700 */
701
702void
703cupsdLoadAllSubscriptions(void)
704{
a4b3db80 705 int i; /* Looping var */
4a1c4581 706 cups_file_t *fp; /* subscriptions.conf file */
707 int linenum; /* Current line number */
4a1c4581 708 char line[1024], /* Line from file */
709 name[256], /* Parameter name */
4a1c4581 710 *value, /* Pointer to value */
711 *valueptr; /* Pointer into value */
712 cupsd_subscription_t *sub; /* Current subscription */
713 int hex; /* Non-zero if reading hex data */
a4b3db80 714 int delete_sub; /* Delete subscription? */
4a1c4581 715
716
717 /*
718 * Open the subscriptions.conf file...
719 */
720
721 snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
722 if ((fp = cupsFileOpen(line, "r")) == NULL)
723 {
589eb420 724 cupsdLogMessage(L_ERROR, "LoadAllSubscriptions: Unable to open %s - %s", line,
4a1c4581 725 strerror(errno));
726 return;
727 }
728
729 /*
730 * Read all of the lines from the file...
731 */
732
a4b3db80 733 linenum = 0;
734 sub = NULL;
735 delete_sub = 0;
4a1c4581 736
737 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
738 {
a4b3db80 739 if (!strcasecmp(name, "<Subscription"))
740 {
741 /*
742 * <Subscription #>
743 */
744
745 if (!sub && value && isdigit(value[0] & 255))
746 {
747 sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL);
748 sub->id = atoi(value);
749 }
750 else
751 {
589eb420 752 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 753 linenum);
754 return;
755 }
756 }
757 else if (!strcasecmp(name, "</Subscription>"))
758 {
759 if (!sub)
760 {
589eb420 761 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 762 linenum);
763 return;
764 }
765
766 if (delete_sub)
767 cupsdDeleteSubscription(sub, 0);
768
769 sub = NULL;
770 delete_sub = 0;
771 }
772 else if (!sub)
773 {
589eb420 774 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 775 linenum);
776 return;
777 }
778 else if (!strcasecmp(name, "Events"))
779 {
780 /*
781 * Events name
782 * Events name name name ...
783 */
784
785 if (!value)
786 {
589eb420 787 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 788 linenum);
789 return;
790 }
791
792 while (*value)
793 {
794 /*
795 * Separate event names...
796 */
797
798 for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
799
800 while (isspace(*valueptr & 255))
801 *valueptr++ = '\0';
802
803 /*
804 * See if the name exists...
805 */
806
807 if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
808 {
589eb420 809 cupsdLogMessage(L_ERROR, "Unknown event name \'%s\' on line %d of subscriptions.conf.",
a4b3db80 810 value, linenum);
811 return;
812 }
813
814 value = valueptr;
815 }
816 }
fd0624de 817 else if (!strcasecmp(name, "Owner"))
818 {
819 /*
820 * Owner
821 */
822
823 if (value)
589eb420 824 cupsdSetString(&sub->owner, value);
fd0624de 825 else
826 {
589eb420 827 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
fd0624de 828 linenum);
829 return;
830 }
831 }
a4b3db80 832 else if (!strcasecmp(name, "Recipient"))
833 {
834 /*
835 * Recipient uri
836 */
837
838 if (value)
589eb420 839 cupsdSetString(&sub->recipient, value);
a4b3db80 840 else
841 {
589eb420 842 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 843 linenum);
844 return;
845 }
846 }
847 else if (!strcasecmp(name, "JobId"))
848 {
849 /*
850 * JobId #
851 */
852
853 if (value && isdigit(*value & 255))
854 {
589eb420 855 if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
a4b3db80 856 {
589eb420 857 cupsdLogMessage(L_ERROR, "Job %s not found on line %d of subscriptions.conf.",
a4b3db80 858 value, linenum);
859 delete_sub = 1;
860 }
861 }
862 else
863 {
589eb420 864 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 865 linenum);
866 return;
867 }
868 }
869 else if (!strcasecmp(name, "PrinterName"))
870 {
871 /*
872 * PrinterName name
873 */
874
875 if (value)
876 {
589eb420 877 if ((sub->dest = cupsdFindDest(value)) == NULL)
a4b3db80 878 {
589eb420 879 cupsdLogMessage(L_ERROR, "Printer \'%s\' not found on line %d of subscriptions.conf.",
a4b3db80 880 value, linenum);
881 delete_sub = 1;
882 }
883 }
884 else
885 {
589eb420 886 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 887 linenum);
888 return;
889 }
890 }
891 else if (!strcasecmp(name, "UserData"))
892 {
893 /*
894 * UserData encoded-string
895 */
896
897 if (value)
898 {
899 for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
900 {
901 if (*valueptr == '<' && !hex)
902 {
903 hex = 1;
904 valueptr ++;
905 }
906
907 if (hex)
908 {
909 if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
910 {
911 if (isdigit(valueptr[0]))
912 sub->user_data[i] = (valueptr[0] - '0') << 4;
913 else
914 sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
915
916 if (isdigit(valueptr[1]))
917 sub->user_data[i] |= valueptr[1] - '0';
918 else
919 sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
920
921 valueptr += 2;
922
923 if (*valueptr == '>')
924 {
925 hex = 0;
926 valueptr ++;
927 }
928 }
929 else
930 break;
931 }
932 else
933 sub->user_data[i] = *valueptr++;
934 }
935
936 if (*valueptr)
937 {
589eb420 938 cupsdLogMessage(L_ERROR, "Bad UserData \'%s\' on line %d of subscriptions.conf.",
a4b3db80 939 value, linenum);
940 }
941 else
942 sub->user_data_len = i;
943 }
944 else
945 {
589eb420 946 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 947 linenum);
948 return;
949 }
950 }
951 else if (!strcasecmp(name, "LeaseTime"))
952 {
953 /*
954 * LeaseTime #
955 */
956
957 if (value && isdigit(*value & 255))
958 sub->lease = atoi(value);
959 else
960 {
589eb420 961 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 962 linenum);
963 return;
964 }
965 }
966 else if (!strcasecmp(name, "Interval"))
967 {
968 /*
969 * Interval #
970 */
971
972 if (value && isdigit(*value & 255))
973 sub->interval = atoi(value);
974 else
975 {
589eb420 976 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 977 linenum);
978 return;
979 }
980 }
981 else if (!strcasecmp(name, "ExpirationTime"))
982 {
983 /*
984 * ExpirationTime #
985 */
986
987 if (value && isdigit(*value & 255))
988 sub->expire = atoi(value);
989 else
990 {
589eb420 991 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 992 linenum);
993 return;
994 }
995 }
996 else if (!strcasecmp(name, "NextEventId"))
997 {
998 /*
999 * NextEventId #
1000 */
1001
1002 if (value && isdigit(*value & 255))
1003 sub->next_event_id = sub->first_event_id = atoi(value);
1004 else
1005 {
589eb420 1006 cupsdLogMessage(L_ERROR, "Syntax error on line %d of subscriptions.conf.",
a4b3db80 1007 linenum);
1008 return;
1009 }
1010 }
1011 else
1012 {
1013 /*
1014 * Something else we don't understand...
1015 */
1016
589eb420 1017 cupsdLogMessage(L_ERROR, "Unknown configuration directive %s on line %d of subscriptions.conf.",
a4b3db80 1018 name, linenum);
1019 }
4a1c4581 1020 }
1021
1022 cupsFileClose(fp);
bf6409d1 1023}
1024
1025
1026/*
1027 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
1028 */
1029
1030void
1031cupsdSaveAllSubscriptions(void)
1032{
4a1c4581 1033 int i, j; /* Looping vars */
bf6409d1 1034 cups_file_t *fp; /* subscriptions.conf file */
1035 char temp[1024]; /* Temporary string */
1036 char backup[1024]; /* subscriptions.conf.O file */
1037 cupsd_subscription_t *sub; /* Current subscription */
1038 time_t curtime; /* Current time */
1039 struct tm *curdate; /* Current date */
1040 unsigned mask; /* Current event mask */
1041 const char *name; /* Current event name */
4a1c4581 1042 int hex; /* Non-zero if we are writing hex data */
bf6409d1 1043
1044
1045 /*
1046 * Create the subscriptions.conf file...
1047 */
1048
1049 snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
1050 snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
1051
1052 if (rename(temp, backup))
589eb420 1053 cupsdLogMessage(L_ERROR, "Unable to backup subscriptions.conf - %s",
bf6409d1 1054 strerror(errno));
1055
1056 if ((fp = cupsFileOpen(temp, "w")) == NULL)
1057 {
589eb420 1058 cupsdLogMessage(L_ERROR, "Unable to save subscriptions.conf - %s",
bf6409d1 1059 strerror(errno));
1060
1061 if (rename(backup, temp))
589eb420 1062 cupsdLogMessage(L_ERROR, "Unable to restore subscriptions.conf - %s",
bf6409d1 1063 strerror(errno));
1064 return;
1065 }
1066 else
589eb420 1067 cupsdLogMessage(L_INFO, "Saving subscriptions.conf...");
bf6409d1 1068
1069 /*
1070 * Restrict access to the file...
1071 */
1072
1073 fchown(cupsFileNumber(fp), getuid(), Group);
1074 fchmod(cupsFileNumber(fp), ConfigFilePerm);
1075
1076 /*
1077 * Write a small header to the file...
1078 */
1079
1080 curtime = time(NULL);
1081 curdate = localtime(&curtime);
03f61bf3 1082 strftime(temp, sizeof(temp) - 1, "%c", curdate);
bf6409d1 1083
1084 cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
1085 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1086
1087 /*
1088 * Write every subscription known to the system...
1089 */
1090
1091 for (i = 0; i < NumSubscriptions; i ++)
1092 {
1093 sub = Subscriptions[i];
1094
1095 cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
1096
1097 if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
1098 {
1099 /*
1100 * Simple event list...
1101 */
1102
1103 cupsFilePrintf(fp, "Events %s\n", name);
1104 }
1105 else
1106 {
1107 /*
1108 * Complex event list...
1109 */
1110
1111 cupsFilePuts(fp, "Events");
1112
1113 for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
1114 if (sub->mask & mask)
1115 cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
1116
1117 cupsFilePuts(fp, "\n");
1118 }
1119
fd0624de 1120 if (sub->owner)
1121 cupsFilePrintf(fp, "Owner %s\n", sub->owner);
bf6409d1 1122 if (sub->recipient)
1123 cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
1124 if (sub->job)
1125 cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
1126 if (sub->dest)
1127 cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
1128
4a1c4581 1129 if (sub->user_data_len > 0)
1130 {
1131 cupsFilePuts(fp, "UserData ");
1132
1133 for (j = 0, hex = 0; j < sub->user_data_len; j ++)
1134 {
1135 if (sub->user_data[j] < ' ' ||
1136 sub->user_data[j] > 0x7f ||
1137 sub->user_data[j] == '<')
1138 {
1139 if (!hex)
1140 {
1141 cupsFilePrintf(fp, "<%02X", sub->user_data[j]);
1142 hex = 1;
1143 }
1144 else
1145 cupsFilePrintf(fp, "%02X", sub->user_data[j]);
1146 }
1147 else
1148 {
1149 if (hex)
1150 {
1151 cupsFilePrintf(fp, ">%c", sub->user_data[j]);
1152 hex = 0;
1153 }
1154 else
1155 cupsFilePutChar(fp, sub->user_data[j]);
1156 }
1157 }
1158
1159 if (hex)
1160 cupsFilePuts(fp, ">\n");
1161 else
1162 cupsFilePutChar(fp, '\n');
1163 }
1164
bf6409d1 1165 cupsFilePrintf(fp, "LeaseTime %d\n", sub->lease);
1166 cupsFilePrintf(fp, "Interval %d\n", sub->interval);
1167 cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
1168 cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
1169
1170 cupsFilePuts(fp, "</Subscription>\n");
1171 }
1172
1173 cupsFileClose(fp);
1174}
1175
1176
1177/*
1178 * 'cupsdSendNotification()' - Send a notification for the specified event.
1179 */
1180
1181void
1182cupsdSendNotification(
1183 cupsd_subscription_t *sub, /* I - Subscription object */
1184 cupsd_event_t *event) /* I - Event to send */
1185{
589eb420 1186 cupsdLogMessage(L_DEBUG, "cupsdSendNotification(sub=%p(%d), event=%p(%s))\n",
bf6409d1 1187 sub, sub->id, event, cupsdEventName(event->event));
1188
277a6a9b 1189 /*
1190 * Allocate the events array as needed...
1191 */
1192
1193 if (!sub->events)
1194 {
1195 sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
1196
1197 if (!sub->events)
1198 {
589eb420 1199 cupsdLogMessage(L_CRIT, "Unable to allocate memory for subscription #%d!",
277a6a9b 1200 sub->id);
1201 return;
1202 }
1203 }
1204
bf6409d1 1205 /*
1206 * Add the event to the subscription. Since the events array is
1207 * always MaxEvents in length, and since we will have already
1208 * removed an event from the subscription cache if we hit the
1209 * event cache limit, we don't need to check for overflow here...
1210 */
1211
1212 sub->events[sub->num_events] = event;
1213 sub->num_events ++;
1214
1215 /*
1216 * Deliver the event...
1217 */
1218
1219 /**** TODO ****/
1220
1221 /*
1222 * Bump the event sequence number...
1223 */
1224
1225 sub->next_event_id ++;
1226}
1227
1228
ddd3933d 1229/*
1230 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1231 */
1232
1233void
1234cupsdStopAllNotifiers(void)
1235{
1236 int i; /* Looping var */
1237 cupsd_subscription_t *sub; /* Current subscription */
1238
1239
1240 /*
1241 * See if we have started any notifiers...
1242 */
1243
1244 if (!NotifierStatusBuffer)
1245 return;
1246
1247 /*
1248 * Yes, kill and processes that are left...
1249 */
1250
1251 for (i = 0; i < NumSubscriptions; i ++)
1252 {
1253 sub = Subscriptions[i];
1254
1255 if (sub->pid)
1256 {
1257 cupsdEndProcess(sub->pid, 0);
1258
1259 close(sub->pipe);
1260 sub->pipe = -1;
1261 }
1262 }
1263
1264 /*
1265 * Close the status pipes...
1266 */
1267
589eb420 1268 cupsdLogMessage(L_DEBUG2, "cupsdStopAllNotifiers: Removing fd %d from InputSet...",
ddd3933d 1269 NotifierPipes[0]);
1270 FD_CLR(NotifierPipes[0], InputSet);
1271
1272 cupsdStatBufDelete(NotifierStatusBuffer);
1273
1274 close(NotifierPipes[0]);
1275 close(NotifierPipes[1]);
1276
1277 NotifierPipes[0] = -1;
1278 NotifierPipes[1] = -1;
1279 NotifierStatusBuffer = NULL;
1280}
1281
1282
fd0624de 1283/*
1284 * 'cupsdUpdateNotifierStatus()' - Read messages from notifiers.
1285 */
1286
1287void
1288cupsdUpdateNotiferStatus(void)
1289{
ddd3933d 1290 char *ptr, /* Pointer to end of line in buffer */
1291 message[1024]; /* Pointer to message text */
1292 int loglevel; /* Log level for message */
1293
1294
1295 while ((ptr = cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
1296 message, sizeof(message))) != NULL)
1297 if (!strchr(NotifierStatusBuffer->buffer, '\n'))
1298 break;
1299
1300 if (ptr == NULL)
1301 {
1302 /*
1303 * Fatal error on pipe - should never happen!
1304 */
1305
589eb420 1306 cupsdLogMessage(L_CRIT, "cupsdUpdateNotifierStatus: error reading from notifier error pipe - %s",
ddd3933d 1307 strerror(errno));
1308 }
fd0624de 1309}
1310
1311
bf6409d1 1312/*
1313 * 'cupsd_delete_event()' - Delete a single event...
1314 *
1315 * Oldest events must be deleted first, otherwise the subscription cache
1316 * flushing code will not work properly.
1317 */
1318
1319static void
1320cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
1321{
1322 int i; /* Looping var */
1323 cupsd_subscription_t *sub; /* Current subscription */
1324
1325
1326 /*
1327 * Loop through the subscriptions and look for the event in the cache...
1328 */
1329
1330 for (i = 0; i < NumSubscriptions; i ++)
1331 {
1332 /*
1333 * Only check the first event in the subscription cache, since the
1334 * caller will only delete the oldest event in the cache...
1335 */
1336
1337 sub = Subscriptions[i];
1338
1339 if (sub->num_events > 0 && sub->events[0] == event)
1340 {
1341 /*
1342 * Remove this event...
1343 */
1344
1345 sub->num_events --;
1346 sub->first_event_id ++;
1347
1348 if (sub->num_events > 0)
1349 {
1350 /*
1351 * Shift other events upward in cache...
1352 */
1353
1354 memmove(sub->events, sub->events + 1,
1355 sub->num_events * sizeof(cupsd_event_t *));
1356 }
1357 }
1358 }
1359
1360 /*
1361 * Free memory...
1362 */
1363
1364 ippDelete(event->attrs);
1365 free(event);
1366}
1367
1368
1369/*
1370 * End of "$Id$".
1371 */