1/* 2 * "$Id: dbus.c 11528 2014-01-14 20:24:03Z msweet $" 3 * 4 * D-Bus notifier for CUPS. 5 * 6 * Copyright 2008-2014 by Apple Inc. 7 * Copyright (C) 2011, 2013 Red Hat, Inc. 8 * Copyright (C) 2007 Tim Waugh <twaugh@redhat.com> 9 * Copyright 1997-2005 by Easy Software Products. 10 * 11 * These coded instructions, statements, and computer programs are the 12 * property of Apple Inc. and are protected by Federal copyright 13 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 14 * which should have been included with this file. If this file is 15 * file is missing or damaged, see the license at "http://www.cups.org/". 16 */ 17 18/* 19 * Include necessary headers... 20 */ 21 22#include <cups/cups.h> 23#include <cups/string-private.h> 24#include <fcntl.h> 25#include <signal.h> 26#include <sys/stat.h> 27#include <sys/types.h> 28#include <unistd.h> 29 30#ifdef HAVE_DBUS 31# include <dbus/dbus.h> 32# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND 33# define dbus_message_append_iter_init dbus_message_iter_init_append 34# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, v) 35# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, v) 36# define dbus_message_iter_append_boolean(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, v) 37# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */ 38 39 40/* 41 * D-Bus object: org.cups.cupsd.Notifier 42 * D-Bus object path: /org/cups/cupsd/Notifier 43 * 44 * D-Bus interface name: org.cups.cupsd.Notifier 45 * 46 * Signals: 47 * 48 * ServerRestarted(STRING text) 49 * Server has restarted. 50 * 51 * ServerStarted(STRING text) 52 * Server has started. 53 * 54 * ServerStopped(STRING text) 55 * Server has stopped. 56 * 57 * ServerAudit(STRING text) 58 * Security-related event. 59 * 60 * PrinterRestarted(STRING text, 61 * STRING printer-uri, 62 * STRING printer-name, 63 * UINT32 printer-state, 64 * STRING printer-state-reasons, 65 * BOOLEAN printer-is-accepting-jobs) 66 * Printer has restarted. 67 * 68 * PrinterShutdown(STRING text, 69 * STRING printer-uri, 70 * STRING printer-name, 71 * UINT32 printer-state, 72 * STRING printer-state-reasons, 73 * BOOLEAN printer-is-accepting-jobs) 74 * Printer has shutdown. 75 * 76 * PrinterStopped(STRING text, 77 * STRING printer-uri, 78 * STRING printer-name, 79 * UINT32 printer-state, 80 * STRING printer-state-reasons, 81 * BOOLEAN printer-is-accepting-jobs) 82 * Printer has stopped. 83 * 84 * PrinterStateChanged(STRING text, 85 * STRING printer-uri, 86 * STRING printer-name, 87 * UINT32 printer-state, 88 * STRING printer-state-reasons, 89 * BOOLEAN printer-is-accepting-jobs) 90 * Printer state has changed. 91 * 92 * PrinterFinishingsChanged(STRING text, 93 * STRING printer-uri, 94 * STRING printer-name, 95 * UINT32 printer-state, 96 * STRING printer-state-reasons, 97 * BOOLEAN printer-is-accepting-jobs) 98 * Printer's finishings-supported attribute has changed. 99 * 100 * PrinterMediaChanged(STRING text, 101 * STRING printer-uri, 102 * STRING printer-name, 103 * UINT32 printer-state, 104 * STRING printer-state-reasons, 105 * BOOLEAN printer-is-accepting-jobs) 106 * Printer's media-supported attribute has changed. 107 * 108 * PrinterAdded(STRING text, 109 * STRING printer-uri, 110 * STRING printer-name, 111 * UINT32 printer-state, 112 * STRING printer-state-reasons, 113 * BOOLEAN printer-is-accepting-jobs) 114 * Printer has been added. 115 * 116 * PrinterDeleted(STRING text, 117 * STRING printer-uri, 118 * STRING printer-name, 119 * UINT32 printer-state, 120 * STRING printer-state-reasons, 121 * BOOLEAN printer-is-accepting-jobs) 122 * Printer has been deleted. 123 * 124 * PrinterModified(STRING text, 125 * STRING printer-uri, 126 * STRING printer-name, 127 * UINT32 printer-state, 128 * STRING printer-state-reasons, 129 * BOOLEAN printer-is-accepting-jobs) 130 * Printer has been modified. 131 * 132 * text describes the event. 133 * printer-state-reasons is a comma-separated list. 134 * If printer-uri is "" in a Job* signal, the other printer-* parameters 135 * must be ignored. 136 * If the job name is not know, job-name will be "". 137 */ 138 139/* 140 * Constants... 141 */ 142 143enum 144{ 145 PARAMS_NONE, 146 PARAMS_PRINTER, 147 PARAMS_JOB 148}; 149 150 151/* 152 * Global variables... 153 */ 154 155static char lock_filename[1024]; /* Lock filename */ 156 157 158/* 159 * Local functions... 160 */ 161 162static int acquire_lock(int *fd, char *lockfile, size_t locksize); 163static void release_lock(void); 164 165 166/* 167 * 'main()' - Read events and send DBUS notifications. 168 */ 169 170int /* O - Exit status */ 171main(int argc, /* I - Number of command-line args */ 172 char *argv[]) /* I - Command-line arguments */ 173{ 174 ipp_t *msg; /* Event message from scheduler */ 175 ipp_state_t state; /* IPP event state */ 176 struct sigaction action; /* POSIX sigaction data */ 177 DBusConnection *con = NULL; /* Connection to DBUS server */ 178 DBusError error; /* Error, if any */ 179 DBusMessage *message; /* Message to send */ 180 DBusMessageIter iter; /* Iterator for message data */ 181 int lock_fd = -1; /* Lock file descriptor */ 182 183 184 /* 185 * Don't buffer stderr... 186 */ 187 188 setbuf(stderr, NULL); 189 190 /* 191 * Ignore SIGPIPE signals... 192 */ 193 194 memset(&action, 0, sizeof(action)); 195 action.sa_handler = SIG_IGN; 196 sigaction(SIGPIPE, &action, NULL); 197 198 /* 199 * Validate command-line options... 200 */ 201 202 if (argc != 3) 203 { 204 fputs("Usage: dbus dbus:/// notify-user-data\n", stderr); 205 return (1); 206 } 207 208 if (strncmp(argv[1], "dbus:", 5)) 209 { 210 fprintf(stderr, "ERROR: Bad URI \"%s\"!\n", argv[1]); 211 return (1); 212 } 213 214 /* 215 * Loop forever until we run out of events... 216 */ 217 218 for (;;) 219 { 220 ipp_attribute_t *attr; /* Current attribute */ 221 const char *event; /* Event name */ 222 const char *signame = NULL;/* DBUS signal name */ 223 char *printer_reasons = NULL; 224 /* Printer reasons string */ 225 char *job_reasons = NULL; 226 /* Job reasons string */ 227 const char *nul = ""; /* Empty string value */ 228 int no = 0; /* Boolean "no" value */ 229 int params = PARAMS_NONE; 230 /* What parameters to include? */ 231 232 233 /* 234 * Get the next event... 235 */ 236 237 msg = ippNew(); 238 while ((state = ippReadFile(0, msg)) != IPP_DATA) 239 { 240 if (state <= IPP_IDLE) 241 break; 242 } 243 244 fprintf(stderr, "DEBUG: state=%d\n", state); 245 246 if (state == IPP_ERROR) 247 fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr); 248 249 if (state <= IPP_IDLE) 250 { 251 /* 252 * Out of messages, free memory and then exit... 253 */ 254 255 ippDelete(msg); 256 break; 257 } 258 259 /* 260 * Verify connection to DBUS server... 261 */ 262 263 if (con && !dbus_connection_get_is_connected(con)) 264 { 265 dbus_connection_unref(con); 266 con = NULL; 267 } 268 269 if (!con) 270 { 271 dbus_error_init(&error); 272 273 con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); 274 if (!con) 275 dbus_error_free(&error); 276 else 277 fputs("DEBUG: Connected to D-BUS\n", stderr); 278 } 279 280 if (!con) 281 continue; 282 283 if (lock_fd == -1 && 284 acquire_lock(&lock_fd, lock_filename, sizeof(lock_filename))) 285 continue; 286 287 attr = ippFindAttribute(msg, "notify-subscribed-event", 288 IPP_TAG_KEYWORD); 289 if (!attr) 290 continue; 291 292 event = ippGetString(attr, 0, NULL); 293 if (!strncmp(event, "server-", 7)) 294 { 295 const char *word2 = event + 7; /* Second word */ 296 297 if (!strcmp(word2, "restarted")) 298 signame = "ServerRestarted"; 299 else if (!strcmp(word2, "started")) 300 signame = "ServerStarted"; 301 else if (!strcmp(word2, "stopped")) 302 signame = "ServerStopped"; 303 else if (!strcmp(word2, "audit")) 304 signame = "ServerAudit"; 305 else 306 continue; 307 } 308 else if (!strncmp(event, "printer-", 8)) 309 { 310 const char *word2 = event + 8; /* Second word */ 311 312 params = PARAMS_PRINTER; 313 if (!strcmp(word2, "restarted")) 314 signame = "PrinterRestarted"; 315 else if (!strcmp(word2, "shutdown")) 316 signame = "PrinterShutdown"; 317 else if (!strcmp(word2, "stopped")) 318 signame = "PrinterStopped"; 319 else if (!strcmp(word2, "state-changed")) 320 signame = "PrinterStateChanged"; 321 else if (!strcmp(word2, "finishings-changed")) 322 signame = "PrinterFinishingsChanged"; 323 else if (!strcmp(word2, "media-changed")) 324 signame = "PrinterMediaChanged"; 325 else if (!strcmp(word2, "added")) 326 signame = "PrinterAdded"; 327 else if (!strcmp(word2, "deleted")) 328 signame = "PrinterDeleted"; 329 else if (!strcmp(word2, "modified")) 330 signame = "PrinterModified"; 331 else 332 continue; 333 } 334 else if (!strncmp(event, "job-", 4)) 335 { 336 const char *word2 = event + 4; /* Second word */ 337 338 params = PARAMS_JOB; 339 if (!strcmp(word2, "state-changed")) 340 signame = "JobState"; 341 else if (!strcmp(word2, "created")) 342 signame = "JobCreated"; 343 else if (!strcmp(word2, "completed")) 344 signame = "JobCompleted"; 345 else if (!strcmp(word2, "stopped")) 346 signame = "JobStopped"; 347 else if (!strcmp(word2, "config-changed")) 348 signame = "JobConfigChanged"; 349 else if (!strcmp(word2, "progress")) 350 signame = "JobProgress"; 351 else 352 continue; 353 } 354 else 355 continue; 356 357 /* 358 * Create and send the new message... 359 */ 360 361 fprintf(stderr, "DEBUG: %s\n", signame); 362 message = dbus_message_new_signal("/org/cups/cupsd/Notifier", 363 "org.cups.cupsd.Notifier", 364 signame); 365 366 dbus_message_append_iter_init(message, &iter); 367 attr = ippFindAttribute(msg, "notify-text", IPP_TAG_TEXT); 368 if (attr) 369 { 370 const char *val = ippGetString(attr, 0, NULL); 371 if (!dbus_message_iter_append_string(&iter, &val)) 372 goto bail; 373 } 374 else 375 goto bail; 376 377 if (params >= PARAMS_PRINTER) 378 { 379 char *p; /* Pointer into printer_reasons */ 380 size_t reasons_length; /* Required size of printer_reasons */ 381 int i; /* Looping var */ 382 int have_printer_params = 1;/* Do we have printer URI? */ 383 384 /* STRING printer-uri or "" */ 385 attr = ippFindAttribute(msg, "notify-printer-uri", IPP_TAG_URI); 386 if (attr) 387 { 388 const char *val = ippGetString(attr, 0, NULL); 389 if (!dbus_message_iter_append_string(&iter, &val)) 390 goto bail; 391 } 392 else 393 { 394 have_printer_params = 0; 395 dbus_message_iter_append_string(&iter, &nul); 396 } 397 398 /* STRING printer-name */ 399 if (have_printer_params) 400 { 401 attr = ippFindAttribute(msg, "printer-name", IPP_TAG_NAME); 402 if (attr) 403 { 404 const char *val = ippGetString(attr, 0, NULL); 405 if (!dbus_message_iter_append_string(&iter, &val)) 406 goto bail; 407 } 408 else 409 goto bail; 410 } 411 else 412 dbus_message_iter_append_string(&iter, &nul); 413 414 /* UINT32 printer-state */ 415 if (have_printer_params) 416 { 417 attr = ippFindAttribute(msg, "printer-state", IPP_TAG_ENUM); 418 if (attr) 419 { 420 dbus_uint32_t val = ippGetInteger(attr, 0); 421 dbus_message_iter_append_uint32(&iter, &val); 422 } 423 else 424 goto bail; 425 } 426 else 427 dbus_message_iter_append_uint32(&iter, &no); 428 429 /* STRING printer-state-reasons */ 430 if (have_printer_params) 431 { 432 attr = ippFindAttribute(msg, "printer-state-reasons", 433 IPP_TAG_KEYWORD); 434 if (attr) 435 { 436 int num_values = ippGetCount(attr); 437 for (reasons_length = 0, i = 0; i < num_values; i++) 438 /* All need commas except the last, which needs a nul byte. */ 439 reasons_length += 1 + strlen(ippGetString(attr, i, NULL)); 440 printer_reasons = malloc(reasons_length); 441 if (!printer_reasons) 442 goto bail; 443 p = printer_reasons; 444 for (i = 0; i < num_values; i++) 445 { 446 if (i) 447 *p++ = ','; 448 449 strlcpy(p, ippGetString(attr, i, NULL), 450 reasons_length - (p - printer_reasons)); 451 p += strlen(p); 452 } 453 if (!dbus_message_iter_append_string(&iter, &printer_reasons)) 454 goto bail; 455 } 456 else 457 goto bail; 458 } 459 else 460 dbus_message_iter_append_string(&iter, &nul); 461 462 /* BOOL printer-is-accepting-jobs */ 463 if (have_printer_params) 464 { 465 attr = ippFindAttribute(msg, "printer-is-accepting-jobs", 466 IPP_TAG_BOOLEAN); 467 if (attr) 468 { 469 dbus_bool_t val = ippGetBoolean(attr, 0); 470 dbus_message_iter_append_boolean(&iter, &val); 471 } 472 else 473 goto bail; 474 } 475 else 476 dbus_message_iter_append_boolean(&iter, &no); 477 } 478 479 if (params >= PARAMS_JOB) 480 { 481 char *p; /* Pointer into job_reasons */ 482 size_t reasons_length; /* Required size of job_reasons */ 483 int i; /* Looping var */ 484 485 /* UINT32 job-id */ 486 attr = ippFindAttribute(msg, "notify-job-id", IPP_TAG_INTEGER); 487 if (attr) 488 { 489 dbus_uint32_t val = ippGetInteger(attr, 0); 490 dbus_message_iter_append_uint32(&iter, &val); 491 } 492 else 493 goto bail; 494 495 /* UINT32 job-state */ 496 attr = ippFindAttribute(msg, "job-state", IPP_TAG_ENUM); 497 if (attr) 498 { 499 dbus_uint32_t val = ippGetInteger(attr, 0); 500 dbus_message_iter_append_uint32(&iter, &val); 501 } 502 else 503 goto bail; 504 505 /* STRING job-state-reasons */ 506 attr = ippFindAttribute(msg, "job-state-reasons", IPP_TAG_KEYWORD); 507 if (attr) 508 { 509 int num_values = ippGetCount(attr); 510 for (reasons_length = 0, i = 0; i < num_values; i++) 511 /* All need commas except the last, which needs a nul byte. */ 512 reasons_length += 1 + strlen(ippGetString(attr, i, NULL)); 513 job_reasons = malloc(reasons_length); 514 if (!job_reasons) 515 goto bail; 516 p = job_reasons; 517 for (i = 0; i < num_values; i++) 518 { 519 if (i) 520 *p++ = ','; 521 522 strlcpy(p, ippGetString(attr, i, NULL), 523 reasons_length - (p - job_reasons)); 524 p += strlen(p); 525 } 526 if (!dbus_message_iter_append_string(&iter, &job_reasons)) 527 goto bail; 528 } 529 else 530 goto bail; 531 532 /* STRING job-name or "" */ 533 attr = ippFindAttribute(msg, "job-name", IPP_TAG_NAME); 534 if (attr) 535 { 536 const char *val = ippGetString(attr, 0, NULL); 537 if (!dbus_message_iter_append_string(&iter, &val)) 538 goto bail; 539 } 540 else 541 dbus_message_iter_append_string(&iter, &nul); 542 543 /* UINT32 job-impressions-completed */ 544 attr = ippFindAttribute(msg, "job-impressions-completed", 545 IPP_TAG_INTEGER); 546 if (attr) 547 { 548 dbus_uint32_t val = ippGetInteger(attr, 0); 549 dbus_message_iter_append_uint32(&iter, &val); 550 } 551 else 552 goto bail; 553 } 554 555 dbus_connection_send(con, message, NULL); 556 dbus_connection_flush(con); 557 558 /* 559 * Cleanup... 560 */ 561 562 bail: 563 564 dbus_message_unref(message); 565 566 if (printer_reasons) 567 free(printer_reasons); 568 569 if (job_reasons) 570 free(job_reasons); 571 572 ippDelete(msg); 573 } 574 575 /* 576 * Remove lock file... 577 */ 578 579 if (lock_fd >= 0) 580 { 581 close(lock_fd); 582 release_lock(); 583 } 584 585 return (0); 586} 587 588 589/* 590 * 'release_lock()' - Release the singleton lock. 591 */ 592 593static void 594release_lock(void) 595{ 596 unlink(lock_filename); 597} 598 599 600/* 601 * 'handle_sigterm()' - Handle SIGTERM signal. 602 */ 603static void 604handle_sigterm(int signum) 605{ 606 release_lock(); 607 _exit(0); 608} 609 610/* 611 * 'acquire_lock()' - Acquire a lock so we only have a single notifier running. 612 */ 613 614static int /* O - 0 on success, -1 on failure */ 615acquire_lock(int *fd, /* O - Lock file descriptor */ 616 char *lockfile, /* I - Lock filename buffer */ 617 size_t locksize) /* I - Size of filename buffer */ 618{ 619 const char *tmpdir; /* Temporary directory */ 620 struct sigaction action; /* POSIX sigaction data */ 621 622 623 /* 624 * Figure out where to put the lock file... 625 */ 626 627 if ((tmpdir = getenv("TMPDIR")) == NULL) 628 tmpdir = "/tmp"; 629 630 snprintf(lockfile, locksize, "%s/cups-dbus-notifier-lockfile", tmpdir); 631 632 /* 633 * Create the lock file and fail if it already exists... 634 */ 635 636 if ((*fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) 637 return (-1); 638 639 /* 640 * Set a SIGTERM handler to make sure we release the lock if the 641 * scheduler decides to stop us. 642 */ 643 memset(&action, 0, sizeof(action)); 644 action.sa_handler = handle_sigterm; 645 sigaction(SIGTERM, &action, NULL); 646 647 return (0); 648} 649#else /* !HAVE_DBUS */ 650int 651main(void) 652{ 653 return (1); 654} 655#endif /* HAVE_DBUS */ 656 657 658/* 659 * End of "$Id: dbus.c 11528 2014-01-14 20:24:03Z msweet $". 660 */ 661