Jackdmp.cpp 12.3 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2001 Paul Davis
sletz's avatar
sletz committed
3
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

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, or
(at your option) any later version.

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 <iostream>
#include <assert.h>
#include <signal.h>
#include <pwd.h>
#include <sys/types.h>
#include <dirent.h>
#include <getopt.h>

#include "JackServer.h"
#include "JackConstants.h"
#include "driver_interface.h"
#include "JackDriverLoader.h"
#include "jslist.h"
#include "JackError.h"
35
#include "JackTools.h"
sletz's avatar
sletz committed
36
37
38
#include "shm.h"
#include "jack.h"

39
40
41
42
#ifdef __APPLE_
#include <CoreFoundation/CFNotificationCenter.h>
#endif

sletz's avatar
sletz committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using namespace Jack;

static JackServer* fServer;
static char* server_name = NULL;
static int realtime_priority = 10;
static int do_mlock = 1;
static int realtime = 0;
static int loopback = 0;
static int temporary = 0;
static int client_timeout = 0; /* msecs; if zero, use period size. */
static int do_unlock = 0;
static JSList* drivers = NULL;
static sigset_t signals;

sletz's avatar
sletz committed
57
static void silent_jack_error_callback(const char *desc)
sletz's avatar
sletz committed
58
59
60
61
{}

static void copyright(FILE* file)
{
sletz's avatar
sletz committed
62
    fprintf(file, "jackdmp " VERSION "\n"
sletz's avatar
sletz committed
63
64
65
66
67
            "Copyright 2001-2005 Paul Davis and others.\n"
            "Copyright 2004-2008 Grame.\n"
            "jackdmp comes with ABSOLUTELY NO WARRANTY\n"
            "This is free software, and you are welcome to redistribute it\n"
            "under certain conditions; see the file COPYING for details\n");
sletz's avatar
sletz committed
68
69
}

sletz's avatar
sletz committed
70
static void usage(FILE* file)
sletz's avatar
sletz committed
71
{
sletz's avatar
sletz committed
72
73
    copyright(file);
    fprintf(file, "\n"
sletz's avatar
sletz committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
            "usage: jackdmp [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
            "               [ --name OR -n server-name ]\n"
            "               [ --timeout OR -t client-timeout-in-msecs ]\n"
            "               [ --loopback OR -L loopback-port-number ]\n"
            "               [ --verbose OR -v ]\n"
            "               [ --replace-registry OR -r ]\n"
            "               [ --silent OR -s ]\n"
            "               [ --sync OR -S ]\n"
            "               [ --temporary OR -T ]\n"
            "               [ --version OR -V ]\n"
            "         -d driver [ ... driver args ... ]\n"
            "             where driver can be `alsa', `coreaudio', 'portaudio' or `dummy'\n"
            "       jackdmp -d driver --help\n"
            "             to display options for each driver\n\n");
sletz's avatar
sletz committed
88
89
90
91
92
93
94
95
96
97
}


static void DoNothingHandler(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];
sletz's avatar
sletz committed
98
    snprintf(buf, sizeof(buf), "received signal %d during shutdown(ignored)\n", sig);
sletz's avatar
sletz committed
99
100
101
    write(1, buf, strlen(buf));
}

102
static int JackStart(const char* server_name, jack_driver_desc_t* driver_desc, JSList* driver_params, int sync, int temporary, int time_out_ms, int rt, int priority, int loopback, int verbose)
sletz's avatar
sletz committed
103
{
sletz's avatar
sletz committed
104
    jack_log("Jackdmp: sync = %ld timeout = %ld rt = %ld priority = %ld verbose = %ld ", sync, time_out_ms, rt, priority, verbose);
105
    fServer = new JackServer(sync, temporary, time_out_ms, rt, priority, loopback, verbose, server_name);
sletz's avatar
sletz committed
106
107
108
109
110
111
112
113
    int res = fServer->Open(driver_desc, driver_params);
    return (res < 0) ? res : fServer->Start();
}

static int JackStop()
{
    fServer->Stop();
    fServer->Close();
sletz's avatar
sletz committed
114
    jack_log("Jackdmp: server close");
sletz's avatar
sletz committed
115
    delete fServer;
sletz's avatar
sletz committed
116
    jack_log("Jackdmp: delete server");
sletz's avatar
sletz committed
117
118
119
120
121
122
    return 0;
}

static int JackDelete()
{
    delete fServer;
sletz's avatar
sletz committed
123
    jack_log("Jackdmp: delete server");
sletz's avatar
sletz committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
    return 0;
}

static void FilterSIGPIPE()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGPIPE);
    //sigprocmask(SIG_BLOCK, &set, 0);
    pthread_sigmask(SIG_BLOCK, &set, 0);
}

int main(int argc, char* argv[])
{
    int sig;
    sigset_t allsignals;
    struct sigaction action;
    int waiting;

    jack_driver_desc_t* driver_desc;
144
    const char *options = "-ad:P:uvrshVRL:STFl:t:mn:";
sletz's avatar
sletz committed
145
    struct option long_options[] = {
146
                                       { "driver", 1, 0, 'd'},
sletz's avatar
sletz committed
147
148
149
150
151
152
153
                                       { "verbose", 0, 0, 'v' },
                                       { "help", 0, 0, 'h' },
                                       { "port-max", 1, 0, 'p' },
                                       { "no-mlock", 0, 0, 'm' },
                                       { "name", 0, 0, 'n' },
                                       { "unlock", 0, 0, 'u' },
                                       { "realtime", 0, 0, 'R' },
sletz's avatar
sletz committed
154
                                       { "replace-registry", 0, 0, 'r' },
sletz's avatar
sletz committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
                                       { "loopback", 0, 0, 'L' },
                                       { "realtime-priority", 1, 0, 'P' },
                                       { "timeout", 1, 0, 't' },
                                       { "temporary", 0, 0, 'T' },
                                       { "version", 0, 0, 'V' },
                                       { "silent", 0, 0, 's' },
                                       { "sync", 0, 0, 'S' },
                                       { 0, 0, 0, 0 }
                                   };
    int opt = 0;
    int option_index = 0;
    int seen_driver = 0;
    char *driver_name = NULL;
    char **driver_args = NULL;
    JSList* driver_params;
    int driver_nargs = 1;
    int show_version = 0;
sletz's avatar
sletz committed
172
    int replace_registry = 0;
sletz's avatar
sletz committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    int sync = 0;
    int rc, i;

    opterr = 0;
    while (!seen_driver &&
            (opt = getopt_long(argc, argv, options,
                               long_options, &option_index)) != EOF) {
        switch (opt) {

            case 'd':
                seen_driver = 1;
                driver_name = optarg;
                break;

            case 'v':
sletz's avatar
sletz committed
188
                jack_verbose = 1;
sletz's avatar
sletz committed
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
                break;

            case 's':
                jack_set_error_function(silent_jack_error_callback);
                break;

            case 'S':
                sync = 1;
                break;

            case 'n':
                server_name = optarg;
                break;

            case 'm':
                do_mlock = 0;
                break;

            case 'P':
                realtime_priority = atoi(optarg);
                break;
sletz's avatar
sletz committed
210
211
212
213
214

            case 'r':
                replace_registry = 1;
                break;

sletz's avatar
sletz committed
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
            case 'R':
                realtime = 1;
                break;

            case 'L':
                loopback = atoi(optarg);
                break;

            case 'T':
                temporary = 1;
                break;

            case 't':
                client_timeout = atoi(optarg);
                break;

            case 'u':
                do_unlock = 1;
                break;

            case 'V':
                show_version = 1;
                break;

            default:
                fprintf(stderr, "unknown option character %c\n",
                        optopt);
                /*fallthru*/
            case 'h':
                usage(stdout);
                return -1;
        }
    }

249
250
251
   if (show_version) {
    	printf("jackdmp version " VERSION 
               "\n");
sletz's avatar
sletz committed
252
253
    	return -1;
    }
254
 
sletz's avatar
sletz committed
255
    if (!seen_driver) {
sletz's avatar
sletz committed
256
257
        usage(stderr);
        exit(1);
sletz's avatar
sletz committed
258
259
    }

sletz's avatar
sletz committed
260
    drivers = jack_drivers_load(drivers);
sletz's avatar
sletz committed
261
    if (!drivers) {
sletz's avatar
sletz committed
262
263
        fprintf(stderr, "jackdmp: no drivers found; exiting\n");
        exit(1);
sletz's avatar
sletz committed
264
265
    }

sletz's avatar
sletz committed
266
    driver_desc = jack_find_driver_descriptor(drivers, driver_name);
sletz's avatar
sletz committed
267
    if (!driver_desc) {
sletz's avatar
sletz committed
268
269
        fprintf(stderr, "jackdmp: unknown driver '%s'\n", driver_name);
        exit(1);
sletz's avatar
sletz committed
270
271
272
273
274
275
276
277
278
    }

    if (optind < argc) {
        driver_nargs = 1 + argc - optind;
    } else {
        driver_nargs = 1;
    }

    if (driver_nargs == 0) {
sletz's avatar
sletz committed
279
        fprintf(stderr, "No driver specified ... hmm. JACK won't do"
sletz's avatar
sletz committed
280
                " anything when run like this.\n");
sletz's avatar
sletz committed
281
282
283
        return -1;
    }

sletz's avatar
sletz committed
284
    driver_args = (char **) malloc(sizeof(char *) * driver_nargs);
sletz's avatar
sletz committed
285
286
287
288
289
290
    driver_args[0] = driver_name;

    for (i = 1; i < driver_nargs; i++) {
        driver_args[i] = argv[optind++];
    }

sletz's avatar
sletz committed
291
    if (jack_parse_driver_params(driver_desc, driver_nargs,
sletz's avatar
sletz committed
292
                                 driver_args, &driver_params)) {
sletz's avatar
sletz committed
293
        exit(0);
sletz's avatar
sletz committed
294
295
296
    }

    if (server_name == NULL)
sletz's avatar
sletz committed
297
        server_name = (char*)JackTools::DefaultServerName();
sletz's avatar
sletz committed
298

sletz's avatar
sletz committed
299
    copyright(stdout);
sletz's avatar
sletz committed
300

301
    rc = jack_register_server(server_name, replace_registry);
sletz's avatar
sletz committed
302
303
    switch (rc) {
        case EEXIST:
sletz's avatar
sletz committed
304
305
            fprintf(stderr, "`%s' server already active\n", server_name);
            exit(1);
sletz's avatar
sletz committed
306
        case ENOSPC:
sletz's avatar
sletz committed
307
308
            fprintf(stderr, "too many servers already active\n");
            exit(2);
sletz's avatar
sletz committed
309
        case ENOMEM:
sletz's avatar
sletz committed
310
311
            fprintf(stderr, "no access to shm registry\n");
            exit(3);
sletz's avatar
sletz committed
312
        default:
sletz's avatar
sletz committed
313
            if (jack_verbose)
sletz's avatar
Cleanup    
sletz committed
314
                fprintf(stderr, "server `%s' registered\n", server_name);
sletz's avatar
sletz committed
315
316
317
318
319
    }

    /* clean up shared memory and files from any previous
     * instance of this server name */
    jack_cleanup_shm();
320
    JackTools::CleanupFiles(server_name);
sletz's avatar
sletz committed
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341

    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    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

    FilterSIGPIPE();
    pthread_sigmask(SIG_BLOCK, &signals, 0);

    if (!realtime && client_timeout == 0)
        client_timeout = 500; /* 0.5 sec; usable when non realtime. */

342
    int res = JackStart(server_name, driver_desc, driver_params, sync, temporary, client_timeout, realtime, realtime_priority, loopback, jack_verbose);
sletz's avatar
sletz committed
343
344
345
346
347
    if (res < 0) {
        jack_error("Cannot start server... exit");
        JackDelete();
        return 0;
    }
348
349

#ifdef __APPLE__
sletz's avatar
sletz committed
350
    CFStringRef ref = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman);
351
    // Send notification to be used in the JackRouter plugin
352
    CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(),
sletz's avatar
sletz committed
353
354
355
356
357
            CFSTR("com.grame.jackserver.start"),
            ref,
            NULL,
            kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions);
    CFRelease(ref);
358
#endif
sletz's avatar
sletz committed
359

sletz's avatar
sletz committed
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
    // install a do-nothing handler because otherwise pthreads
    // behaviour is undefined when we enter sigwait.

    sigfillset(&allsignals);
    action.sa_handler = DoNothingHandler;
    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);
        }
    }

    waiting = TRUE;

    while (waiting) {
        sigwait(&signals, &sig);
        fprintf(stderr, "jack main caught signal %d\n", sig);

        switch (sig) {
            case SIGUSR1:
                //jack_dump_configuration(engine, 1);
                break;
            case SIGUSR2:
                // driver exit
                waiting = FALSE;
                break;
            default:
                waiting = FALSE;
                break;
        }
    }

    if (sig != SIGSEGV) {
        // unblock signals so we can see them during shutdown.
        // this will help prod developers not to lose sight of
        // bugs that cause segfaults etc. during shutdown.
        sigprocmask(SIG_UNBLOCK, &signals, 0);
    }
sletz's avatar
sletz committed
400
401

    JackStop();
sletz's avatar
sletz committed
402
403

    jack_cleanup_shm();
404
    JackTools::CleanupFiles(server_name);
sletz's avatar
sletz committed
405
406
    jack_unregister_server(server_name);

407
#ifdef __APPLE__
sletz's avatar
sletz committed
408
    CFStringRef ref1 = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman);
409
    // Send notification to be used in the JackRouter plugin
410
    CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(),
sletz's avatar
sletz committed
411
412
413
414
415
            CFSTR("com.grame.jackserver.stop"),
            ref1,
            NULL,
            kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions);
    CFRelease(ref1);
416
417
#endif

sletz's avatar
sletz committed
418
419
    return 1;
}