Jack2 1.9.6

JackPosixServerLaunch.cpp

00001 /*
00002 Copyright (C) 2001-2003 Paul Davis
00003 Copyright (C) 2004-2008 Grame
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU Lesser General Public License as published by
00007 the Free Software Foundation; either version 2.1 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public License
00016 along with this program; if not, write to the Free Software 
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 
00019 */
00020 
00021 #include "JackConstants.h"
00022 #include "JackChannel.h"
00023 #include "JackLibGlobals.h"
00024 #include "JackServerLaunch.h"
00025 #include "JackPlatformPlug.h"
00026 
00027 using namespace Jack;
00028 
00029 #if defined(USE_LIBDBUS_AUTOLAUNCH)
00030 
00031 #include <dbus/dbus.h>
00032 
00033 static int start_server_dbus(const char* server_name)
00034 {
00035     DBusError err;
00036     DBusConnection *conn;
00037     DBusMessage *msg;
00038 
00039     // initialise the errors
00040     dbus_error_init(&err);
00041 
00042     // connect to the bus
00043     conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
00044     if (dbus_error_is_set(&err)) {
00045         fprintf(stderr, "Connection Error (%s)\n", err.message);
00046         dbus_error_free(&err);
00047     }
00048     if (NULL == conn) {
00049         return 1;
00050     }
00051 
00052     msg = dbus_message_new_method_call(
00053         "org.jackaudio.service",     // target for the method call
00054         "/org/jackaudio/Controller", // object to call on
00055         "org.jackaudio.JackControl", // interface to call on
00056         "StartServer");              // method name
00057     if (NULL == msg) {
00058         fprintf(stderr, "Message Null\n");
00059         return 1;
00060     }
00061 
00062     // send message and get a handle for a reply
00063     if (!dbus_connection_send(conn, msg, NULL))
00064     {
00065         fprintf(stderr, "Out Of Memory!\n");
00066         return 1;
00067     }
00068 
00069     dbus_message_unref(msg);
00070     dbus_connection_flush(conn);
00071     dbus_error_free(&err);
00072 
00073     return 0;
00074 }
00075 
00076 #else
00077 
00078 /* Exec the JACK server in this process.  Does not return. */
00079 static void start_server_classic_aux(const char* server_name)
00080 {
00081     FILE* fp = 0;
00082     char filename[255];
00083     char arguments[255];
00084     char buffer[255];
00085     char* command = 0;
00086     size_t pos = 0;
00087     size_t result = 0;
00088     char** argv = 0;
00089     int i = 0;
00090     int good = 0;
00091     int ret;
00092 
00093     snprintf(filename, 255, "%s/.jackdrc", getenv("HOME"));
00094     fp = fopen(filename, "r");
00095 
00096     if (!fp) {
00097         fp = fopen("/etc/jackdrc", "r");
00098     }
00099     /* if still not found, check old config name for backwards compatability */
00100     if (!fp) {
00101         fp = fopen("/etc/jackd.conf", "r");
00102     }
00103 
00104     if (fp) {
00105         arguments[0] = '\0';
00106         ret = fscanf(fp, "%s", buffer);
00107         while (ret != 0 && ret != EOF) {
00108             strcat(arguments, buffer);
00109             strcat(arguments, " ");
00110             ret = fscanf(fp, "%s", buffer);
00111         }
00112         if (strlen(arguments) > 0) {
00113             good = 1;
00114         }
00115         fclose(fp);
00116     }
00117 
00118     if (!good) {
00119         command = (char*)(JACK_LOCATION "/jackd");
00120         strncpy(arguments, JACK_LOCATION "/jackd -T -d "JACK_DEFAULT_DRIVER, 255);
00121     } else {
00122         result = strcspn(arguments, " ");
00123         command = (char*)malloc(result + 1);
00124         strncpy(command, arguments, result);
00125         command[result] = '\0';
00126     }
00127 
00128     argv = (char**)malloc(255);
00129 
00130     while (1) {
00131         /* insert -T and -nserver_name in front of arguments */
00132         if (i == 1) {
00133             argv[i] = (char*)malloc(strlen ("-T") + 1);
00134             strcpy (argv[i++], "-T");
00135             if (server_name) {
00136                 size_t optlen = strlen("-n");
00137                 char* buf = (char*)malloc(optlen + strlen(server_name) + 1);
00138                 strcpy(buf, "-n");
00139                 strcpy(buf + optlen, server_name);
00140                 argv[i++] = buf;
00141             }
00142         }
00143 
00144         result = strcspn(arguments + pos, " ");
00145         if (result == 0) {
00146             break;
00147         }
00148         argv[i] = (char*)malloc(result + 1);
00149         strncpy(argv[i], arguments + pos, result);
00150         argv[i][result] = '\0';
00151         pos += result + 1;
00152         ++i;
00153     }
00154     argv[i] = 0;
00155     execv(command, argv);
00156 
00157     /* If execv() succeeds, it does not return. There's no point
00158      * in calling jack_error() here in the child process. */
00159     fprintf(stderr, "exec of JACK server (command = \"%s\") failed: %s\n", command, strerror(errno));
00160 }
00161 
00162 static int start_server_classic(const char* server_name)
00163 {
00164     /* The double fork() forces the server to become a child of
00165      * init, which will always clean up zombie process state on
00166      * termination. This even works in cases where the server
00167      * terminates but this client does not.
00168      *
00169      * Since fork() is usually implemented using copy-on-write
00170      * virtual memory tricks, the overhead of the second fork() is
00171      * probably relatively small.
00172      */
00173     switch (fork()) {
00174         case 0:                                 /* child process */
00175             switch (fork()) {
00176                 case 0:                 /* grandchild process */
00177                     start_server_classic_aux(server_name);
00178                     _exit(99);  /* exec failed */
00179                 case - 1:
00180                     _exit(98);
00181                 default:
00182                     _exit(0);
00183             }
00184         case - 1:                       /* fork() error */
00185             return 1;           /* failed to start server */
00186     }
00187 
00188     /* only the original parent process goes here */
00189     return 0;                   /* (probably) successful */
00190 }
00191 
00192 #endif
00193 
00194 static int start_server(const char* server_name, jack_options_t options)
00195 {
00196     if ((options & JackNoStartServer) || getenv("JACK_NO_START_SERVER")) {
00197         return 1;
00198     }
00199 
00200 #if defined(USE_LIBDBUS_AUTOLAUNCH)
00201     return start_server_dbus(server_name);
00202 #else
00203     return start_server_classic(server_name);
00204 #endif
00205 }
00206 
00207 static int server_connect(char* server_name)
00208 {
00209     JackClientChannel channel;
00210     int res = channel.ServerCheck(server_name);
00211     channel.Close();
00212     return res;
00213 }
00214 
00215 int try_start_server(jack_varargs_t* va, jack_options_t options, jack_status_t* status)
00216 {
00217     if (server_connect(va->server_name) < 0) {
00218         int trys;
00219         if (start_server(va->server_name, options)) {
00220             int my_status1 = *status | JackFailure | JackServerFailed;
00221             *status = (jack_status_t)my_status1;
00222             return -1;
00223         }
00224         trys = 5;
00225         do {
00226             sleep(1);
00227             if (--trys < 0) {
00228                 int my_status1 = *status | JackFailure | JackServerFailed;
00229                 *status = (jack_status_t)my_status1;
00230                 return -1;
00231             }
00232         } while (server_connect(va->server_name) < 0);
00233         int my_status1 = *status | JackServerStarted;
00234         *status = (jack_status_t)my_status1;
00235     }
00236 
00237     return 0;
00238 }