]>
Commit | Line | Data |
---|---|---|
c5571a1d MS |
1 | /* |
2 | * "$Id$" | |
3 | * | |
71e16022 | 4 | * D-Bus notifier for CUPS. |
c5571a1d | 5 | * |
71e16022 | 6 | * Copyright 2008-2010 by Apple Inc. |
c5571a1d MS |
7 | * Copyright (C) 2007 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 | * Contents: | |
18 | * | |
49d87452 MS |
19 | * main() - Read events and send DBUS notifications. |
20 | * acquire_lock() - Acquire a lock so we only have a single notifier running. | |
c5571a1d MS |
21 | */ |
22 | ||
23 | /* | |
24 | * Include necessary headers... | |
25 | */ | |
26 | ||
27 | #include <cups/cups.h> | |
71e16022 | 28 | #include <cups/string-private.h> |
c5571a1d MS |
29 | #include <fcntl.h> |
30 | #include <signal.h> | |
31 | #include <sys/stat.h> | |
32 | #include <sys/types.h> | |
33 | #include <unistd.h> | |
34 | ||
35 | #include <dbus/dbus.h> | |
36 | #ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND | |
37 | # define dbus_message_append_iter_init dbus_message_iter_init_append | |
38 | # define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, v) | |
39 | # define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, v) | |
40 | # define dbus_message_iter_append_boolean(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, v) | |
41 | #endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */ | |
42 | ||
43 | ||
44 | /* | |
45 | * D-Bus object: org.cups.cupsd.Notifier | |
46 | * D-Bus object path: /org/cups/cupsd/Notifier | |
47 | * | |
48 | * D-Bus interface name: org.cups.cupsd.Notifier | |
49 | * | |
50 | * Signals: | |
51 | * | |
52 | * ServerRestarted(STRING text) | |
53 | * Server has restarted. | |
54 | * | |
55 | * ServerStarted(STRING text) | |
56 | * Server has started. | |
57 | * | |
58 | * ServerStopped(STRING text) | |
59 | * Server has stopped. | |
60 | * | |
61 | * ServerAudit(STRING text) | |
62 | * Security-related event. | |
63 | * | |
64 | * PrinterRestarted(STRING text, | |
65 | * STRING printer-uri, | |
66 | * STRING printer-name, | |
67 | * UINT32 printer-state, | |
68 | * STRING printer-state-reasons, | |
69 | * BOOLEAN printer-is-accepting-jobs) | |
70 | * Printer has restarted. | |
71 | * | |
72 | * PrinterShutdown(STRING text, | |
73 | * STRING printer-uri, | |
74 | * STRING printer-name, | |
75 | * UINT32 printer-state, | |
76 | * STRING printer-state-reasons, | |
77 | * BOOLEAN printer-is-accepting-jobs) | |
78 | * Printer has shutdown. | |
79 | * | |
80 | * PrinterStopped(STRING text, | |
81 | * STRING printer-uri, | |
82 | * STRING printer-name, | |
83 | * UINT32 printer-state, | |
84 | * STRING printer-state-reasons, | |
85 | * BOOLEAN printer-is-accepting-jobs) | |
86 | * Printer has stopped. | |
87 | * | |
88 | * PrinterStateChanged(STRING text, | |
89 | * STRING printer-uri, | |
90 | * STRING printer-name, | |
91 | * UINT32 printer-state, | |
92 | * STRING printer-state-reasons, | |
93 | * BOOLEAN printer-is-accepting-jobs) | |
94 | * Printer state has changed. | |
95 | * | |
96 | * PrinterFinishingsChanged(STRING text, | |
97 | * STRING printer-uri, | |
98 | * STRING printer-name, | |
99 | * UINT32 printer-state, | |
100 | * STRING printer-state-reasons, | |
101 | * BOOLEAN printer-is-accepting-jobs) | |
102 | * Printer's finishings-supported attribute has changed. | |
103 | * | |
104 | * PrinterMediaChanged(STRING text, | |
105 | * STRING printer-uri, | |
106 | * STRING printer-name, | |
107 | * UINT32 printer-state, | |
108 | * STRING printer-state-reasons, | |
109 | * BOOLEAN printer-is-accepting-jobs) | |
110 | * Printer's media-supported attribute has changed. | |
111 | * | |
112 | * PrinterAdded(STRING text, | |
113 | * STRING printer-uri, | |
114 | * STRING printer-name, | |
115 | * UINT32 printer-state, | |
116 | * STRING printer-state-reasons, | |
117 | * BOOLEAN printer-is-accepting-jobs) | |
118 | * Printer has been added. | |
119 | * | |
120 | * PrinterDeleted(STRING text, | |
121 | * STRING printer-uri, | |
122 | * STRING printer-name, | |
123 | * UINT32 printer-state, | |
124 | * STRING printer-state-reasons, | |
125 | * BOOLEAN printer-is-accepting-jobs) | |
126 | * Printer has been deleted. | |
127 | * | |
128 | * PrinterModified(STRING text, | |
129 | * STRING printer-uri, | |
130 | * STRING printer-name, | |
131 | * UINT32 printer-state, | |
132 | * STRING printer-state-reasons, | |
133 | * BOOLEAN printer-is-accepting-jobs) | |
134 | * Printer has been modified. | |
135 | * | |
136 | * text describes the event. | |
137 | * printer-state-reasons is a comma-separated list. | |
138 | * If printer-uri is "" in a Job* signal, the other printer-* parameters | |
139 | * must be ignored. | |
140 | * If the job name is not know, job-name will be "". | |
141 | */ | |
142 | ||
143 | /* | |
144 | * Constants... | |
145 | */ | |
146 | ||
147 | enum | |
148 | { | |
149 | PARAMS_NONE, | |
150 | PARAMS_PRINTER, | |
151 | PARAMS_JOB | |
152 | }; | |
153 | ||
154 | ||
155 | /* | |
156 | * Local functions... | |
157 | */ | |
158 | ||
49d87452 | 159 | static int acquire_lock(int *fd, char *lockfile, size_t locksize); |
c5571a1d MS |
160 | |
161 | ||
162 | /* | |
163 | * 'main()' - Read events and send DBUS notifications. | |
164 | */ | |
165 | ||
166 | int /* O - Exit status */ | |
167 | main(int argc, /* I - Number of command-line args */ | |
168 | char *argv[]) /* I - Command-line arguments */ | |
169 | { | |
170 | ipp_t *msg; /* Event message from scheduler */ | |
171 | ipp_state_t state; /* IPP event state */ | |
172 | struct sigaction action; /* POSIX sigaction data */ | |
173 | DBusConnection *con = NULL; /* Connection to DBUS server */ | |
174 | DBusError error; /* Error, if any */ | |
175 | DBusMessage *message; /* Message to send */ | |
176 | DBusMessageIter iter; /* Iterator for message data */ | |
177 | int lock_fd = -1; /* Lock file descriptor */ | |
178 | char lock_filename[1024]; | |
179 | /* Lock filename */ | |
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 | ||
49d87452 | 206 | if (strncmp(argv[1], "dbus:", 5)) |
c5571a1d MS |
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 | const char *nul = ""; /* Empty string value */ | |
224 | int no = 0; /* Boolean "no" value */ | |
225 | int params = PARAMS_NONE; | |
226 | /* What parameters to include? */ | |
227 | ||
228 | ||
229 | /* | |
230 | * Get the next event... | |
231 | */ | |
232 | ||
233 | msg = ippNew(); | |
234 | while ((state = ippReadFile(0, msg)) != IPP_DATA) | |
235 | { | |
236 | if (state <= IPP_IDLE) | |
237 | break; | |
238 | } | |
239 | ||
240 | fprintf(stderr, "DEBUG: state=%d\n", state); | |
241 | ||
242 | if (state == IPP_ERROR) | |
243 | fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr); | |
244 | ||
245 | if (state <= IPP_IDLE) | |
246 | { | |
247 | /* | |
248 | * Out of messages, free memory and then exit... | |
249 | */ | |
250 | ||
251 | ippDelete(msg); | |
252 | break; | |
253 | } | |
254 | ||
255 | /* | |
256 | * Verify connection to DBUS server... | |
257 | */ | |
258 | ||
259 | if (con && !dbus_connection_get_is_connected(con)) | |
260 | { | |
261 | dbus_connection_unref(con); | |
262 | con = NULL; | |
263 | } | |
264 | ||
265 | if (!con) | |
266 | { | |
267 | dbus_error_init(&error); | |
268 | ||
269 | con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); | |
270 | if (!con) | |
271 | dbus_error_free(&error); | |
272 | else | |
273 | fputs("DEBUG: Connected to D-BUS\n", stderr); | |
274 | } | |
275 | ||
276 | if (!con) | |
277 | continue; | |
278 | ||
279 | if (lock_fd == -1 && | |
280 | acquire_lock(&lock_fd, lock_filename, sizeof(lock_filename))) | |
281 | continue; | |
282 | ||
283 | attr = ippFindAttribute(msg, "notify-subscribed-event", | |
284 | IPP_TAG_KEYWORD); | |
285 | if (!attr) | |
286 | continue; | |
287 | ||
288 | event = attr->values[0].string.text; | |
289 | if (!strncmp(event, "server-", 7)) | |
290 | { | |
291 | const char *word2 = event + 7; /* Second word */ | |
292 | ||
293 | if (!strcmp(word2, "restarted")) | |
294 | signame = "ServerRestarted"; | |
295 | else if (!strcmp(word2, "started")) | |
296 | signame = "ServerStarted"; | |
297 | else if (!strcmp(word2, "stopped")) | |
298 | signame = "ServerStopped"; | |
299 | else if (!strcmp(word2, "audit")) | |
300 | signame = "ServerAudit"; | |
301 | else | |
302 | continue; | |
303 | } | |
304 | else if (!strncmp(event, "printer-", 8)) | |
305 | { | |
306 | const char *word2 = event + 8; /* Second word */ | |
307 | ||
308 | params = PARAMS_PRINTER; | |
309 | if (!strcmp(word2, "restarted")) | |
310 | signame = "PrinterRestarted"; | |
311 | else if (!strcmp(word2, "shutdown")) | |
312 | signame = "PrinterShutdown"; | |
313 | else if (!strcmp(word2, "stopped")) | |
314 | signame = "PrinterStopped"; | |
315 | else if (!strcmp(word2, "state-changed")) | |
316 | signame = "PrinterStateChanged"; | |
317 | else if (!strcmp(word2, "finishings-changed")) | |
318 | signame = "PrinterFinishingsChanged"; | |
319 | else if (!strcmp(word2, "media-changed")) | |
320 | signame = "PrinterMediaChanged"; | |
321 | else if (!strcmp(word2, "added")) | |
322 | signame = "PrinterAdded"; | |
323 | else if (!strcmp(word2, "deleted")) | |
324 | signame = "PrinterDeleted"; | |
325 | else if (!strcmp(word2, "modified")) | |
326 | signame = "PrinterModified"; | |
327 | else | |
328 | continue; | |
329 | } | |
330 | else if (!strncmp(event, "job-", 4)) | |
331 | { | |
332 | const char *word2 = event + 4; /* Second word */ | |
333 | ||
334 | params = PARAMS_JOB; | |
335 | if (!strcmp(word2, "state")) | |
336 | signame = "JobState"; | |
337 | else if (!strcmp(word2, "created")) | |
338 | signame = "JobCreated"; | |
339 | else if (!strcmp(word2, "completed")) | |
340 | signame = "JobCompleted"; | |
341 | else if (!strcmp(word2, "stopped")) | |
342 | signame = "JobStopped"; | |
343 | else if (!strcmp(word2, "config-changed")) | |
344 | signame = "JobConfigChanged"; | |
345 | else if (!strcmp(word2, "progress")) | |
346 | signame = "JobProgress"; | |
347 | else | |
348 | continue; | |
349 | } | |
350 | else | |
351 | continue; | |
352 | ||
353 | /* | |
354 | * Create and send the new message... | |
355 | */ | |
356 | ||
357 | fprintf(stderr, "DEBUG: %s\n", signame); | |
358 | message = dbus_message_new_signal("/org/cups/cupsd/Notifier", | |
359 | "org.cups.cupsd.Notifier", | |
360 | signame); | |
361 | ||
362 | dbus_message_append_iter_init(message, &iter); | |
363 | attr = ippFindAttribute(msg, "notify-text", IPP_TAG_TEXT); | |
364 | if (!attr) | |
365 | goto bail; | |
366 | dbus_message_iter_append_string(&iter, &(attr->values[0].string.text)); | |
367 | ||
368 | if (params >= PARAMS_PRINTER) | |
369 | { | |
370 | char *p; /* Pointer into printer_reasons */ | |
371 | size_t reasons_length; /* Required size of printer_reasons */ | |
372 | int i; /* Looping var */ | |
373 | int have_printer_params = 1;/* Do we have printer URI? */ | |
374 | ||
375 | /* STRING printer-uri or "" */ | |
376 | attr = ippFindAttribute(msg, "notify-printer-uri", IPP_TAG_URI); | |
377 | if (attr) | |
378 | dbus_message_iter_append_string(&iter, &(attr->values[0].string.text)); | |
379 | else | |
380 | { | |
381 | have_printer_params = 0; | |
382 | dbus_message_iter_append_string(&iter, &nul); | |
383 | } | |
384 | ||
385 | /* STRING printer-name */ | |
386 | if (have_printer_params) | |
387 | { | |
388 | attr = ippFindAttribute(msg, "printer-name", IPP_TAG_NAME); | |
389 | if (attr) | |
390 | dbus_message_iter_append_string(&iter, | |
391 | &(attr->values[0].string.text)); | |
392 | else | |
393 | goto bail; | |
394 | } | |
395 | else | |
396 | dbus_message_iter_append_string(&iter, &nul); | |
397 | ||
398 | /* UINT32 printer-state */ | |
399 | if (have_printer_params) | |
400 | { | |
401 | attr = ippFindAttribute(msg, "printer-state", IPP_TAG_ENUM); | |
402 | if (attr) | |
403 | dbus_message_iter_append_uint32(&iter, &(attr->values[0].integer)); | |
404 | else | |
405 | goto bail; | |
406 | } | |
407 | else | |
408 | dbus_message_iter_append_uint32(&iter, &no); | |
409 | ||
410 | /* STRING printer-state-reasons */ | |
411 | if (have_printer_params) | |
412 | { | |
413 | attr = ippFindAttribute(msg, "printer-state-reasons", | |
414 | IPP_TAG_KEYWORD); | |
415 | if (attr) | |
416 | { | |
417 | for (reasons_length = 0, i = 0; i < attr->num_values; i++) | |
418 | /* All need commas except the last, which needs a nul byte. */ | |
419 | reasons_length += 1 + strlen(attr->values[i].string.text); | |
420 | printer_reasons = malloc(reasons_length); | |
421 | if (!printer_reasons) | |
422 | goto bail; | |
423 | p = printer_reasons; | |
424 | for (i = 0; i < attr->num_values; i++) | |
425 | { | |
426 | strcpy(p, attr->values[i].string.text); | |
427 | p += strlen(p); | |
428 | if (i) | |
429 | *p++ = ','; | |
430 | } | |
431 | dbus_message_iter_append_string(&iter, &printer_reasons); | |
432 | } | |
433 | else | |
434 | goto bail; | |
435 | } | |
436 | else | |
437 | dbus_message_iter_append_string(&iter, &nul); | |
438 | ||
439 | /* BOOL printer-is-accepting-jobs */ | |
440 | if (have_printer_params) | |
441 | { | |
442 | attr = ippFindAttribute(msg, "printer-is-accepting-jobs", | |
443 | IPP_TAG_BOOLEAN); | |
444 | if (attr) | |
445 | dbus_message_iter_append_boolean(&iter, &(attr->values[0].boolean)); | |
446 | else | |
447 | goto bail; | |
448 | } | |
449 | else | |
450 | dbus_message_iter_append_boolean(&iter, &no); | |
451 | } | |
452 | ||
453 | if (params >= PARAMS_JOB) | |
454 | { | |
455 | /* UINT32 job-id */ | |
456 | attr = ippFindAttribute(msg, "notify-job-id", IPP_TAG_INTEGER); | |
457 | if (!attr) | |
458 | goto bail; | |
459 | dbus_message_iter_append_uint32(&iter, &(attr->values[0].integer)); | |
460 | ||
461 | /* UINT32 job-state */ | |
462 | attr = ippFindAttribute(msg, "job-state", IPP_TAG_ENUM); | |
463 | if (!attr) | |
464 | goto bail; | |
465 | dbus_message_iter_append_uint32(&iter, &(attr->values[0].integer)); | |
466 | ||
467 | /* STRING job-state-reasons */ | |
468 | attr = ippFindAttribute(msg, "job-state-reasons", IPP_TAG_KEYWORD); | |
469 | if (!attr) | |
470 | goto bail; | |
471 | dbus_message_iter_append_string(&iter, &(attr->values[0].string.text)); | |
472 | ||
473 | /* STRING job-name or "" */ | |
474 | attr = ippFindAttribute(msg, "job-name", IPP_TAG_NAME); | |
475 | if (attr) | |
476 | dbus_message_iter_append_string(&iter, &(attr->values[0].string.text)); | |
477 | else | |
478 | dbus_message_iter_append_string(&iter, &nul); | |
479 | ||
480 | /* UINT32 job-impressions-completed */ | |
481 | attr = ippFindAttribute(msg, "job-impressions-completed", | |
482 | IPP_TAG_INTEGER); | |
483 | if (!attr) | |
484 | goto bail; | |
485 | dbus_message_iter_append_uint32(&iter, &(attr->values[0].integer)); | |
486 | } | |
487 | ||
488 | dbus_connection_send(con, message, NULL); | |
489 | dbus_connection_flush(con); | |
490 | ||
491 | /* | |
492 | * Cleanup... | |
493 | */ | |
494 | ||
495 | bail: | |
496 | ||
497 | if (printer_reasons) | |
498 | free(printer_reasons); | |
499 | ||
500 | dbus_message_unref(message); | |
501 | ippDelete(msg); | |
502 | } | |
503 | ||
504 | /* | |
505 | * Remove lock file... | |
506 | */ | |
507 | ||
508 | if (lock_fd >= 0) | |
509 | { | |
510 | close(lock_fd); | |
511 | unlink(lock_filename); | |
512 | } | |
513 | ||
514 | return (0); | |
515 | } | |
516 | ||
517 | ||
518 | /* | |
519 | * 'acquire_lock()' - Acquire a lock so we only have a single notifier running. | |
520 | */ | |
521 | ||
522 | static int /* O - 0 on success, -1 on failure */ | |
523 | acquire_lock(int *fd, /* O - Lock file descriptor */ | |
524 | char *lockfile, /* I - Lock filename buffer */ | |
525 | size_t locksize) /* I - Size of filename buffer */ | |
526 | { | |
527 | const char *tmpdir; /* Temporary directory */ | |
528 | ||
529 | ||
530 | /* | |
531 | * Figure out where to put the lock file... | |
532 | */ | |
533 | ||
534 | if ((tmpdir = getenv("TMPDIR")) == NULL) | |
535 | tmpdir = "/tmp"; | |
536 | ||
537 | snprintf(lockfile, locksize, "%s/cups-dbus-notifier-lockfile", tmpdir); | |
538 | ||
539 | /* | |
540 | * Create the lock file and fail if it already exists... | |
541 | */ | |
542 | ||
543 | if ((*fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) | |
544 | return (-1); | |
545 | else | |
546 | return (0); | |
547 | } | |
548 | ||
549 | ||
550 | /* | |
551 | * End of "$Id$". | |
552 | */ |