blob: 1ebd2eaf47e6d64a21958678ac840c8055f6f1fc [file] [log] [blame]
/*
* D-Bus notifier for CUPS.
*
* Copyright 2008-2014 by Apple Inc.
* Copyright (C) 2011, 2013 Red Hat, Inc.
* Copyright (C) 2007 Tim Waugh <twaugh@redhat.com>
* 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 */