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