/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright (C) 2007 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "jackcontroller.h" FILE *g_logfile; void jack_dbus_info (const char *msg) { time_t timestamp; char timestamp_str[26]; time(×tamp); ctime_r(×tamp, timestamp_str); timestamp_str[24] = 0; fprintf (g_logfile, "%s: %s\n", timestamp_str, msg); fflush(g_logfile); } void jack_dbus_error (const char *msg) { time_t timestamp; char timestamp_str[26]; time(×tamp); ctime_r(×tamp, timestamp_str); timestamp_str[24] = 0; fprintf (g_logfile, "%s: ERROR: %s\n", timestamp_str, msg); fflush(g_logfile); } gboolean log_init() { char path[255]; const char *home_dir; struct stat st; home_dir = getenv("HOME"); if (home_dir == NULL) { g_printerr("Environment variable HOME not set\n"); return FALSE; } snprintf(path, sizeof(path), "%s/.jackdbus", home_dir); if (stat(path, &st) != 0) { if (errno == ENOENT) { g_print("Directory \"%s\" does not exist. Creating...\n", path); if (mkdir(path, 0777) != 0) { g_printerr("Failed to create \"%s\" directory: %d (%s)\n", path, errno, strerror(errno)); return FALSE; } } else { g_printerr("Failed to stat \"%s\": %d (%s)\n", path, errno, strerror(errno)); return FALSE; } } else { if (!S_ISDIR(st.st_mode)) { g_printerr("\"%s\" exists but is not directory.\n", path); return FALSE; } } snprintf(path, sizeof(path), "%s/.jackdbus/jackdbus.log", home_dir); g_logfile = fopen(path, "a"); if (g_logfile == NULL) { fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", path, errno, strerror(errno)); return FALSE; } return TRUE; } void log_uninit() { fclose(g_logfile); } static void do_nothing_handler (int sig) { /* this is used by the child (active) process, but it never gets called unless we are already shutting down after another signal. */ char buf[64]; snprintf (buf, sizeof(buf), "received signal %d during shutdown (ignored)\n", sig); write (1, buf, strlen (buf)); } int main (int argc, char **argv) { DBusGConnection *connection; GError *error; JackController *controller_ptr; DBusGProxy *bus_proxy; guint request_name_result; int ret; sigset_t signals; sigset_t allsignals; struct sigaction action; int i; if (!log_init()) { goto fail; } /* ensure that we are in our own process group so that kill (SIG, -pgrp) does the right thing. */ setsid(); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* what's this for? POSIX says that signals are delivered like this: * if a thread has blocked that signal, it is not a candidate to receive the signal. * of all threads not blocking the signal, pick one at random, and deliver the signal. this means that a simple-minded multi-threaded program can expect to get POSIX signals delivered randomly to any one of its threads, here, we block all signals that we think we might receive and want to catch. all "child" threads will inherit this setting. if we create a thread that calls sigwait() on the same set of signals, implicitly unblocking all those signals. any of those signals that are delivered to the process will be delivered to that thread, and that thread alone. this makes cleanup for a signal-driven exit much easier, since we know which thread is doing it and more importantly, we are free to call async-unsafe functions, because the code is executing in normal thread context after a return from sigwait(). */ sigemptyset(&signals); sigaddset(&signals, SIGHUP); sigaddset(&signals, SIGINT); sigaddset(&signals, SIGQUIT); sigaddset(&signals, SIGPIPE); sigaddset(&signals, SIGTERM); sigaddset(&signals, SIGUSR1); sigaddset(&signals, SIGUSR2); /* all child threads will inherit this mask unless they * explicitly reset it */ pthread_sigmask (SIG_BLOCK, &signals, 0); /* install a do-nothing handler because otherwise pthreads behaviour is undefined when we enter sigwait. */ sigfillset (&allsignals); action.sa_handler = do_nothing_handler; action.sa_mask = allsignals; action.sa_flags = SA_RESTART|SA_RESETHAND; for (i = 1; i < NSIG; i++) { if (sigismember (&signals, i)) { sigaction (i, &action, 0); } } g_type_init (); jack_set_error_function (jack_dbus_error); jack_set_info_function (jack_dbus_info); jack_info("------------------"); jack_info("Controller activated."); error = NULL; connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (connection == NULL) { g_printerr ("Failed to open connection to bus: %s\n", error->message); g_error_free (error); goto fail_uninit_log; } bus_proxy = dbus_g_proxy_new_for_name ( connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error, G_TYPE_STRING, "org.jackaudio.service", G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &request_name_result, G_TYPE_INVALID)) { g_printerr ("Failed to acquire org.jackaudio.service %s", error->message); goto fail_uninit_log; } controller_ptr = g_object_new(JACK_CONTROLLER_TYPE, NULL); dbus_g_connection_register_g_object (connection, "/DefaultController", G_OBJECT(controller_ptr)); jack_controller_run(controller_ptr); g_object_unref(controller_ptr); jack_info("Controller deactivated."); ret = 0; fail_uninit_log: log_uninit(); fail: return ret; }