Cleanup
[jack2.git] / common / Jackdmp.cpp
blobfc7c66048c17e4cf500a47211e8f48c489cf4ae3
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2006 Grame
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.
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.
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.
21 #include <iostream>
22 #include <assert.h>
23 #include <signal.h>
24 #include <pwd.h>
25 #include <sys/types.h>
26 #include <dirent.h>
27 #include <getopt.h>
29 #include "JackServer.h"
30 #include "JackConstants.h"
31 #include "driver_interface.h"
32 #include "driver_parse.h"
33 #include "JackDriverLoader.h"
34 #include "jslist.h"
35 #include "JackError.h"
36 #include "shm.h"
37 #include "jack.h"
39 using namespace Jack;
41 static JackServer* fServer;
42 static char* server_name = NULL;
43 static int realtime_priority = 10;
44 static int do_mlock = 1;
45 static unsigned int port_max = 128;
46 static int realtime = 0;
47 static int loopback = 0;
48 static int temporary = 0;
49 static int client_timeout = 0; /* msecs; if zero, use period size. */
50 static int do_unlock = 0;
51 static JSList* drivers = NULL;
53 static sigset_t signals;
55 #define DEFAULT_TMP_DIR "/tmp"
56 char* jack_tmpdir = DEFAULT_TMP_DIR;
58 static void silent_jack_error_callback (const char *desc)
61 static void copyright(FILE* file)
63 fprintf (file, "jackdmp " VERSION "\n"
64 "Copyright 2001-2005 Paul Davis and others.\n"
65 "Copyright 2004-2007 Grame.\n"
66 "jackdmp comes with ABSOLUTELY NO WARRANTY\n"
67 "This is free software, and you are welcome to redistribute it\n"
68 "under certain conditions; see the file COPYING for details\n");
71 static void usage (FILE* file)
73 copyright (file);
74 fprintf (file, "\n"
75 "usage: jackdmp [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
76 " [ --name OR -n server-name ]\n"
77 // " [ --no-mlock OR -m ]\n"
78 // " [ --unlock OR -u ]\n"
79 " [ --timeout OR -t client-timeout-in-msecs ]\n"
80 " [ --loopback OR -L loopback-port-number ]\n"
81 // " [ --port-max OR -p maximum-number-of-ports]\n"
82 " [ --verbose OR -v ]\n"
83 " [ --silent OR -s ]\n"
84 " [ --sync OR -S ]\n"
85 " [ --version OR -V ]\n"
86 " -d driver [ ... driver args ... ]\n"
87 " where driver can be `alsa', `coreaudio', 'portaudio' or `dummy'\n"
88 " jackdmp -d driver --help\n"
89 " to display options for each driver\n\n");
93 static void DoNothingHandler(int sig)
95 /* this is used by the child (active) process, but it never
96 gets called unless we are already shutting down after
97 another signal.
99 char buf[64];
100 snprintf(buf, sizeof(buf), "received signal %d during shutdown (ignored)\n", sig);
101 write(1, buf, strlen(buf));
104 static int JackStart(jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int time_out_ms, int rt, int priority, int loopback, int verbose)
106 JackLog("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld \n", sync, time_out_ms, rt, priority, verbose);
107 fServer = new JackServer(sync, time_out_ms, rt, priority, loopback, verbose);
108 int res = fServer->Open(driver_desc, driver_params);
109 return (res < 0) ? res : fServer->Start();
112 static int JackStop()
114 fServer->Stop();
115 fServer->Close();
116 JackLog("Jackdmp: server close\n");
117 delete fServer;
118 JackLog("Jackdmp: delete server\n");
119 return 0;
122 static int JackDelete()
124 delete fServer;
125 JackLog("Jackdmp: delete server\n");
126 return 0;
129 static void FilterSIGPIPE()
131 sigset_t set;
132 sigemptyset(&set);
133 sigaddset(&set, SIGPIPE);
134 //sigprocmask(SIG_BLOCK, &set, 0);
135 pthread_sigmask(SIG_BLOCK, &set, 0);
138 static char* jack_default_server_name(void)
140 char *server_name;
141 if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
142 server_name = "default";
143 return server_name;
146 /* returns the name of the per-user subdirectory of jack_tmpdir */
147 static char* jack_user_dir(void)
149 static char user_dir[PATH_MAX] = "";
151 /* format the path name on the first call */
152 if (user_dir[0] == '\0') {
153 snprintf (user_dir, sizeof (user_dir), "%s/jack-%d",
154 jack_tmpdir, getuid ());
157 return user_dir;
160 /* returns the name of the per-server subdirectory of jack_user_dir() */
162 static char* get_jack_server_dir(const char* toto)
164 static char server_dir[PATH_MAX] = "";
166 // format the path name on the first call
167 if (server_dir[0] == '\0') {
168 snprintf (server_dir, sizeof (server_dir), "%s/%s",
169 jack_user_dir (), server_name);
172 return server_dir;
175 static void
176 jack_cleanup_files (const char *server_name)
178 DIR *dir;
179 struct dirent *dirent;
180 char *dir_name = get_jack_server_dir (server_name);
182 /* On termination, we remove all files that jackd creates so
183 * subsequent attempts to start jackd will not believe that an
184 * instance is already running. If the server crashes or is
185 * terminated with SIGKILL, this is not possible. So, cleanup
186 * is also attempted when jackd starts.
188 * There are several tricky issues. First, the previous JACK
189 * server may have run for a different user ID, so its files
190 * may be inaccessible. This is handled by using a separate
191 * JACK_TMP_DIR subdirectory for each user. Second, there may
192 * be other servers running with different names. Each gets
193 * its own subdirectory within the per-user directory. The
194 * current process has already registered as `server_name', so
195 * we know there is no other server actively using that name.
198 /* nothing to do if the server directory does not exist */
199 if ((dir = opendir (dir_name)) == NULL) {
200 return ;
203 /* unlink all the files in this directory, they are mine */
204 while ((dirent = readdir (dir)) != NULL) {
206 char fullpath[PATH_MAX];
208 if ((strcmp (dirent->d_name, ".") == 0)
209 || (strcmp (dirent->d_name, "..") == 0)) {
210 continue;
213 snprintf (fullpath, sizeof (fullpath), "%s/%s",
214 dir_name, dirent->d_name);
216 if (unlink (fullpath)) {
217 jack_error ("cannot unlink `%s' (%s)", fullpath,
218 strerror (errno));
222 closedir (dir);
224 /* now, delete the per-server subdirectory, itself */
225 if (rmdir (dir_name)) {
226 jack_error ("cannot remove `%s' (%s)", dir_name,
227 strerror (errno));
230 /* finally, delete the per-user subdirectory, if empty */
231 if (rmdir (jack_user_dir ())) {
232 if (errno != ENOTEMPTY) {
233 jack_error ("cannot remove `%s' (%s)",
234 jack_user_dir (), strerror (errno));
239 #ifdef FORK_SERVER
241 int main(int argc, char* argv[])
243 int sig;
244 sigset_t allsignals;
245 struct sigaction action;
246 int waiting;
248 jack_driver_desc_t* driver_desc;
249 const char *options = "-ad:P:uvshVRL:STFl:t:mn:p:";
250 struct option long_options[] = {
251 { "driver", 1, 0, 'd'
253 { "verbose", 0, 0, 'v' },
254 { "help", 0, 0, 'h' },
255 { "port-max", 1, 0, 'p' },
256 { "no-mlock", 0, 0, 'm' },
257 { "name", 0, 0, 'n' },
258 { "unlock", 0, 0, 'u' },
259 { "realtime", 0, 0, 'R' },
260 { "loopback", 0, 0, 'L' },
261 { "realtime-priority", 1, 0, 'P' },
262 { "timeout", 1, 0, 't' },
263 { "temporary", 0, 0, 'T' },
264 { "version", 0, 0, 'V' },
265 { "silent", 0, 0, 's' },
266 { "sync", 0, 0, 'S' },
267 { 0, 0, 0, 0 }
269 int opt = 0;
270 int option_index = 0;
271 int seen_driver = 0;
272 char *driver_name = NULL;
273 char **driver_args = NULL;
274 JSList* driver_params;
275 int driver_nargs = 1;
276 int show_version = 0;
277 int sync = 0;
278 int rc, i;
280 opterr = 0;
281 while (!seen_driver &&
282 (opt = getopt_long(argc, argv, options,
283 long_options, &option_index)) != EOF) {
284 switch (opt) {
286 case 'd':
287 seen_driver = 1;
288 driver_name = optarg;
289 break;
291 case 'v':
292 jack_verbose = 1;
293 break;
295 case 's':
296 jack_set_error_function(silent_jack_error_callback);
297 break;
299 case 'S':
300 sync = 1;
301 break;
303 case 'n':
304 server_name = optarg;
305 break;
307 case 'm':
308 do_mlock = 0;
309 break;
311 case 'p':
312 port_max = (unsigned int)atol(optarg);
313 break;
315 case 'P':
316 realtime_priority = atoi(optarg);
317 break;
319 case 'R':
320 realtime = 1;
321 break;
323 case 'L':
324 loopback = atoi(optarg);
325 break;
327 case 'T':
328 temporary = 1;
329 break;
331 case 't':
332 client_timeout = atoi(optarg);
333 break;
335 case 'u':
336 do_unlock = 1;
337 break;
339 case 'V':
340 show_version = 1;
341 break;
343 default:
344 fprintf(stderr, "unknown option character %c\n",
345 optopt);
346 /*fallthru*/
347 case 'h':
348 usage(stdout);
349 return -1;
354 if (show_version) {
355 printf ( "jackd version " VERSION
356 " tmpdir " DEFAULT_TMP_DIR
357 " protocol " PROTOCOL_VERSION
358 "\n");
359 return -1;
363 if (!seen_driver) {
364 usage (stderr);
365 exit (1);
368 drivers = jack_drivers_load (drivers);
369 if (!drivers) {
370 fprintf (stderr, "jackdmp: no drivers found; exiting\n");
371 exit (1);
374 driver_desc = jack_find_driver_descriptor (drivers, driver_name);
375 if (!driver_desc) {
376 fprintf (stderr, "jackdmp: unknown driver '%s'\n", driver_name);
377 exit (1);
380 if (optind < argc) {
381 driver_nargs = 1 + argc - optind;
382 } else {
383 driver_nargs = 1;
386 if (driver_nargs == 0) {
387 fprintf (stderr, "No driver specified ... hmm. JACK won't do"
388 " anything when run like this.\n");
389 return -1;
392 driver_args = (char **) malloc (sizeof (char *) * driver_nargs);
393 driver_args[0] = driver_name;
395 for (i = 1; i < driver_nargs; i++) {
396 driver_args[i] = argv[optind++];
399 if (jack_parse_driver_params (driver_desc, driver_nargs,
400 driver_args, &driver_params)) {
401 exit (0);
404 if (server_name == NULL)
405 server_name = jack_default_server_name ();
407 copyright (stdout);
409 rc = jack_register_server (server_name);
410 switch (rc) {
411 case EEXIST:
412 fprintf (stderr, "`%s' server already active\n", server_name);
413 exit (1);
414 case ENOSPC:
415 fprintf (stderr, "too many servers already active\n");
416 exit (2);
417 case ENOMEM:
418 fprintf (stderr, "no access to shm registry\n");
419 exit (3);
420 default:
421 if (jack_verbose)
422 fprintf (stderr, "server `%s' registered\n",
423 server_name);
426 /* clean up shared memory and files from any previous
427 * instance of this server name */
428 jack_cleanup_shm();
429 jack_cleanup_files(server_name);
431 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
433 sigemptyset(&signals);
434 sigaddset(&signals, SIGHUP);
435 sigaddset(&signals, SIGINT);
436 sigaddset(&signals, SIGQUIT);
437 sigaddset(&signals, SIGPIPE);
438 sigaddset(&signals, SIGTERM);
439 sigaddset(&signals, SIGUSR1);
440 sigaddset(&signals, SIGUSR2);
442 // all child threads will inherit this mask unless they
443 // explicitly reset it
445 FilterSIGPIPE();
446 pthread_sigmask(SIG_BLOCK, &signals, 0);
448 if (!realtime && client_timeout == 0)
449 client_timeout = 500; /* 0.5 sec; usable when non realtime. */
451 int res = JackStart(driver_desc, driver_params, sync, client_timeout, realtime, realtime_priority, loopback, jack_verbose);
452 if (res < 0) {
453 jack_error("Cannot start server... exit");
454 JackDelete();
455 return 0;
459 For testing purpose...
460 InternalMetro* client1 = new InternalMetro(1200, 0.4, 20, 80, "metro1");
461 InternalMetro* client2 = new InternalMetro(600, 0.4, 20, 150, "metro2");
462 InternalMetro* client3 = new InternalMetro(1000, 0.4, 20, 110, "metro3");
463 InternalMetro* client4 = new InternalMetro(1200, 0.4, 20, 80, "metro4");
464 InternalMetro* client5 = new InternalMetro(1500, 0.4, 20, 60, "metro5");
465 InternalMetro* client6 = new InternalMetro(1200, 0.4, 20, 84, "metro6");
466 InternalMetro* client7 = new InternalMetro(600, 0.4, 20, 160, "metro7");
467 InternalMetro* client8 = new InternalMetro(1000, 0.4, 20, 113, "metro8");
468 InternalMetro* client9 = new InternalMetro(1200, 0.4, 20, 84, "metro9");
469 InternalMetro* client10 = new InternalMetro(1500, 0.4, 20, 70, "metro10");
472 // install a do-nothing handler because otherwise pthreads
473 // behaviour is undefined when we enter sigwait.
475 sigfillset(&allsignals);
476 action.sa_handler = DoNothingHandler;
477 action.sa_mask = allsignals;
478 action.sa_flags = SA_RESTART | SA_RESETHAND;
480 for (i = 1; i < NSIG; i++) {
481 if (sigismember(&signals, i)) {
482 sigaction(i, &action, 0);
486 waiting = TRUE;
488 while (waiting) {
489 sigwait(&signals, &sig);
491 fprintf(stderr, "jack main caught signal %d\n", sig);
493 switch (sig) {
494 case SIGUSR1:
495 //jack_dump_configuration(engine, 1);
496 break;
497 case SIGUSR2:
498 // driver exit
499 waiting = FALSE;
500 break;
501 default:
502 waiting = FALSE;
503 break;
507 if (sig != SIGSEGV) {
508 // unblock signals so we can see them during shutdown.
509 // this will help prod developers not to lose sight of
510 // bugs that cause segfaults etc. during shutdown.
511 sigprocmask(SIG_UNBLOCK, &signals, 0);
514 JackStop();
516 jack_cleanup_shm();
517 jack_cleanup_files(server_name);
518 jack_unregister_server(server_name);
520 return 1;
523 #else
525 int main(int argc, char* argv[])
527 char c;
528 long sample_sate = lopt(argv, "-r", 44100);
529 long buffer_size = lopt(argv, "-p", 512);
530 long chan_in = lopt(argv, "-i", 2);
531 long chan_out = lopt(argv, "-o", 2);
532 long audiodevice = lopt(argv, "-I", -1);
533 long sync = lopt(argv, "-s", 0);
534 long timeout = lopt(argv, "-t", 100 * 1000);
535 const char* name = flag(argv, "-n", "Built-in Audio");
536 long rt = lopt(argv, "-R", 0);
537 verbose = lopt(argv, "-v", 0);
539 copyright(stdout);
540 usage(stdout);
542 FilterSIGPIPE();
544 printf("jackdmp: sample_sate = %ld buffer_size = %ld chan_in = %ld chan_out = %ld name = %s sync-mode = %ld\n",
545 sample_sate, buffer_size, chan_in, chan_out, name, sync);
546 assert(buffer_size <= BUFFER_SIZE_MAX);
548 int res = JackStart(sample_sate, buffer_size, chan_in, chan_out, name, audiodevice, sync, timeout, rt);
549 if (res < 0) {
550 jack_error("Cannot start server... exit\n");
551 JackDelete();
552 return 0;
555 while (((c = getchar()) != 'q')) {
557 switch (c) {
559 case 's':
560 fServer->PrintState();
561 break;
565 JackStop();
566 return 0;
569 #endif