xref: /aosp_15_r20/external/libcups/notifier/dbus.c (revision 5e7646d21f1134fb0638875d812ef646c12ab91e)
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 <[email protected]>
7  * Copyright 1997-2005 by Easy Software Products.
8  *
9  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
10  */
11 
12 /*
13  * Include necessary headers...
14  */
15 
16 #include <cups/cups.h>
17 #include <cups/string-private.h>
18 #include <fcntl.h>
19 #include <signal.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
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 */
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 
137 enum
138 {
139   PARAMS_NONE,
140   PARAMS_PRINTER,
141   PARAMS_JOB
142 };
143 
144 
145 /*
146  * Global variables...
147  */
148 
149 static char		lock_filename[1024];	/* Lock filename */
150 
151 
152 /*
153  * Local functions...
154  */
155 
156 static int	acquire_lock(int *fd, char *lockfile, size_t locksize);
157 static void	release_lock(void);
158 
159 
160 /*
161  * 'main()' - Read events and send DBUS notifications.
162  */
163 
164 int					/* O - Exit status */
main(int argc,char * argv[])165 main(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 */
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 
202   if (strncmp(argv[1], "dbus:", 5))
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 */
219     char		*job_reasons = NULL;
220 					/* Job reasons string */
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",
282 			    IPP_TAG_KEYWORD);
283     if (!attr)
284       continue;
285 
286     event = ippGetString(attr, 0, NULL);
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;
333       if (!strcmp(word2, "state-changed"))
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      */
354 
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);
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
369       goto bail;
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)
381       {
382         const char *val = ippGetString(attr, 0, NULL);
383         if (!dbus_message_iter_append_string(&iter, &val))
384 	  goto bail;
385       }
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);
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;
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)
413 	{
414 	  dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0);
415 	  dbus_message_iter_append_uint32(&iter, &val);
416 	}
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 	{
430 	  int num_values = ippGetCount(attr);
431 	  for (reasons_length = 0, i = 0; i < num_values; i++)
432 	    /* All need commas except the last, which needs a nul byte. */
433 	    reasons_length += 1 + strlen(ippGetString(attr, i, NULL));
434 	  printer_reasons = malloc(reasons_length);
435 	  if (!printer_reasons)
436 	    goto bail;
437 	  p = printer_reasons;
438 	  for (i = 0; i < num_values; i++)
439 	  {
440 	    if (i)
441 	      *p++ = ',';
442 
443 	    strlcpy(p, ippGetString(attr, i, NULL), reasons_length - (size_t)(p - printer_reasons));
444 	    p += strlen(p);
445 	  }
446 	  if (!dbus_message_iter_append_string(&iter, &printer_reasons))
447 	    goto bail;
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)
461 	{
462 	  dbus_bool_t val = (dbus_bool_t)ippGetBoolean(attr, 0);
463 	  dbus_message_iter_append_boolean(&iter, &val);
464 	}
465 	else
466 	  goto bail;
467       }
468       else
469 	dbus_message_iter_append_boolean(&iter, &no);
470     }
471 
472     if (params >= PARAMS_JOB)
473     {
474       char	*p;			/* Pointer into job_reasons */
475       size_t	reasons_length;		/* Required size of job_reasons */
476       int	i;			/* Looping var */
477 
478       /* UINT32 job-id */
479       attr = ippFindAttribute(msg, "notify-job-id", IPP_TAG_INTEGER);
480       if (attr)
481       {
482         dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0);
483         dbus_message_iter_append_uint32(&iter, &val);
484       }
485       else
486 	goto bail;
487 
488       /* UINT32 job-state */
489       attr = ippFindAttribute(msg, "job-state", IPP_TAG_ENUM);
490       if (attr)
491       {
492         dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0);
493         dbus_message_iter_append_uint32(&iter, &val);
494       }
495       else
496 	goto bail;
497 
498       /* STRING job-state-reasons */
499       attr = ippFindAttribute(msg, "job-state-reasons", IPP_TAG_KEYWORD);
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 
515 	  strlcpy(p, ippGetString(attr, i, NULL), reasons_length - (size_t)(p - job_reasons));
516 	  p += strlen(p);
517 	}
518 	if (!dbus_message_iter_append_string(&iter, &job_reasons))
519 	  goto bail;
520       }
521       else
522 	goto bail;
523 
524       /* STRING job-name or "" */
525       attr = ippFindAttribute(msg, "job-name", IPP_TAG_NAME);
526       if (attr)
527       {
528         const char *val = ippGetString(attr, 0, NULL);
529         if (!dbus_message_iter_append_string(&iter, &val))
530           goto bail;
531       }
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);
538       if (attr)
539       {
540         dbus_uint32_t val = (dbus_uint32_t)ippGetInteger(attr, 0);
541         dbus_message_iter_append_uint32(&iter, &val);
542       }
543       else
544 	goto bail;
545     }
546 
547     dbus_connection_send(con, message, NULL);
548     dbus_connection_flush(con);
549 
550    /*
551     * Cleanup...
552     */
553 
554     bail:
555 
556     dbus_message_unref(message);
557 
558     if (printer_reasons)
559       free(printer_reasons);
560 
561     if (job_reasons)
562       free(job_reasons);
563 
564     ippDelete(msg);
565   }
566 
567  /*
568   * Remove lock file...
569   */
570 
571   if (lock_fd >= 0)
572   {
573     close(lock_fd);
574     release_lock();
575   }
576 
577   return (0);
578 }
579 
580 
581 /*
582  * 'release_lock()' - Release the singleton lock.
583  */
584 
585 static void
release_lock(void)586 release_lock(void)
587 {
588   unlink(lock_filename);
589 }
590 
591 
592 /*
593  * 'handle_sigterm()' - Handle SIGTERM signal.
594  */
595 static void
handle_sigterm(int signum)596 handle_sigterm(int signum)
597 {
598   release_lock();
599   _exit(0);
600 }
601 
602 /*
603  * 'acquire_lock()' - Acquire a lock so we only have a single notifier running.
604  */
605 
606 static int				/* O - 0 on success, -1 on failure */
acquire_lock(int * fd,char * lockfile,size_t locksize)607 acquire_lock(int    *fd,		/* O - Lock file descriptor */
608              char   *lockfile,		/* I - Lock filename buffer */
609 	     size_t locksize)		/* I - Size of filename buffer */
610 {
611   const char		*tmpdir;	/* Temporary directory */
612   struct sigaction	action;		/* POSIX sigaction data */
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);
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);
640 }
641 #else /* !HAVE_DBUS */
642 int
main(void)643 main(void)
644 {
645   return (1);
646 }
647 #endif /* HAVE_DBUS */
648