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