| /* |
| * D-Bus notifier for CUPS. |
| * |
| * Copyright 2008-2014 by Apple Inc. |
| * Copyright (C) 2011, 2013 Red Hat, Inc. |
| * Copyright (C) 2007 Tim Waugh <[email protected]> |
| * Copyright 1997-2005 by Easy Software Products. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/cups.h> |
| #include <cups/string-private.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #ifdef HAVE_DBUS |
| # include <dbus/dbus.h> |
| # ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND |
| # define dbus_message_append_iter_init dbus_message_iter_init_append |
| # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, v) |
| # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, v) |
| # define dbus_message_iter_append_boolean(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, v) |
| # endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */ |
| |
| |
| /* |
| * D-Bus object: org.cups.cupsd.Notifier |
| * D-Bus object path: /org/cups/cupsd/Notifier |
| * |
| * D-Bus interface name: org.cups.cupsd.Notifier |
| * |
| * Signals: |
| * |
| * ServerRestarted(STRING text) |
| * Server has restarted. |
| * |
| * ServerStarted(STRING text) |
| * Server has started. |
| * |
| * ServerStopped(STRING text) |
| * Server has stopped. |
| * |
| * ServerAudit(STRING text) |
| * Security-related event. |
| * |
| * PrinterRestarted(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has restarted. |
| * |
| * PrinterShutdown(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has shutdown. |
| * |
| * PrinterStopped(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has stopped. |
| * |
| * PrinterStateChanged(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer state has changed. |
| * |
| * PrinterFinishingsChanged(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer's finishings-supported attribute has changed. |
| * |
| * PrinterMediaChanged(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer's media-supported attribute has changed. |
| * |
| * PrinterAdded(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has been added. |
| * |
| * PrinterDeleted(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has been deleted. |
| * |
| * PrinterModified(STRING text, |
| * STRING printer-uri, |
| * STRING printer-name, |
| * UINT32 printer-state, |
| * STRING printer-state-reasons, |
| * BOOLEAN printer-is-accepting-jobs) |
| * Printer has been modified. |
| * |
| * text describes the event. |
| * printer-state-reasons is a comma-separated list. |
| * If printer-uri is "" in a Job* signal, the other printer-* parameters |
| * must be ignored. |
| * If the job name is not know, job-name will be "". |
| */ |
| |
| /* |
| * Constants... |
| */ |
| |
| enum |
| { |
| PARAMS_NONE, |
| PARAMS_PRINTER, |
| PARAMS_JOB |
| }; |
| |
| |
| /* |
| * Global variables... |
| */ |
| |
| static char lock_filename[1024]; /* Lock filename */ |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static int acquire_lock(int *fd, char *lockfile, size_t locksize); |
| static void release_lock(void); |
| |
| |
| /* |
| * 'main()' - Read events and send DBUS notifications. |
| */ |
| |
| int /* O - Exit status */ |
| main(int argc, /* I - Number of command-line args */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| ipp_t *msg; /* Event message from scheduler */ |
| ipp_state_t state; /* IPP event state */ |
| struct sigaction action; /* POSIX sigaction data */ |
| DBusConnection *con = NULL; /* Connection to DBUS server */ |
| DBusError error; /* Error, if any */ |
| DBusMessage *message; /* Message to send */ |
| DBusMessageIter iter; /* Iterator for message data */ |
| int lock_fd = -1; /* Lock file descriptor */ |
| |
| |
| /* |
| * Don't buffer stderr... |
| */ |
| |
| setbuf(stderr, NULL); |
| |
| /* |
| * Ignore SIGPIPE signals... |
| */ |
| |
| memset(&action, 0, sizeof(action)); |
| action.sa_handler = SIG_IGN; |
| sigaction(SIGPIPE, &action, NULL); |
| |
| /* |
| * Validate command-line options... |
| */ |
| |
| if (argc != 3) |
| { |
| fputs("Usage: dbus dbus:/// notify-user-data\n", stderr); |
| return (1); |
| } |
| |
| if (strncmp(argv[1], "dbus:", 5)) |
| { |
| fprintf(stderr, "ERROR: Bad URI \"%s\"!\n", argv[1]); |
| return (1); |
| } |
| |
| /* |
| * Loop forever until we run out of events... |
| */ |
| |
| for (;;) |
| { |
| ipp_attribute_t *attr; /* Current attribute */ |
| const char *event; /* Event name */ |
| const char *signame = NULL;/* DBUS signal name */ |
| char *printer_reasons = NULL; |
| /* Printer reasons string */ |
| char *job_reasons = NULL; |
| /* Job reasons string */ |
| const char *nul = ""; /* Empty string value */ |
| int no = 0; /* Boolean "no" value */ |
| int params = PARAMS_NONE; |
| /* What parameters to include? */ |
| |
| |
| /* |
| * Get the next event... |
| */ |
| |
| msg = ippNew(); |
| while ((state = ippReadFile(0, msg)) != IPP_DATA) |
| { |
| if (state <= IPP_IDLE) |
| break; |
| } |
| |
| fprintf(stderr, "DEBUG: state=%d\n", state); |
| |
| if (state == IPP_ERROR) |
| fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr); |
| |
| if (state <= IPP_IDLE) |
| { |
| /* |
| * Out of messages, free memory and then exit... |
| */ |
| |
| ippDelete(msg); |
| break; |
| } |
| |
| /* |
| * Verify connection to DBUS server... |
| */ |
| |
| if (con && !dbus_connection_get_is_connected(con)) |
| { |
| dbus_connection_unref(con); |
| con = NULL; |
| } |
| |
| if (!con) |
| { |
| dbus_error_init(&error); |
| |
| con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); |
| if (!con) |
| dbus_error_free(&error); |
| else |
| fputs("DEBUG: Connected to D-BUS\n", stderr); |
| } |
| |
| if (!con) |
| continue; |
| |
| if (lock_fd == -1 && |
| acquire_lock(&lock_fd, lock_filename, sizeof(lock_filename))) |
| continue; |
| |
| attr = ippFindAttribute(msg, "notify-subscribed-event", |
| IPP_TAG_KEYWORD); |
| if (!attr) |
| continue; |
| |
| event = ippGetString(attr, 0, NULL); |
| if (!strncmp(event, "server-", 7)) |
| { |
| const char *word2 = event + 7; /* Second word */ |
| |
| if (!strcmp(word2, "restarted")) |
| signame = "ServerRestarted"; |
| else if (!strcmp(word2, "started")) |
| signame = "ServerStarted"; |
| else if (!strcmp(word2, "stopped")) |
| signame = "ServerStopped"; |
| else if (!strcmp(word2, "audit")) |
| signame = "ServerAudit"; |
| else |
| continue; |
| } |
| else if (!strncmp(event, "printer-", 8)) |
| { |
| const char *word2 = event + 8; /* Second word */ |
| |
| params = PARAMS_PRINTER; |
| if (!strcmp(word2, "restarted")) |
| signame = "PrinterRestarted"; |
| else if (!strcmp(word2, "shutdown")) |
| signame = "PrinterShutdown"; |
| else if (!strcmp(word2, "stopped")) |
| signame = "PrinterStopped"; |
| else if (!strcmp(word2, "state-changed")) |
| signame = "PrinterStateChanged"; |
| else if (!strcmp(word2, "finishings-changed")) |
| signame = "PrinterFinishingsChanged"; |
| else if (!strcmp(word2, "media-changed")) |
| signame = "PrinterMediaChanged"; |
| else if (!strcmp(word2, "added")) |
| signame = "PrinterAdded"; |
| else if (!strcmp(word2, "deleted")) |
| signame = "PrinterDeleted"; |
| else if (!strcmp(word2, "modified")) |
| signame = "PrinterModified"; |
| else |
| continue; |
| } |
| else if (!strncmp(event, "job-", 4)) |
| { |
| const char *word2 = event + 4; /* Second word */ |
| |
| params = PARAMS_JOB; |
| if (!strcmp(word2, "state-changed")) |
| signame = "JobState"; |
| else if (!strcmp(word2, "created")) |
| signame = "JobCreated"; |
| else if (!strcmp(word2, "completed")) |
| signame = "JobCompleted"; |
| else if (!strcmp(word2, "stopped")) |
| signame = "JobStopped"; |
| else if (!strcmp(word2, "config-changed")) |
| signame = "JobConfigChanged"; |
| else if (!strcmp(word2, "progress")) |
| signame = "JobProgress"; |
| else |
| continue; |
| } |
| else |
| continue; |
| |
| /* |
| * Create and send the new message... |
| */ |
| |
| fprintf(stderr, "DEBUG: %s\n", signame); |
| message = dbus_message_new_signal("/org/cups/cupsd/Notifier", |
| "org.cups.cupsd.Notifier", |
| signame); |
| |
| dbus_message_append_iter_init(message, &iter); |
| attr = ippFindAttribute(msg, "notify-text", IPP_TAG_TEXT); |
| if (attr) |
| { |
| const char *val = ippGetString(attr, 0, NULL); |
| if (!dbus_message_iter_append_string(&iter, &val)) |
| goto bail; |
| } |
| else |
| goto bail; |
| |
| if (params >= PARAMS_PRINTER) |
| { |
| char *p; /* Pointer into printer_reasons */ |
| size_t reasons_length; /* Required size of printer_reasons */ |
| int i; /* Looping var */ |
| int have_printer_params = 1;/* Do we have printer URI? */ |
| |
| /* STRING printer-uri or "" */ |
| attr = ippFindAttribute(msg, "notify-printer-uri", IPP_TAG_URI); |
| if (attr) |
| { |
| const char *val = ippGetString(attr, 0, NULL); |
| if (!dbus_message_iter_append_string(&iter, &val)) |
| goto bail; |
| } |
| else |
| { |
| have_printer_params = 0; |
| dbus_message_iter_append_string(&iter, &nul); |
| } |
| |
| /* STRING printer-name */ |
| if (have_printer_params) |
| { |
| attr = ippFindAttribute(msg, "printer-name", IPP_TAG_NAME); |
| if (attr) |
| { |
| const char *val = ippGetString(attr, 0, NULL); |
| if (!dbus_message_iter_append_string(&iter, &val)) |
| goto bail; |
| } |
| else |
| goto bail; |
| } |
| else |
| dbus_message_iter_append_string(&iter, &nul); |
| |
| /* UINT32 printer-state */ |
| if (have_printer_params) |
| { |
| attr = ippFindAttribute(msg, "printer-state", IPP_TAG_ENUM); |
| if (attr) |
| { |
| dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); |
| dbus_message_iter_append_uint32(&iter, &val); |
| } |
| else |
| goto bail; |
| } |
| else |
| dbus_message_iter_append_uint32(&iter, &no); |
| |
| /* STRING printer-state-reasons */ |
| if (have_printer_params) |
| { |
| attr = ippFindAttribute(msg, "printer-state-reasons", |
| IPP_TAG_KEYWORD); |
| if (attr) |
| { |
| int num_values = ippGetCount(attr); |
| for (reasons_length = 0, i = 0; i < num_values; i++) |
| /* All need commas except the last, which needs a nul byte. */ |
| reasons_length += 1 + strlen(ippGetString(attr, i, NULL)); |
| printer_reasons = malloc(reasons_length); |
| if (!printer_reasons) |
| goto bail; |
| p = printer_reasons; |
| for (i = 0; i < num_values; i++) |
| { |
| if (i) |
| *p++ = ','; |
| |
| strlcpy(p, ippGetString(attr, i, NULL), reasons_length - (size_t)(p - printer_reasons)); |
| p += strlen(p); |
| } |
| if (!dbus_message_iter_append_string(&iter, &printer_reasons)) |
| goto bail; |
| } |
| else |
| goto bail; |
| } |
| else |
| dbus_message_iter_append_string(&iter, &nul); |
| |
| /* BOOL printer-is-accepting-jobs */ |
| if (have_printer_params) |
| { |
| attr = ippFindAttribute(msg, "printer-is-accepting-jobs", |
| IPP_TAG_BOOLEAN); |
| if (attr) |
| { |
| dbus_bool_t val = (dbus_bool_t)ippGetBoolean(attr, 0); |
| dbus_message_iter_append_boolean(&iter, &val); |
| } |
| else |
| goto bail; |
| } |
| else |
| dbus_message_iter_append_boolean(&iter, &no); |
| } |
| |
| if (params >= PARAMS_JOB) |
| { |
| char *p; /* Pointer into job_reasons */ |
| size_t reasons_length; /* Required size of job_reasons */ |
| int i; /* Looping var */ |
| |
| /* UINT32 job-id */ |
| attr = ippFindAttribute(msg, "notify-job-id", IPP_TAG_INTEGER); |
| if (attr) |
| { |
| dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); |
| dbus_message_iter_append_uint32(&iter, &val); |
| } |
| else |
| goto bail; |
| |
| /* UINT32 job-state */ |
| attr = ippFindAttribute(msg, "job-state", IPP_TAG_ENUM); |
| if (attr) |
| { |
| dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); |
| dbus_message_iter_append_uint32(&iter, &val); |
| } |
| else |
| goto bail; |
| |
| /* STRING job-state-reasons */ |
| attr = ippFindAttribute(msg, "job-state-reasons", IPP_TAG_KEYWORD); |
| if (attr) |
| { |
| int num_values = ippGetCount(attr); |
| for (reasons_length = 0, i = 0; i < num_values; i++) |
| /* All need commas except the last, which needs a nul byte. */ |
| reasons_length += 1 + strlen(ippGetString(attr, i, NULL)); |
| job_reasons = malloc(reasons_length); |
| if (!job_reasons) |
| goto bail; |
| p = job_reasons; |
| for (i = 0; i < num_values; i++) |
| { |
| if (i) |
| *p++ = ','; |
| |
| strlcpy(p, ippGetString(attr, i, NULL), reasons_length - (size_t)(p - job_reasons)); |
| p += strlen(p); |
| } |
| if (!dbus_message_iter_append_string(&iter, &job_reasons)) |
| goto bail; |
| } |
| else |
| goto bail; |
| |
| /* STRING job-name or "" */ |
| attr = ippFindAttribute(msg, "job-name", IPP_TAG_NAME); |
| if (attr) |
| { |
| const char *val = ippGetString(attr, 0, NULL); |
| if (!dbus_message_iter_append_string(&iter, &val)) |
| goto bail; |
| } |
| else |
| dbus_message_iter_append_string(&iter, &nul); |
| |
| /* UINT32 job-impressions-completed */ |
| attr = ippFindAttribute(msg, "job-impressions-completed", |
| IPP_TAG_INTEGER); |
| if (attr) |
| { |
| dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0); |
| dbus_message_iter_append_uint32(&iter, &val); |
| } |
| else |
| goto bail; |
| } |
| |
| dbus_connection_send(con, message, NULL); |
| dbus_connection_flush(con); |
| |
| /* |
| * Cleanup... |
| */ |
| |
| bail: |
| |
| dbus_message_unref(message); |
| |
| if (printer_reasons) |
| free(printer_reasons); |
| |
| if (job_reasons) |
| free(job_reasons); |
| |
| ippDelete(msg); |
| } |
| |
| /* |
| * Remove lock file... |
| */ |
| |
| if (lock_fd >= 0) |
| { |
| close(lock_fd); |
| release_lock(); |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'release_lock()' - Release the singleton lock. |
| */ |
| |
| static void |
| release_lock(void) |
| { |
| unlink(lock_filename); |
| } |
| |
| |
| /* |
| * 'handle_sigterm()' - Handle SIGTERM signal. |
| */ |
| static void |
| handle_sigterm(int signum) |
| { |
| release_lock(); |
| _exit(0); |
| } |
| |
| /* |
| * 'acquire_lock()' - Acquire a lock so we only have a single notifier running. |
| */ |
| |
| static int /* O - 0 on success, -1 on failure */ |
| acquire_lock(int *fd, /* O - Lock file descriptor */ |
| char *lockfile, /* I - Lock filename buffer */ |
| size_t locksize) /* I - Size of filename buffer */ |
| { |
| const char *tmpdir; /* Temporary directory */ |
| struct sigaction action; /* POSIX sigaction data */ |
| |
| |
| /* |
| * Figure out where to put the lock file... |
| */ |
| |
| if ((tmpdir = getenv("TMPDIR")) == NULL) |
| tmpdir = "/tmp"; |
| |
| snprintf(lockfile, locksize, "%s/cups-dbus-notifier-lockfile", tmpdir); |
| |
| /* |
| * Create the lock file and fail if it already exists... |
| */ |
| |
| if ((*fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) |
| return (-1); |
| |
| /* |
| * Set a SIGTERM handler to make sure we release the lock if the |
| * scheduler decides to stop us. |
| */ |
| memset(&action, 0, sizeof(action)); |
| action.sa_handler = handle_sigterm; |
| sigaction(SIGTERM, &action, NULL); |
| |
| return (0); |
| } |
| #else /* !HAVE_DBUS */ |
| int |
| main(void) |
| { |
| return (1); |
| } |
| #endif /* HAVE_DBUS */ |