** Warning: Cannot open xref database.
1 /* -*- mode: c; c-file-style: "bsd"; -*- */
2 /*
3 Copyright (C) 2001-2003 Paul Davis
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 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 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 $Id: jackd.c,v 1.29 2003/12/01 19:45:17 pbd Exp $
20 */
21
22 #include <stdio.h>
23 #include <signal.h>
24 #include <getopt.h>
25 #include <sys/types.h>
26 #include <sys/shm.h>
27 #include <sys/wait.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <dlfcn.h>
33
34 #include <config.h>
35
36 #include <jack/engine.h>
37 #include <jack/internal.h>
38 #include <jack/driver.h>
39 #include <jack/shm.h>
40 #include <jack/driver_parse.h>
41
42 #ifdef USE_CAPABILITIES
43
44 #include <sys/stat.h>
45 /* capgetp and capsetp are linux only extensions, not posix */
46 #undef _POSIX_SOURCE
47 #include <sys/capability.h>
48 #include <jack/start.h>
49
50 static struct stat pipe_stat;
51
52 #endif
53
54 static JSList * drivers = NULL;
55 static sigset_t signals;
56 static jack_engine_t *engine = 0;
57 static int realtime = 0;
58 static int realtime_priority = 10;
59 static int verbose = 0;
60 static int client_timeout = 500; /* msecs */
61
62 static void
63 do_nothing_handler (int sig)
64 {
65 /* this is used by the child (active) process, but it never
66 gets called unless we are already shutting down after
67 another signal.
68 */
69 char buf[64];
70 snprintf (buf, sizeof(buf),
71 "received signal %d during shutdown (ignored)\n", sig);
72 write (1, buf, strlen (buf));
73 }
74
75 static int
76 jack_main (jack_driver_desc_t * driver_desc, JSList * driver_params)
77 {
78 int sig;
79 int i;
80 sigset_t allsignals;
81 struct sigaction action;
82 int waiting;
83
84 /* ensure that we are in our own process group so that
85 kill (SIG, -pgrp) does the right thing.
86 */
87
88 setsid ();
89
90 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
91
92 /* what's this for?
93
94 POSIX says that signals are delivered like this:
95
96 * if a thread has blocked that signal, it is not
97 a candidate to receive the signal.
98 * of all threads not blocking the signal, pick
99 one at random, and deliver the signal.
100
101 this means that a simple-minded multi-threaded program can
102 expect to get POSIX signals delivered randomly to any one
103 of its threads,
104
105 here, we block all signals that we think we might receive
106 and want to catch. all "child" threads will inherit this
107 setting. if we create a thread that calls sigwait() on the
108 same set of signals, implicitly unblocking all those
109 signals. any of those signals that are delivered to the
110 process will be delivered to that thread, and that thread
111 alone. this makes cleanup for a signal-driven exit much
112 easier, since we know which thread is doing it and more
113 importantly, we are free to call async-unsafe functions,
114 because the code is executing in normal thread context
115 after a return from sigwait().
116 */
117
118 sigemptyset (&signals);
119 sigaddset(&signals, SIGHUP);
120 sigaddset(&signals, SIGINT);
121 sigaddset(&signals, SIGQUIT);
122 sigaddset(&signals, SIGPIPE);
123 sigaddset(&signals, SIGTERM);
124 sigaddset(&signals, SIGUSR1);
125 sigaddset(&signals, SIGUSR2);
126
127 /* all child threads will inherit this mask unless they
128 * explicitly reset it
129 */
130
131 pthread_sigmask (SIG_BLOCK, &signals, 0);
132
133 /* get the engine/driver started */
134
135 if ((engine = jack_engine_new (realtime, realtime_priority,
136 verbose, client_timeout,
137 getpid(), drivers)) == 0) {
138 fprintf (stderr, "cannot create engine\n");
139 return -1;
140 }
141
142 fprintf (stderr, "loading driver ..\n");
143
144 if (jack_engine_load_driver (engine, driver_desc, driver_params)) {
145 fprintf (stderr, "cannot load driver module %s\n", driver_desc->name);
146 return -1;
147 }
148
149 if (engine->driver->start (engine->driver) != 0) {
150 jack_error ("cannot start driver");
151 return -1;
152 }
153
154 /* install a do-nothing handler because otherwise pthreads
155 behaviour is undefined when we enter sigwait.
156 */
157
158 sigfillset (&allsignals);
159 action.sa_handler = do_nothing_handler;
160 action.sa_mask = allsignals;
161 action.sa_flags = SA_RESTART|SA_RESETHAND;
162
163 for (i = 1; i < NSIG; i++) {
164 if (sigismember (&signals, i)) {
165 sigaction (i, &action, 0);
166 }
167 }
168
169 if (verbose) {
170 fprintf (stderr, "%d waiting for signals\n", getpid());
171 }
172
173 waiting = TRUE;
174
175 while (waiting) {
176 sigwait (&signals, &sig);
177
178 fprintf (stderr, "jack main caught signal %d\n", sig);
179
180 switch (sig) {
181 case SIGUSR1:
182 jack_dump_configuration(engine, 1);
183 break;
184 case SIGUSR2:
185 /* driver exit */
186 waiting = FALSE;
187 break;
188 default:
189 waiting = FALSE;
190 break;
191 }
192 }
193
194 if (sig != SIGSEGV) {
195
196 /* unblock signals so we can see them during shutdown.
197 this will help prod developers not to lose sight of
198 bugs that cause segfaults etc. during shutdown.
199 */
200 sigprocmask (SIG_UNBLOCK, &signals, 0);
201 }
202
203 jack_engine_delete (engine);
204 return 1;
205 }
206
207 static jack_driver_desc_t *
208 jack_drivers_get_descriptor (JSList * drivers, const char * sofile)
209 {
210 jack_driver_desc_t * descriptor, * other_descriptor;
211 JackDriverDescFunction so_get_descriptor;
212 JSList * node;
213 void * dlhandle;
214 char * filename;
215 const char * dlerr;
216 int err;
217
218 filename = malloc (strlen (ADDON_DIR) + 1 + strlen (sofile) + 1);
219 sprintf (filename, "%s/%s", ADDON_DIR, sofile);
220
221 if (verbose)
222 printf ("getting driver descriptor from %s\n", filename);
223
224 dlhandle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
225 if (!dlhandle) {
226 jack_error ("could not open driver .so '%s': %s\n", filename, dlerror ());
227 free (filename);
228 return NULL;
229 }
230
231 dlerror ();
232
233 so_get_descriptor = (JackDriverDescFunction)
234 dlsym (dlhandle, "driver_get_descriptor");
235
236 dlerr = dlerror ();
237 if (dlerr) {
238 dlclose (dlhandle);
239 free (filename);
240 return NULL;
241 }
242
243 descriptor = so_get_descriptor ();
244 if (!descriptor) {
245 jack_error ("driver from '%s' returned NULL descriptor\n", filename);
246 dlclose (dlhandle);
247 free (filename);
248 return NULL;
249 }
250
251 err = dlclose (dlhandle);
252 if (err) {
253 jack_error ("error closing driver .so '%s': %s\n", filename, dlerror ());
254 }
255
256
257 /* check it doesn't exist already */
258 for (node = drivers; node; node = jack_slist_next (node)) {
259 other_descriptor = (jack_driver_desc_t *) node->data;
260
261 if (strcmp (descriptor->name, other_descriptor->name) == 0) {
262 jack_error ("the drivers in '%s' and '%s' both have the name '%s'; using the first\n",
263 other_descriptor->file, filename, other_descriptor->name);
264 /* FIXME: delete the descriptor */
265 free (filename);
266 return NULL;
267 }
268 }
269
270 strncpy (descriptor->file, filename, PATH_MAX);
271 free (filename);
272
273 return descriptor;
274
275 }
276
277 static JSList *
278 jack_drivers_load ()
279 {
280 struct dirent * dir_entry;
281 DIR * dir_stream;
282 const char * ptr;
283 int err;
284 JSList * driver_list = NULL;
285 jack_driver_desc_t * desc;
286
287 /* search through the ADDON_DIR and add get descriptors
288 from the .so files in it */
289 dir_stream = opendir (ADDON_DIR);
290 if (!dir_stream) {
291 jack_error ("could not open driver directory %s: %s\n", ADDON_DIR, strerror (errno));
292 return NULL;
293 }
294
295 while ( (dir_entry = readdir (dir_stream)) ) {
296 /* check the filename is of the right format */
297 if (strncmp ("jack_", dir_entry->d_name, 5) != 0) {
298 continue;
299 }
300
301 ptr = strrchr (dir_entry->d_name, '.');
302 if (!ptr) {
303 continue;
304 }
305 ptr++;
306 if (strncmp ("so", ptr, 2) != 0) {
307 continue;
308 }
309
310 desc = jack_drivers_get_descriptor (drivers, dir_entry->d_name);
311 if (desc) {
312 driver_list = jack_slist_append (driver_list, desc);
313 }
314
315 }
316
317 err = closedir (dir_stream);
318 if (err) {
319 jack_error ("error closing driver directory %s: %s\n", ADDON_DIR, strerror (errno));
320 }
321
322 if (!driver_list) {
323 jack_error ("could not find any drivers in %s!\n", ADDON_DIR);
324 return NULL;
325 }
326
327 return driver_list;
328 }
329
330 static void copyright (FILE* file)
331 {
332 fprintf (file, "jackd " VERSION "\n"
333 "Copyright 2001-2003 Paul Davis and others.\n"
334 "jackd comes with ABSOLUTELY NO WARRANTY\n"
335 "This is free software, and you are welcome to redistribute it\n"
336 "under certain conditions; see the file COPYING for details\n\n");
337 }
338
339 static void usage (FILE *file)
340 {
341 copyright (file);
342 fprintf (file, "\n"
343 "usage: jackd [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
344 " [ --timeout OR -t client-timeout-in-msecs ]\n"
345 " [ --verbose OR -v ]\n"
346 " [ --silent OR -s ]\n"
347 " [ --version OR -V ]\n"
348 " -d driver [ ... driver args ... ]\n"
349 " driver can be `alsa', `dummy' or `portaudio'\n\n"
350 " jackd -d driver --help\n"
351 " to display options for each driver\n\n");
352 }
353
354 static jack_driver_desc_t *
355 jack_find_driver_descriptor (const char * name)
356 {
357 jack_driver_desc_t * desc = 0;
358 JSList * node;
359
360 for (node = drivers; node; node = jack_slist_next (node)) {
361 desc = (jack_driver_desc_t *) node->data;
362
363 if (strcmp (desc->name, name) != 0) {
364 desc = NULL;
365 } else {
366 break;
367 }
368 }
369
370 return desc;
371 }
372
373 int
374 main (int argc, char *argv[])
375
376 {
377 jack_driver_desc_t * desc;
378 const char *options = "-ad:P:vshVRFl:t:";
379 struct option long_options[] =
380 {
381 { "driver", 1, 0, 'd' },
382 { "verbose", 0, 0, 'v' },
383 { "help", 0, 0, 'h' },
384 { "realtime", 0, 0, 'R' },
385 { "realtime-priority", 1, 0, 'P' },
386 { "timeout", 1, 0, 't' },
387 { "version", 0, 0, 'V' },
388 { "silent", 0, 0, 's' },
389 { 0, 0, 0, 0 }
390 };
391 int option_index;
392 int opt;
393 int seen_driver = 0;
394 char *driver_name = 0;
395 char **driver_args = 0;
396 JSList * driver_params;
397 int driver_nargs = 1;
398 int show_version = 0;
399 int i;
400 #ifdef USE_CAPABILITIES
401 int status;
402 #endif
403
404 setvbuf (stdout, NULL, _IOLBF, 0);
405
406 #ifdef USE_CAPABILITIES
407
408 /* check to see if there is a pipe in the right descriptor */
409 if ((status = fstat (PIPE_WRITE_FD, &pipe_stat)) == 0 &&
410 S_ISFIFO(pipe_stat.st_mode)) {
411
412 /* tell jackstart we are up and running */
413 char c = 1;
414
415 if (write (PIPE_WRITE_FD, &c, 1) != 1) {
416 fprintf (stderr, "cannot write to jackstart sync "
417 "pipe %d (%s)\n", PIPE_WRITE_FD,
418 strerror (errno));
419 }
420
421 if (close(PIPE_WRITE_FD) != 0) {
422 fprintf(stderr,
423 "jackd: error on startup pipe close: %s\n",
424 strerror (errno));
425 } else {
426 /* wait for jackstart process to set our capabilities */
427 if (wait (&status) == -1) {
428 fprintf (stderr, "jackd: wait for startup "
429 "process exit failed\n");
430 }
431 if (!WIFEXITED (status) || WEXITSTATUS (status)) {
432 fprintf(stderr, "jackd: jackstart did not "
433 "exit cleanly\n");
434 exit (1);
435 }
436 }
437 }
438 #endif
439 opterr = 0;
440 while (!seen_driver && (opt = getopt_long (argc, argv, options,
441 long_options,
442 &option_index)) != EOF) {
443 switch (opt) {
444
445 case 'd':
446 seen_driver = 1;
447 driver_name = optarg;
448 break;
449
450 case 'v':
451 verbose = 1;
452 break;
453
454 case 's':
455 jack_set_error_function (silent_jack_error_callback);
456 break;
457
458 case 'P':
459 realtime_priority = atoi (optarg);
460 break;
461
462 case 'R':
463 realtime = 1;
464 break;
465
466 case 't':
467 client_timeout = atoi (optarg);
468 break;
469
470 case 'V':
471 show_version = 1;
472 break;
473
474 default:
475 fprintf (stderr, "unknown option character %c\n", optopt);
476 /*fallthru*/
477 case 'h':
478 usage (stdout);
479 return -1;
480 }
481 }
482
483 if (show_version) {
484 printf ( "jackd version " VERSION "\n");
485 #ifdef DEFAULT_TMP_DIR
486 printf ( "default tmp directory: " DEFAULT_TMP_DIR "\n");
487 #else
488 printf ( "default tmp directory: /tmp\n");
489 #endif
490 return -1;
491 }
492
493 if (!seen_driver) {
494 usage (stderr);
495 exit (1);
496 }
497
498 drivers = jack_drivers_load ();
499 if (!drivers)
500 {
501 fprintf (stderr, "jackd: no drivers found; exiting\n");
502 exit (1);
503 }
504
505 desc = jack_find_driver_descriptor (driver_name);
506 if (!desc)
507 {
508 fprintf (stderr, "jackd: unknown driver '%s'\n", driver_name);
509 exit (1);
510 }
511
512 if (optind < argc) {
513 driver_nargs = 1 + argc - optind;
514 } else {
515 driver_nargs = 1;
516 }
517
518 if (driver_nargs == 0) {
519 fprintf (stderr, "No driver specified ... hmm. JACK won't do"
520 " anything when run like this.\n");
521 return -1;
522 }
523
524 driver_args = (char **) malloc (sizeof (char *) * driver_nargs);
525 driver_args[0] = driver_name;
526
527 for (i = 1; i < driver_nargs; i++) {
528 driver_args[i] = argv[optind++];
529 }
530
531 i = jack_parse_driver_params (desc, driver_nargs, driver_args, &driver_params);
532 if (i)
533 exit (0);
534
535 copyright (stdout);
536
537 jack_cleanup_shm ();
538 jack_cleanup_files ();
539
540 jack_main (desc, driver_params);
541
542 jack_cleanup_shm ();
543 jack_cleanup_files ();
544
545 exit (0);
546 }
547
548
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.