1/* 2 * "$Id: dbus.c 11645 2014-02-27 16:35:53Z 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 = (dbus_uint32_t)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), reasons_length - (size_t)(p - printer_reasons)); 450 p += strlen(p); 451 } 452 if (!dbus_message_iter_append_string(&iter, &printer_reasons)) 453 goto bail; 454 } 455 else 456 goto bail; 457 } 458 else 459 dbus_message_iter_append_string(&iter, &nul); 460 461 /* BOOL printer-is-accepting-jobs */ 462 if (have_printer_params) 463 { 464 attr = ippFindAttribute(msg, "printer-is-accepting-jobs", 465 IPP_TAG_BOOLEAN); 466 if (attr) 467 { 468 dbus_bool_t val = (dbus_bool_t)ippGetBoolean(attr, 0); 469 dbus_message_iter_append_boolean(&iter, &val); 470 } 471 else 472 goto bail; 473 } 474 else 475 dbus_message_iter_append_boolean(&iter, &no); 476 } 477 478 if (params >= PARAMS_JOB) 479 { 480 char *p; /* Pointer into job_reasons */ 481 size_t reasons_length; /* Required size of job_reasons */ 482 int i; /* Looping var */ 483 484 /* UINT32 job-id */ 485 attr = ippFindAttribute(msg, "notify-job-id", IPP_TAG_INTEGER); 486 if (attr) 487 { 488 dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); 489 dbus_message_iter_append_uint32(&iter, &val); 490 } 491 else 492 goto bail; 493 494 /* UINT32 job-state */ 495 attr = ippFindAttribute(msg, "job-state", IPP_TAG_ENUM); 496 if (attr) 497 { 498 dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); 499 dbus_message_iter_append_uint32(&iter, &val); 500 } 501 else 502 goto bail; 503 504 /* STRING job-state-reasons */ 505 attr = ippFindAttribute(msg, "job-state-reasons", IPP_TAG_KEYWORD); 506 if (attr) 507 { 508 int num_values = ippGetCount(attr); 509 for (reasons_length = 0, i = 0; i < num_values; i++) 510 /* All need commas except the last, which needs a nul byte. */ 511 reasons_length += 1 + strlen(ippGetString(attr, i, NULL)); 512 job_reasons = malloc(reasons_length); 513 if (!job_reasons) 514 goto bail; 515 p = job_reasons; 516 for (i = 0; i < num_values; i++) 517 { 518 if (i) 519 *p++ = ','; 520 521 strlcpy(p, ippGetString(attr, i, NULL), reasons_length - (size_t)(p - job_reasons)); 522 p += strlen(p); 523 } 524 if (!dbus_message_iter_append_string(&iter, &job_reasons)) 525 goto bail; 526 } 527 else 528 goto bail; 529 530 /* STRING job-name or "" */ 531 attr = ippFindAttribute(msg, "job-name", IPP_TAG_NAME); 532 if (attr) 533 { 534 const char *val = ippGetString(attr, 0, NULL); 535 if (!dbus_message_iter_append_string(&iter, &val)) 536 goto bail; 537 } 538 else 539 dbus_message_iter_append_string(&iter, &nul); 540 541 /* UINT32 job-impressions-completed */ 542 attr = ippFindAttribute(msg, "job-impressions-completed", 543 IPP_TAG_INTEGER); 544 if (attr) 545 { 546 dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); 547 dbus_message_iter_append_uint32(&iter, &val); 548 } 549 else 550 goto bail; 551 } 552 553 dbus_connection_send(con, message, NULL); 554 dbus_connection_flush(con); 555 556 /* 557 * Cleanup... 558 */ 559 560 bail: 561 562 dbus_message_unref(message); 563 564 if (printer_reasons) 565 free(printer_reasons); 566 567 if (job_reasons) 568 free(job_reasons); 569 570 ippDelete(msg); 571 } 572 573 /* 574 * Remove lock file... 575 */ 576 577 if (lock_fd >= 0) 578 { 579 close(lock_fd); 580 release_lock(); 581 } 582 583 return (0); 584} 585 586 587/* 588 * 'release_lock()' - Release the singleton lock. 589 */ 590 591static void 592release_lock(void) 593{ 594 unlink(lock_filename); 595} 596 597 598/* 599 * 'handle_sigterm()' - Handle SIGTERM signal. 600 */ 601static void 602handle_sigterm(int signum) 603{ 604 release_lock(); 605 _exit(0); 606} 607 608/* 609 * 'acquire_lock()' - Acquire a lock so we only have a single notifier running. 610 */ 611 612static int /* O - 0 on success, -1 on failure */ 613acquire_lock(int *fd, /* O - Lock file descriptor */ 614 char *lockfile, /* I - Lock filename buffer */ 615 size_t locksize) /* I - Size of filename buffer */ 616{ 617 const char *tmpdir; /* Temporary directory */ 618 struct sigaction action; /* POSIX sigaction data */ 619 620 621 /* 622 * Figure out where to put the lock file... 623 */ 624 625 if ((tmpdir = getenv("TMPDIR")) == NULL) 626 tmpdir = "/tmp"; 627 628 snprintf(lockfile, locksize, "%s/cups-dbus-notifier-lockfile", tmpdir); 629 630 /* 631 * Create the lock file and fail if it already exists... 632 */ 633 634 if ((*fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) 635 return (-1); 636 637 /* 638 * Set a SIGTERM handler to make sure we release the lock if the 639 * scheduler decides to stop us. 640 */ 641 memset(&action, 0, sizeof(action)); 642 action.sa_handler = handle_sigterm; 643 sigaction(SIGTERM, &action, NULL); 644 645 return (0); 646} 647#else /* !HAVE_DBUS */ 648int 649main(void) 650{ 651 return (1); 652} 653#endif /* HAVE_DBUS */ 654 655 656/* 657 * End of "$Id: dbus.c 11645 2014-02-27 16:35:53Z msweet $". 658 */ 659