1/* 2 * "$Id: subscriptions.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * Subscription routines for the CUPS scheduler. 5 * 6 * Copyright 2007-2011 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * Contents: 16 * 17 * cupsdAddEvent() - Add an event to the global event cache. 18 * cupsdAddSubscription() - Add a new subscription object. 19 * cupsdDeleteAllSubscriptions() - Delete all subscriptions. 20 * cupsdDeleteSubscription() - Delete a subscription object. 21 * cupsdEventName() - Return a single event name. 22 * cupsdEventValue() - Return the event mask value for a name. 23 * cupsdExpireSubscriptions() - Expire old subscription objects. 24 * cupsdFindSubscription() - Find a subscription by ID. 25 * cupsdLoadAllSubscriptions() - Load all subscriptions from the .conf file. 26 * cupsdSaveAllSubscriptions() - Save all subscriptions to the .conf file. 27 * cupsdStopAllNotifiers() - Stop all notifier processes. 28 * cupsd_compare_subscriptions() - Compare two subscriptions. 29 * cupsd_delete_event() - Delete a single event... 30 * cupsd_send_dbus() - Send a DBUS notification... 31 * cupsd_send_notification() - Send a notification for the specified 32 * event. 33 * cupsd_start_notifier() - Start a notifier subprocess... 34 * cupsd_update_notifier() - Read messages from notifiers. 35 */ 36 37/* 38 * Include necessary headers... 39 */ 40 41#include "cupsd.h" 42#ifdef HAVE_DBUS 43# include <dbus/dbus.h> 44# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND 45# define dbus_message_append_iter_init dbus_message_iter_init_append 46# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v)) 47# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v)) 48# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */ 49#endif /* HAVE_DBUS */ 50 51 52/* 53 * Local functions... 54 */ 55 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); 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 */ 64static void cupsd_send_notification(cupsd_subscription_t *sub, 65 cupsd_event_t *event); 66static void cupsd_start_notifier(cupsd_subscription_t *sub); 67static void cupsd_update_notifier(void); 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 */ 84 ipp_attribute_t *attr; /* Printer/job attribute */ 85 cupsd_event_t *temp; /* New event pointer */ 86 cupsd_subscription_t *sub; /* Current subscription */ 87 88 89 cupsdLogMessage(CUPSD_LOG_DEBUG2, 90 "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)", 91 cupsdEventName(event), dest, dest ? dest->name : "", 92 job, job ? job->id : 0, text); 93 94 /* 95 * Keep track of events with any OS-supplied notification mechanisms... 96 */ 97 98 LastEvent |= event; 99 100#ifdef HAVE_DBUS 101 cupsd_send_dbus(event, dest, job); 102#endif /* HAVE_DBUS */ 103 104 /* 105 * Return if we aren't keeping events... 106 */ 107 108 if (MaxEvents <= 0) 109 { 110 cupsdLogMessage(CUPSD_LOG_WARN, 111 "cupsdAddEvent: Discarding %s event since MaxEvents is %d!", 112 cupsdEventName(event), MaxEvents); 113 return; 114 } 115 116 /* 117 * Then loop through the subscriptions and add the event to the corresponding 118 * caches... 119 */ 120 121 for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions); 122 sub; 123 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions)) 124 { 125 /* 126 * Check if this subscription requires this event... 127 */ 128 129 if ((sub->mask & event) != 0 && 130 (sub->dest == dest || !sub->dest) && 131 (sub->job == job || !sub->job)) 132 { 133 /* 134 * Need this event, so create a new event record... 135 */ 136 137 if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL) 138 { 139 cupsdLogMessage(CUPSD_LOG_CRIT, 140 "Unable to allocate memory for event - %s", 141 strerror(errno)); 142 return; 143 } 144 145 temp->event = event; 146 temp->time = time(NULL); 147 temp->attrs = ippNew(); 148 temp->job = job; 149 150 if (dest) 151 temp->dest = dest; 152 else if (job) 153 temp->dest = dest = cupsdFindPrinter(job->dest); 154 155 /* 156 * Add common event notification attributes... 157 */ 158 159 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET, 160 "notify-charset", NULL, "utf-8"); 161 162 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE, 163 "notify-natural-language", NULL, "en-US"); 164 165 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, 166 "notify-subscription-id", sub->id); 167 168 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, 169 "notify-sequence-number", sub->next_event_id); 170 171 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, 172 "notify-subscribed-event", NULL, cupsdEventName(event)); 173 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); 178 179 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, 180 "printer-up-time", time(NULL)); 181 182 va_start(ap, text); 183 vsnprintf(ftext, sizeof(ftext), text, ap); 184 va_end(ap); 185 186 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT, 187 "notify-text", NULL, ftext); 188 189 if (dest) 190 { 191 /* 192 * Add printer attributes... 193 */ 194 195 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, 196 "notify-printer-uri", NULL, dest->uri); 197 198 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, 199 "printer-name", NULL, dest->name); 200 201 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, 202 "printer-state", dest->state); 203 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); 213 214 ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, 215 "printer-is-accepting-jobs", dest->accepting); 216 } 217 218 if (job) 219 { 220 /* 221 * Add job attributes... 222 */ 223 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) 238 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, 239 IPP_TAG_KEYWORD, "job-state-reasons", NULL, 240 "printer-stopped"); 241 else 242 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, 243 IPP_TAG_KEYWORD, "job-state-reasons", NULL, 244 "none"); 245 break; 246 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) 250 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, 251 IPP_TAG_KEYWORD, "job-state-reasons", NULL, 252 "job-hold-until-specified"); 253 else 254 ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, 255 IPP_TAG_KEYWORD, "job-state-reasons", NULL, 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 271 case IPP_JOB_CANCELED : 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; 288 } 289 290 ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, 291 "job-impressions-completed", 292 job->sheets ? job->sheets->values[0].integer : 0); 293 } 294 295 /* 296 * Send the notification for this subscription... 297 */ 298 299 cupsd_send_notification(sub, temp); 300 } 301 } 302 303 if (temp) 304 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); 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, 327 "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), " 328 "uri=\"%s\")", 329 mask, dest, dest ? dest->name : "", job, job ? job->id : 0, 330 uri ? uri : "(null)"); 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 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)); 354 return (NULL); 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 } 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 444 /* 445 * For RSS subscriptions, run the notifier immediately... 446 */ 447 448 if (uri && !strncmp(uri, "rss:", 4)) 449 cupsd_start_notifier(temp); 450 451 return (temp); 452} 453 454 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 507 cupsArrayDelete(sub->events); 508 509 free(sub); 510 511 /* 512 * Update the subscriptions as needed... 513 */ 514 515 if (update) 516 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); 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 557 case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED : 558 return ("printer-queue-order-changed"); 559 560 case CUPSD_EVENT_PRINTER_STATE : 561 case CUPSD_EVENT_PRINTER_STATE_CHANGED : 562 return ("printer-state-changed"); 563 564 case CUPSD_EVENT_PRINTER_CONFIG : 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 586 case CUPSD_EVENT_JOB_STATE : 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); 633 else if (!strcmp(name, "printer-queue-order-changed")) 634 return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED); 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)) 686 if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) || 687 (dest && sub->dest == dest) || 688 (job && sub->job == job)) 689 { 690 cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...", 691 sub->id); 692 693 cupsdDeleteSubscription(sub, 0); 694 695 update = 1; 696 } 697 698 if (update) 699 cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS); 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); 742 if ((fp = cupsdOpenConfFile(line)) == NULL) 743 return; 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 { 755 if (!_cups_strcasecmp(line, "NextSubscriptionId") && value) 756 { 757 /* 758 * NextSubscriptionId NNN 759 */ 760 761 i = atoi(value); 762 if (i >= NextSubscriptionId && i > 0) 763 NextSubscriptionId = i; 764 } 765 else if (!_cups_strcasecmp(line, "<Subscription")) 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); 781 break; 782 } 783 } 784 else if (!_cups_strcasecmp(line, "</Subscription>")) 785 { 786 if (!sub) 787 { 788 cupsdLogMessage(CUPSD_LOG_ERROR, 789 "Syntax error on line %d of subscriptions.conf.", 790 linenum); 791 break; 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); 805 } 806 else if (!_cups_strcasecmp(line, "Events")) 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); 818 break; 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); 841 break; 842 } 843 844 value = valueptr; 845 } 846 } 847 else if (!_cups_strcasecmp(line, "Owner")) 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); 860 break; 861 } 862 } 863 else if (!_cups_strcasecmp(line, "Recipient")) 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); 876 break; 877 } 878 } 879 else if (!_cups_strcasecmp(line, "JobId")) 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); 900 break; 901 } 902 } 903 else if (!_cups_strcasecmp(line, "PrinterName")) 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); 924 break; 925 } 926 } 927 else if (!_cups_strcasecmp(line, "UserData")) 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); 986 break; 987 } 988 } 989 else if (!_cups_strcasecmp(line, "LeaseDuration")) 990 { 991 /* 992 * LeaseDuration # 993 */ 994 995 if (value && isdigit(*value & 255)) 996 { 997 sub->lease = atoi(value); 998 sub->expire = sub->lease ? time(NULL) + sub->lease : 0; 999 } 1000 else 1001 { 1002 cupsdLogMessage(CUPSD_LOG_ERROR, 1003 "Syntax error on line %d of subscriptions.conf.", 1004 linenum); 1005 break; 1006 } 1007 } 1008 else if (!_cups_strcasecmp(line, "Interval")) 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); 1021 break; 1022 } 1023 } 1024 else if (!_cups_strcasecmp(line, "ExpirationTime")) 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); 1037 break; 1038 } 1039 } 1040 else if (!_cups_strcasecmp(line, "NextEventId")) 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); 1053 break; 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 */ 1081 char filename[1024], /* subscriptions.conf filename */ 1082 temp[1024]; /* Temporary string */ 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 1095 snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot); 1096 1097 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL) 1098 return; 1099 1100 cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf..."); 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 1113 cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId); 1114 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 1201 cupsdCloseCreatedConfFile(fp, filename); 1202} 1203 1204 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 /* 1223 * Yes, kill any processes that are left... 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 { 1243 cupsdRemoveSelect(NotifierPipes[0]); 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 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{ 1283 /* 1284 * Free memory... 1285 */ 1286 1287 ippDelete(event->attrs); 1288 free(event); 1289} 1290 1291 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 && 1322 job->state_value == IPP_JOB_PROCESSING) 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 1356 dbus_message_append_iter_init(message, &iter); 1357 if (dest) 1358 dbus_message_iter_append_string(&iter, dest->name); 1359 if (job) 1360 { 1361 dbus_message_iter_append_uint32(&iter, job->id); 1362 dbus_message_iter_append_string(&iter, job->username); 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 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 1384 cupsdLogMessage(CUPSD_LOG_DEBUG2, 1385 "cupsd_send_notification(sub=%p(%d), event=%p(%s))", 1386 sub, sub->id, event, cupsdEventName(event->event)); 1387 1388 /* 1389 * Allocate the events array as needed... 1390 */ 1391 1392 if (!sub->events) 1393 { 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); 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 1408 /* 1409 * Purge an old event as needed... 1410 */ 1411 1412 if (cupsArrayCount(sub->events) >= MaxEvents) 1413 { 1414 /* 1415 * Purge the oldest event in the cache... 1416 */ 1417 1418 cupsArrayRemove(sub->events, cupsArrayFirst(sub->events)); 1419 1420 sub->first_event_id ++; 1421 } 1422 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 1430 cupsArrayAdd(sub->events, event); 1431 1432 /* 1433 * Deliver the event... 1434 */ 1435 1436 if (sub->recipient) 1437 { 1438 for (;;) 1439 { 1440 if (sub->pipe < 0) 1441 cupsd_start_notifier(sub); 1442 1443 cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe); 1444 1445 if (sub->pipe < 0) 1446 break; 1447 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) 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 1473 cupsdLogMessage(CUPSD_LOG_ERROR, 1474 "Unable to send event for subscription %d (%s)!", 1475 sub->id, sub->recipient); 1476 } 1477 1478 /* 1479 * If we get this far, break out of the loop... 1480 */ 1481 1482 break; 1483 } 1484 } 1485 1486 /* 1487 * Bump the event sequence number... 1488 */ 1489 1490 sub->next_event_id ++; 1491} 1492 1493 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 */ 1504 char *argv[4], /* Command-line arguments */ 1505 *envp[MAX_ENV], /* Environment variables */ 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 1543 cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); 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 1565 cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier, 1566 NULL, NULL); 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], 1588 -1, -1, 0, DefaultProfile, NULL, &pid) < 0) 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/* 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))) 1630 { 1631 if (loglevel == CUPSD_LOG_INFO) 1632 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message); 1633 1634 if (!strchr(NotifierStatusBuffer->buffer, '\n')) 1635 break; 1636 } 1637} 1638 1639 1640/* 1641 * End of "$Id: subscriptions.c 11093 2013-07-03 20:48:42Z msweet $". 1642 */ 1643