1 /*
2 Copyright (C) 2001-2003 Paul Davis
3 Copyright (C) 2004-2008 Grame
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
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 Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 #if !defined(WIN32) || defined(__CYGWIN__)
21 
22 #ifdef PTHREAD_WIN32        // Added by JE - 13-02-2010
23 #include <ptw32/pthread.h>  // Makes sure we #include the ptw32 version for
24 #endif                      // consistency - even though we won't need it !
25 
26 #include "JackConstants.h"
27 #include "JackChannel.h"
28 #include "JackLibGlobals.h"
29 #include "JackServerLaunch.h"
30 #include "JackPlatformPlug.h"
31 
32 #include <sys/wait.h>
33 
34 using namespace Jack;
35 
36 #if defined(USE_LIBDBUS_AUTOLAUNCH)
37 
38 #include <dbus/dbus.h>
39 
start_server_dbus(const char * server_name)40 static int start_server_dbus(const char* server_name)
41 {
42     DBusError err;
43     DBusConnection *conn;
44     DBusMessage *msg;
45 
46     // initialise the errors
47     dbus_error_init(&err);
48 
49     // connect to the bus
50     conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
51     if (dbus_error_is_set(&err)) {
52         fprintf(stderr, "Connection Error (%s)\n", err.message);
53         dbus_error_free(&err);
54     }
55     if (NULL == conn) {
56         return 1;
57     }
58 
59     msg = dbus_message_new_method_call(
60         "org.jackaudio.service",     // target for the method call
61         "/org/jackaudio/Controller", // object to call on
62         "org.jackaudio.JackControl", // interface to call on
63         "StartServer");              // method name
64     if (NULL == msg) {
65         fprintf(stderr, "Message Null\n");
66         return 1;
67     }
68 
69     // send message and get a handle for a reply
70     if (!dbus_connection_send(conn, msg, NULL))
71     {
72         fprintf(stderr, "Out Of Memory!\n");
73         return 1;
74     }
75 
76     dbus_message_unref(msg);
77     dbus_connection_flush(conn);
78     dbus_error_free(&err);
79 
80     return 0;
81 }
82 
83 #elif defined(USE_CLASSIC_AUTOLAUNCH)
84 
85 /* Exec the JACK server in this process.  Does not return. */
start_server_classic_aux(const char * server_name)86 static void start_server_classic_aux(const char* server_name)
87 {
88     FILE* fp = 0;
89     char filename[255];
90     char arguments[255];
91     char buffer[255];
92     char* command = 0;
93     size_t pos = 0;
94     size_t result = 0;
95     char** argv = 0;
96     int i = 0;
97     int good = 0;
98     int res;
99 
100     snprintf(filename, 255, "%s/.jackdrc", getenv("HOME"));
101     fp = fopen(filename, "r");
102 
103     if (!fp) {
104         fp = fopen("/etc/jackdrc", "r");
105     }
106     /* if still not found, check old config name for backwards compatibility */
107     if (!fp) {
108         fp = fopen("/etc/jackd.conf", "r");
109     }
110 
111     if (fp) {
112         arguments[0] = '\0';
113         res = fscanf(fp, "%s", buffer);
114         while (res != 0 && res != EOF) {
115             strcat(arguments, buffer);
116             strcat(arguments, " ");
117             res = fscanf(fp, "%s", buffer);
118         }
119         if (strlen(arguments) > 0) {
120             good = 1;
121         }
122         fclose(fp);
123     }
124 
125     if (!good) {
126         command = (char*)(JACK_LOCATION "/jackd");
127         strncpy(arguments, JACK_LOCATION "/jackd -T -d " JACK_DEFAULT_DRIVER, 255);
128     } else {
129         result = strcspn(arguments, " ");
130         command = (char*)malloc(result + 1);
131         strncpy(command, arguments, result);
132         command[result] = '\0';
133     }
134 
135     argv = (char**)malloc(255);
136 
137     while (1) {
138         /* insert -T and -nserver_name in front of arguments */
139         if (i == 1) {
140             argv[i] = (char*)malloc(strlen ("-T") + 1);
141             strcpy (argv[i++], "-T");
142             if (server_name) {
143                 size_t optlen = strlen("-n");
144                 char* buf = (char*)malloc(optlen + strlen(server_name) + 1);
145                 strcpy(buf, "-n");
146                 strcpy(buf + optlen, server_name);
147                 argv[i++] = buf;
148             }
149         }
150 
151         /* skip whitespace */
152         while (pos < strlen(arguments) && arguments[pos] && arguments[pos] == ' ') {
153             ++pos;
154         }
155 
156         if (pos >= strlen(arguments)) {
157             break;
158         }
159 
160         if (arguments[pos] == '\"') {
161             ++pos;
162             result = strcspn(arguments + pos, "\"");
163         } else {
164             result = strcspn(arguments + pos, " ");
165         }
166 
167         if (0 == result) {
168             break;
169         }
170 
171         argv[i] = (char*)malloc(result + 1);
172         strncpy(argv[i], arguments + pos, result);
173         argv[i][result] = '\0';
174         pos += result + 1;
175 
176         if (++i > 253) {
177             break;
178         }
179     }
180 
181     argv[i] = 0;
182     execv(command, argv);
183 
184     /* If execv() succeeds, it does not return. There's no point
185      * in calling jack_error() here in the child process. */
186     fprintf(stderr, "exec of JACK server (command = \"%s\") failed: %s\n", command, strerror(errno));
187 }
188 
start_server_classic(const char * server_name)189 static int start_server_classic(const char* server_name)
190 {
191     /* The double fork() forces the server to become a child of
192      * init, which will always clean up zombie process state on
193      * termination. This even works in cases where the server
194      * terminates but this client does not.
195      *
196      * Since fork() is usually implemented using copy-on-write
197      * virtual memory tricks, the overhead of the second fork() is
198      * probably relatively small.
199      */
200 
201     int status;
202     pid_t first_child_pid;
203 
204     first_child_pid = fork();
205     switch (first_child_pid) {
206         case 0:					/* child process */
207             switch (fork()) {
208                 case 0:			/* grandchild process */
209                     start_server_classic_aux(server_name);
210                     _exit(99);	/* exec failed */
211                 case - 1:
212                     _exit(98);
213                 default:
214                     _exit(0);
215             }
216         case - 1:			/* fork() error */
217             return 1;		/* failed to start server */
218     }
219     waitpid(first_child_pid, &status, 0);
220 
221     /* only the original parent process goes here */
222     return 0;			/* (probably) successful */
223 }
224 
225 #endif
226 
start_server(const char * server_name,jack_options_t options)227 static int start_server(const char* server_name, jack_options_t options)
228 {
229     if ((options & JackNoStartServer) || getenv("JACK_NO_START_SERVER")) {
230         return 1;
231     }
232 
233 #if defined(USE_LIBDBUS_AUTOLAUNCH)
234     return start_server_dbus(server_name);
235 #elif defined(USE_CLASSIC_AUTOLAUNCH)
236     return start_server_classic(server_name);
237 #else
238     fprintf(stderr, "Automatic start of JACK server is disabled at configure time\n");
239     return 1;
240 #endif
241 }
242 
server_connect(char * server_name)243 static int server_connect(char* server_name)
244 {
245     JackClientChannel channel;
246     int res = channel.ServerCheck(server_name);
247     channel.Close();
248     JackSleep(2000); // Added by JE - 02-01-2009 (gives
249                      // the channel some time to close)
250     return res;
251 }
252 
try_start_server(jack_varargs_t * va,jack_options_t options,jack_status_t * status)253 int try_start_server(jack_varargs_t* va, jack_options_t options, jack_status_t* status)
254 {
255     if (server_connect(va->server_name) < 0) {
256         int trys;
257         if (start_server(va->server_name, options)) {
258             int my_status1 = *status | JackFailure | JackServerFailed;
259             *status = (jack_status_t)my_status1;
260             return -1;
261         }
262         trys = 5;
263         do {
264             sleep(1);
265             if (--trys < 0) {
266                 int my_status1 = *status | JackFailure | JackServerFailed;
267                 *status = (jack_status_t)my_status1;
268                 return -1;
269             }
270         } while (server_connect(va->server_name) < 0);
271         int my_status1 = *status | JackServerStarted;
272         *status = (jack_status_t)my_status1;
273     }
274 
275     return 0;
276 }
277 
278 #endif  // !defined(WIN32) || defined(__CYGWIN__)
279