root/jack2/branches/control/linux/dbus/jackdbus.c

Revision 2059, 20.8 kB (checked in by nedko, 2 years ago)

Expose jack_info() and friends in control.h and remove usage of JackError?.h internal header from jackdbus code

Line 
1 /* -*- Mode: C ; c-basic-offset: 4 -*- */
2 /*
3     Copyright (C) 2007,2008 Nedko Arnaudov
4     Copyright (C) 2007-2008 Juuso Alasuutari
5    
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27 #include <signal.h>
28 #include <dbus/dbus.h>
29 #include <pthread.h>
30 #include <unistd.h>
31
32 #include "jackdbus.h"
33 #include "controller.h"
34 #include "common/JackConstants.h"
35 #include "jack/jack.h"
36 #include "jack/jslist.h"
37 #include "jack/control.h"
38
39 FILE *g_logfile;
40 char *g_jackdbus_dir;
41 size_t g_jackdbus_dir_len; /* without terminating '\0' char */
42 int g_exit_command;
43 DBusConnection *g_connection;
44
45 void
46 jack_dbus_send_signal(
47     const char *sender_object_path,
48     const char *iface,
49     const char *signal_name,
50     int first_arg_type,
51     ...)
52 {
53     DBusMessage *message_ptr;
54     va_list ap;
55
56     va_start(ap, first_arg_type);
57
58     message_ptr = dbus_message_new_signal(sender_object_path, iface, signal_name);
59     if (message_ptr == NULL)
60     {
61         jack_error("dbus_message_new_signal() failed.");
62         goto exit;
63     }
64
65     if (!dbus_message_append_args_valist(message_ptr, first_arg_type, ap))
66     {
67         jack_error("dbus_message_append_args_valist() failed.");
68         goto unref;
69     }
70
71     /* Add message to outgoing message queue */
72     if (!dbus_connection_send(g_connection, message_ptr, NULL))
73     {
74         jack_error("dbus_connection_send() failed.");
75         goto unref;
76     }
77
78 unref:
79     dbus_message_unref(message_ptr);
80
81 exit:
82     va_end(ap);
83 }
84
85 /*
86  * Send a method return.
87  *
88  * If call->reply is NULL (i.e. a message construct method failed
89  * due to lack of memory) attempt to send a void method return.
90  */
91 static
92 void
93 jack_dbus_send_method_return(
94     struct jack_dbus_method_call * call)
95 {
96     if (call->reply)
97     {
98     retry_send:
99         if (!dbus_connection_send (call->connection, call->reply, NULL))
100         {
101             jack_error ("Ran out of memory trying to queue method return");
102         }
103
104         dbus_connection_flush (call->connection);
105         dbus_message_unref (call->reply);
106         call->reply = NULL;
107     }
108     else
109     {
110         jack_error ("send_method_return() called with a NULL message,"
111                     " trying to construct a void return...");
112
113         if ((call->reply = dbus_message_new_method_return (call->message)))
114         {
115             goto retry_send;
116         }
117         else
118         {
119             jack_error ("Failed to construct method return!");
120         }
121     }
122 }
123
124 #define object_ptr ((struct jack_dbus_object_descriptor *)data)
125
126 /*
127  * The D-Bus message handler for object path /org/jackaudio/Controller.
128  */
129 DBusHandlerResult
130 jack_dbus_message_handler(
131     DBusConnection *connection,
132     DBusMessage *message,
133     void *data)
134 {
135     struct jack_dbus_method_call call;
136     const char *interface_name;
137     struct jack_dbus_interface_descriptor ** interface_ptr_ptr;
138
139     /* Check if the message is a method call. If not, ignore it. */
140     if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
141     {
142         goto handled;
143     }
144
145     /* Get the invoked method's name and make sure it's non-NULL. */
146     if (!(call.method_name = dbus_message_get_member (message)))
147     {
148         jack_error ("dbus_message_get_member() returned NULL");
149         goto handled;
150     }
151
152     /* Initialize our data. */
153     call.context = object_ptr->context;
154     call.connection = connection;
155     call.message = message;
156     call.reply = NULL;
157
158     /* Check if there's an interface specified for this method call. */
159     interface_name = dbus_message_get_interface (message);
160     if (interface_name != NULL)
161     {
162         /* Check if we can match the interface and method.
163          * The inteface handler functions only return false if the
164          * method name was unknown, otherwise they run the specified
165          * method and return TRUE.
166          */
167
168         interface_ptr_ptr = object_ptr->interfaces;
169
170         while (*interface_ptr_ptr != NULL)
171         {
172             if (strcmp(interface_name, (*interface_ptr_ptr)->name) == 0)
173             {
174                 if (!(*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
175                 {
176                     break;
177                 }
178
179                 goto send_return;
180             }
181
182             interface_ptr_ptr++;
183         }
184     }
185     else
186     {
187         /* No interface was specified so we have to try them all. This is
188          * dictated by the D-Bus specification which states that method calls
189          * omitting the interface must never be rejected.
190          */
191
192         interface_ptr_ptr = object_ptr->interfaces;
193
194         while (*interface_ptr_ptr != NULL)
195         {
196             if ((*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
197             {
198                 goto send_return;
199             }
200
201             interface_ptr_ptr++;
202         }
203     }
204
205     jack_dbus_error(
206         &call,
207         JACK_DBUS_ERROR_UNKNOWN_METHOD,
208         "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist",
209         call.method_name,
210         dbus_message_get_signature(message),
211         interface_name);
212
213 send_return:
214     jack_dbus_send_method_return(&call);
215
216 handled:
217     return DBUS_HANDLER_RESULT_HANDLED;
218 }
219
220 void
221 jack_dbus_message_handler_unregister(
222     DBusConnection *connection,
223     void *data)
224 {
225     jack_info ("Message handler was unregistered");
226 }
227
228 #undef object_ptr
229
230 /*
231  * Check if the supplied method name exists in org.jackaudio.JackConfigure,
232  * if it does execute it and return TRUE. Otherwise return FALSE.
233  */
234 bool
235 jack_dbus_run_method(
236     struct jack_dbus_method_call *call,
237     const struct jack_dbus_interface_method_descriptor * methods)
238 {
239     const struct jack_dbus_interface_method_descriptor * method_ptr;
240
241     method_ptr = methods;
242
243     while (method_ptr->name != NULL)
244     {
245         if (strcmp(call->method_name, method_ptr->name) == 0)
246         {
247             method_ptr->handler(call);
248             return TRUE;
249         }
250
251         method_ptr++;
252     }
253
254     return FALSE;
255 }
256
257 /*
258  * Read arguments from a method call.
259  * If the operation fails construct an error and return false,
260  * otherwise return true.
261  */
262 bool
263 jack_dbus_get_method_args(
264     struct jack_dbus_method_call *call,
265     int type,
266     ...)
267 {
268     va_list args;
269     DBusError error;
270     bool retval = true;
271
272     va_start (args, type);
273     dbus_error_init (&error);
274
275     if (!dbus_message_get_args_valist (call->message, &error, type, args))
276     {
277         jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
278                          "Invalid arguments to method \"%s\"",
279                          call->method_name);
280         retval = false;
281     }
282
283     dbus_error_free (&error);
284     va_end (args);
285
286     return retval;
287 }
288
289 /*
290  * Read a string and a variant argument from a method call.
291  * If the operation fails construct an error and return false,
292  * otherwise return true.
293  */
294 bool
295 jack_dbus_get_method_args_string_and_variant(
296     struct jack_dbus_method_call *call,
297     const char **arg1,
298     message_arg_t *arg2,
299     int *type_ptr)
300 {
301     DBusMessageIter iter, sub_iter;
302
303     /* First we want a string... */
304     if (dbus_message_iter_init (call->message, &iter)
305         && dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING)
306     {
307         dbus_message_iter_get_basic (&iter, arg1);
308         dbus_message_iter_next (&iter);
309
310         /* ...and then a variant. */
311         if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT)
312         {
313             dbus_message_iter_recurse (&iter, &sub_iter);
314             dbus_message_iter_get_basic (&sub_iter, arg2);
315             *type_ptr = dbus_message_iter_get_arg_type (&sub_iter);
316
317             /* Got what we wanted. */
318             return true;
319         }
320     }
321
322     jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
323                      "Invalid arguments to method \"%s\"",
324                      call->method_name);
325
326     return false;
327 }
328
329 /*
330  * Append a variant type to a D-Bus message.
331  * Return false if something fails, true otherwise.
332  */
333 bool
334 jack_dbus_message_append_variant(
335     DBusMessageIter *iter,
336     int type,
337     const char *signature,
338     message_arg_t *arg)
339 {
340     DBusMessageIter sub_iter;
341
342     /* Open a variant container. */
343     if (!dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, signature, &sub_iter))
344     {
345         goto fail;
346     }
347
348     /* Append the supplied value. */
349     if (!dbus_message_iter_append_basic (&sub_iter, type, (const void *) arg))
350     {
351         dbus_message_iter_close_container (iter, &sub_iter);
352         goto fail;
353     }
354
355     /* Close the container. */
356     if (!dbus_message_iter_close_container (iter, &sub_iter))
357     {
358         goto fail;
359     }
360
361     return true;
362
363 fail:
364     return false;
365 }
366
367 /*
368  * Construct an empty method return message.
369  *
370  * The operation can only fail due to lack of memory, in which case
371  * there's no sense in trying to construct an error return. Instead,
372  * call->reply will be set to NULL and handled in send_method_return().
373  */
374 void
375 jack_dbus_construct_method_return_empty(
376     struct jack_dbus_method_call * call)
377 {
378     call->reply = dbus_message_new_method_return (call->message);
379
380     if (call->reply == NULL)
381     {
382         jack_error ("Ran out of memory trying to construct method return");
383     }
384 }
385
386 /*
387  * Construct a method return which holds a single argument or, if
388  * the type parameter is DBUS_TYPE_INVALID, no arguments at all
389  * (a void message).
390  *
391  * The operation can only fail due to lack of memory, in which case
392  * there's no sense in trying to construct an error return. Instead,
393  * call->reply will be set to NULL and handled in send_method_return().
394  */
395 void
396 jack_dbus_construct_method_return_single(
397     struct jack_dbus_method_call *call,
398     int type,
399     message_arg_t arg)
400 {
401     DBusMessageIter iter;
402     call->reply = dbus_message_new_method_return (call->message);
403
404     if (call->reply == NULL)
405     {
406         goto fail_no_mem;
407     }
408
409     /* Void method return requested by caller. */
410     if (type == DBUS_TYPE_INVALID)
411     {
412         return;
413     }
414
415     /* Prevent crash on NULL input string. */
416     else if (type == DBUS_TYPE_STRING && arg.string == NULL)
417     {
418         arg.string = "";
419     }
420
421     dbus_message_iter_init_append (call->reply, &iter);
422
423     if (!dbus_message_iter_append_basic (&iter, type, (const void *) &arg))
424     {
425         dbus_message_unref (call->reply);
426         call->reply = NULL;
427         goto fail_no_mem;
428     }
429
430     return;
431
432 fail_no_mem:
433     jack_error ("Ran out of memory trying to construct method return");
434 }
435
436 /*
437  * Construct a method return which holds an array of strings.
438  *
439  * The operation can only fail due to lack of memory, in which case
440  * there's no sense in trying to construct an error return. Instead,
441  * call->reply will be set to NULL and handled in send_method_return().
442  */
443 void
444 jack_dbus_construct_method_return_array_of_strings(
445     struct jack_dbus_method_call *call,
446     unsigned int num_members,
447     const char **array)
448 {
449     DBusMessageIter iter, sub_iter;
450     unsigned int i;
451
452     call->reply = dbus_message_new_method_return (call->message);
453     if (!call->reply)
454     {
455         goto fail;
456     }
457
458     dbus_message_iter_init_append (call->reply, &iter);
459
460     if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub_iter))
461     {
462         goto fail_unref;
463     }
464
465     for (i = 0; i < num_members; ++i)
466     {
467         if (!dbus_message_iter_append_basic (&sub_iter, DBUS_TYPE_STRING, (const void *) &array[i]))
468         {
469             dbus_message_iter_close_container (&iter, &sub_iter);
470             goto fail_unref;
471         }
472     }
473
474     if (!dbus_message_iter_close_container (&iter, &sub_iter))
475     {
476         goto fail_unref;
477     }
478
479     return;
480
481 fail_unref:
482     dbus_message_unref (call->reply);
483     call->reply = NULL;
484
485 fail:
486     jack_error ("Ran out of memory trying to construct method return");
487 }
488
489 void
490 jack_dbus_info_callback(const char *msg)
491 {
492     time_t timestamp;
493     char timestamp_str[26];
494
495     time(&timestamp);
496     ctime_r(&timestamp, timestamp_str);
497     timestamp_str[24] = 0;
498
499     fprintf(g_logfile, "%s: %s\n", timestamp_str, msg);
500     fflush(g_logfile);
501 }
502
503 void
504 jack_dbus_error_callback(const char *msg)
505 {
506     time_t timestamp;
507     char timestamp_str[26];
508
509     time(&timestamp);
510     ctime_r(&timestamp, timestamp_str);
511     timestamp_str[24] = 0;
512
513     fprintf(g_logfile, "%s: ERROR: %s\n", timestamp_str, msg);
514     fflush(g_logfile);
515 }
516
517 int
518 paths_init()
519 {
520     const char *home_dir;
521     struct stat st;
522     size_t home_dir_len;
523     size_t jackdbus_dir_len;
524    
525     home_dir = getenv("HOME");
526     if (home_dir == NULL)
527     {
528         fprintf(stderr, "Environment variable HOME not set\n");
529         goto fail;
530     }
531
532     home_dir_len = strlen(home_dir);
533     jackdbus_dir_len = strlen(JACKDBUS_DIR);
534
535     g_jackdbus_dir = malloc(home_dir_len + jackdbus_dir_len + 1);
536     if (g_jackdbus_dir == NULL)
537     {
538         fprintf(stderr, "Out of memory\n");
539         goto fail;
540     }
541
542     memcpy(g_jackdbus_dir, home_dir, home_dir_len);
543     memcpy(g_jackdbus_dir + home_dir_len, JACKDBUS_DIR, jackdbus_dir_len);
544     g_jackdbus_dir_len = home_dir_len + jackdbus_dir_len;
545     g_jackdbus_dir[g_jackdbus_dir_len] = 0;
546
547     if (stat(g_jackdbus_dir, &st) != 0)
548     {
549         if (errno == ENOENT)
550         {
551             printf("Directory \"%s\" does not exist. Creating...\n", g_jackdbus_dir);
552             if (mkdir(g_jackdbus_dir, 0777) != 0)
553             {
554                 fprintf(stderr, "Failed to create \"%s\" directory: %d (%s)\n", g_jackdbus_dir, errno, strerror(errno));
555                 goto fail_free;
556             }
557         }
558         else
559         {
560             fprintf(stderr, "Failed to stat \"%s\": %d (%s)\n", g_jackdbus_dir, errno, strerror(errno));
561             goto fail_free;
562         }
563     }
564     else
565     {
566         if (!S_ISDIR(st.st_mode))
567         {
568             fprintf(stderr, "\"%s\" exists but is not directory.\n", g_jackdbus_dir);
569             goto fail_free;
570         }
571     }
572
573     return TRUE;
574
575 fail_free:
576     free(g_jackdbus_dir);
577
578 fail:
579     return FALSE;
580 }
581
582 void
583 paths_uninit()
584 {
585     free(g_jackdbus_dir);
586 }
587
588 int
589 log_init()
590 {
591     char *log_filename;
592     size_t log_len;
593
594     log_len = strlen(JACKDBUS_LOG);
595
596     log_filename = malloc(g_jackdbus_dir_len + log_len + 1);
597     if (log_filename == NULL)
598     {
599         fprintf(stderr, "Out of memory\n");
600         return FALSE;
601     }
602
603     memcpy(log_filename, g_jackdbus_dir, g_jackdbus_dir_len);
604     memcpy(log_filename + g_jackdbus_dir_len, JACKDBUS_LOG, log_len);
605     log_filename[g_jackdbus_dir_len + log_len] = 0;
606
607     g_logfile = fopen(log_filename, "a");
608     if (g_logfile == NULL)
609     {
610         fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", log_filename, errno, strerror(errno));
611         free(log_filename);
612         return FALSE;
613     }
614
615     free(log_filename);
616
617     return TRUE;
618 }
619
620 void
621 log_uninit()
622 {
623     fclose(g_logfile);
624 }
625
626 void
627 jack_dbus_error(
628     void *dbus_call_context_ptr,
629     const char *error_name,
630     const char *format,
631     ...)
632 {
633     va_list ap;
634     char buffer[300];
635
636     va_start(ap, format);
637
638     vsnprintf(buffer, sizeof(buffer), format, ap);
639
640     jack_error_callback(buffer);
641     if (dbus_call_context_ptr != NULL)
642     {
643         ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error(
644             ((struct jack_dbus_method_call *)dbus_call_context_ptr)->message,
645             error_name,
646             buffer);
647     }
648
649     va_end(ap);
650 }
651
652 static void
653 do_nothing_handler (int sig)
654 {
655     /* this is used by the child (active) process, but it never
656        gets called unless we are already shutting down after
657        another signal.
658     */
659     char buf[64];
660     snprintf (buf, sizeof(buf),
661           "received signal %d during shutdown (ignored)\n", sig);
662     write (1, buf, strlen (buf));
663 }
664
665 void
666 do_signal_magic()
667 {
668     sigset_t signals;
669     sigset_t allsignals;
670     struct sigaction action;
671     int i;
672
673     /* ensure that we are in our own process group so that
674        kill (SIG, -pgrp) does the right thing.
675     */
676
677     setsid();
678
679     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
680
681     /* what's this for?
682
683        POSIX says that signals are delivered like this:
684
685        * if a thread has blocked that signal, it is not
686            a candidate to receive the signal.
687            * of all threads not blocking the signal, pick
688            one at random, and deliver the signal.
689
690            this means that a simple-minded multi-threaded program can
691            expect to get POSIX signals delivered randomly to any one
692            of its threads,
693
694        here, we block all signals that we think we might receive
695        and want to catch. all "child" threads will inherit this
696        setting. if we create a thread that calls sigwait() on the
697        same set of signals, implicitly unblocking all those
698        signals. any of those signals that are delivered to the
699        process will be delivered to that thread, and that thread
700        alone. this makes cleanup for a signal-driven exit much
701        easier, since we know which thread is doing it and more
702        importantly, we are free to call async-unsafe functions,
703        because the code is executing in normal thread context
704        after a return from sigwait().
705     */
706
707     sigemptyset(&signals);
708     sigaddset(&signals, SIGHUP);
709     sigaddset(&signals, SIGINT);
710     sigaddset(&signals, SIGQUIT);
711     sigaddset(&signals, SIGPIPE);
712     sigaddset(&signals, SIGTERM);
713     sigaddset(&signals, SIGUSR1);
714     sigaddset(&signals, SIGUSR2);
715
716     /* all child threads will inherit this mask unless they
717      * explicitly reset it
718      */
719
720     pthread_sigmask (SIG_BLOCK, &signals, 0);
721
722     /* install a do-nothing handler because otherwise pthreads
723        behaviour is undefined when we enter sigwait.
724     */
725
726     sigfillset (&allsignals);
727     action.sa_handler = do_nothing_handler;
728     action.sa_mask = allsignals;
729     action.sa_flags = SA_RESTART|SA_RESETHAND;
730
731     for (i = 1; i < NSIG; i++) {
732         if (sigismember (&signals, i)) {
733             sigaction (i, &action, 0);
734         }
735     }
736 }
737
738 int
739 main (int argc, char **argv)
740 {
741     DBusError error;
742     int ret;
743         void *controller_ptr;
744
745     if (!jack_controller_settings_init())
746     {
747         ret = 1;
748         goto fail;
749     }
750
751     if (argc != 2 || strcmp(argv[1], "auto") != 0)
752     {
753         ret = 0;
754         fprintf(
755             stderr,
756             "jackdbus should be auto-executed by D-Bus message bus daemon.\n"
757             "If you want to run it manually anyway, specify \"auto\" as only parameter\n");
758         goto fail_uninit_xml;
759     }
760
761     if (!paths_init())
762     {
763         ret = 1;
764         goto fail_uninit_xml;
765     }
766
767     if (!log_init())
768     {
769         ret = 1;
770         goto fail_uninit_paths;
771     }
772
773 #if !defined(DISABLE_SIGNAL_MAGIC)
774     do_signal_magic();
775 #endif
776
777     jack_set_error_function(jack_dbus_error_callback);
778     jack_set_info_function(jack_dbus_info_callback);
779
780     jack_info("------------------");
781     jack_info("Controller activated. Version " VERSION);
782
783     if (!dbus_threads_init_default())
784     {
785         jack_error("dbus_threads_init_default() failed");
786         ret = 1;
787         goto fail_uninit_log;
788     }
789
790     dbus_error_init (&error);
791     g_connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
792     if (dbus_error_is_set (&error))
793     {
794         jack_error("Cannot connect to D-Bus session bus: %s", error.message);
795         ret = 1;
796         goto fail_uninit_log;
797     }
798
799     ret = dbus_bus_request_name(
800                 g_connection,
801                 "org.jackaudio.service",
802                 DBUS_NAME_FLAG_DO_NOT_QUEUE,
803                 &error);
804     if (ret == -1)
805     {
806         jack_error("Cannot request service name: %s", error.message);
807                 dbus_error_free(&error);
808         ret = 1;
809         goto fail_unref_connection;
810     }
811     else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
812     {
813         jack_error("Requested D-Bus service name already exists");
814         ret = 1;
815         goto fail_unref_connection;
816     }
817
818     controller_ptr = jack_controller_create(g_connection);
819
820     if (controller_ptr == NULL)
821     {
822         ret = 1;
823         goto fail_unref_connection;
824     }
825
826     jack_info("Listening for D-Bus messages");
827
828     g_exit_command = FALSE;
829     while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200));
830
831     jack_controller_destroy(controller_ptr);
832
833     jack_info("Controller deactivated.");
834
835     ret = 0;
836
837 fail_unref_connection:
838     dbus_connection_unref(g_connection);
839
840 fail_uninit_log:
841     log_uninit();
842
843 fail_uninit_paths:
844     paths_uninit();
845
846 fail_uninit_xml:
847     jack_controller_settings_uninit();
848
849 fail:
850     return ret;
851 }
Note: See TracBrowser for help on using the browser.