Correct JackInfoShutdownCallback prototype, two new JackClientProcessFailure and...
[jack2.git] / common / JackDriverLoader.cpp
blob428d3ab0929e4c87818a56f2f0dbfb268ceb2646
1 /*
2 Copyright (C) 2001-2005 Paul Davis
3 Copyright (C) 2004-2008 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 "JackSystemDeps.h"
22 #include "JackDriverLoader.h"
23 #include "JackConstants.h"
24 #include "JackError.h"
25 #include <getopt.h>
26 #include <stdio.h>
27 #include <errno.h>
29 #ifndef WIN32
30 #include <dirent.h>
31 #endif
33 jack_driver_desc_t * jackctl_driver_get_desc(jackctl_driver_t * driver);
35 SERVER_EXPORT void jack_print_driver_options (jack_driver_desc_t* desc, FILE* file)
37 unsigned long i;
38 char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1];
40 for (i = 0; i < desc->nparams; i++) {
41 switch (desc->params[i].type) {
42 case JackDriverParamInt:
43 sprintf (arg_default, "%" "i", desc->params[i].value.i);
44 break;
45 case JackDriverParamUInt:
46 sprintf (arg_default, "%" "u", desc->params[i].value.ui);
47 break;
48 case JackDriverParamChar:
49 sprintf (arg_default, "%c", desc->params[i].value.c);
50 break;
51 case JackDriverParamString:
52 if (desc->params[i].value.str && strcmp (desc->params[i].value.str, "") != 0)
53 sprintf (arg_default, "%s", desc->params[i].value.str);
54 else
55 sprintf (arg_default, "none");
56 break;
57 case JackDriverParamBool:
58 sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false");
59 break;
62 fprintf (file, "\t-%c, --%s \t%s (default: %s)\n",
63 desc->params[i].character,
64 desc->params[i].name,
65 desc->params[i].long_desc,
66 arg_default);
70 static void
71 jack_print_driver_param_usage (jack_driver_desc_t * desc, unsigned long param, FILE *file)
73 fprintf (file, "Usage information for the '%s' parameter for driver '%s':\n",
74 desc->params[param].name, desc->name);
75 fprintf (file, "%s\n", desc->params[param].long_desc);
78 SERVER_EXPORT void jack_free_driver_params(JSList * driver_params)
80 JSList *node_ptr = driver_params;
81 JSList *next_node_ptr;
83 while (node_ptr) {
84 next_node_ptr = node_ptr->next;
85 free(node_ptr->data);
86 free(node_ptr);
87 node_ptr = next_node_ptr;
91 int
92 jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char* argv[], JSList ** param_ptr)
94 struct option * long_options;
95 char * options, * options_ptr;
96 unsigned long i;
97 int opt;
98 unsigned int param_index;
99 JSList * params = NULL;
100 jack_driver_param_t * driver_param;
102 if (argc <= 1) {
103 *param_ptr = NULL;
104 return 0;
107 /* check for help */
108 if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) {
109 if (argc > 2) {
110 for (i = 0; i < desc->nparams; i++) {
111 if (strcmp (desc->params[i].name, argv[2]) == 0) {
112 jack_print_driver_param_usage (desc, i, stdout);
113 return 1;
117 fprintf (stderr, "jackd: unknown option '%s' "
118 "for driver '%s'\n", argv[2],
119 desc->name);
122 printf ("Parameters for driver '%s' (all parameters are optional):\n", desc->name);
123 jack_print_driver_options (desc, stdout);
124 return 1;
127 /* set up the stuff for getopt */
128 options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char));
129 long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option));
131 options_ptr = options;
132 for (i = 0; i < desc->nparams; i++) {
133 sprintf (options_ptr, "%c::", desc->params[i].character);
134 options_ptr += 3;
135 long_options[i].name = desc->params[i].name;
136 long_options[i].flag = NULL;
137 long_options[i].val = desc->params[i].character;
138 long_options[i].has_arg = optional_argument;
141 /* create the params */
142 optind = 0;
143 opterr = 0;
144 while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) {
146 if (opt == ':' || opt == '?') {
147 if (opt == ':') {
148 fprintf (stderr, "Missing option to argument '%c'\n", optopt);
149 } else {
150 fprintf (stderr, "Unknownage with option '%c'\n", optopt);
153 fprintf (stderr, "Options for driver '%s':\n", desc->name);
154 jack_print_driver_options (desc, stderr);
155 exit (1);
158 for (param_index = 0; param_index < desc->nparams; param_index++) {
159 if (opt == desc->params[param_index].character) {
160 break;
164 driver_param = (jack_driver_param_t*)calloc (1, sizeof (jack_driver_param_t));
165 driver_param->character = desc->params[param_index].character;
167 if (!optarg && optind < argc &&
168 strlen(argv[optind]) &&
169 argv[optind][0] != '-') {
170 optarg = argv[optind];
173 if (optarg) {
174 switch (desc->params[param_index].type) {
175 case JackDriverParamInt:
176 driver_param->value.i = atoi (optarg);
177 break;
178 case JackDriverParamUInt:
179 driver_param->value.ui = strtoul (optarg, NULL, 10);
180 break;
181 case JackDriverParamChar:
182 driver_param->value.c = optarg[0];
183 break;
184 case JackDriverParamString:
185 strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX);
186 break;
187 case JackDriverParamBool:
190 if (strcasecmp ("false", optarg) == 0 ||
191 strcasecmp ("off", optarg) == 0 ||
192 strcasecmp ("no", optarg) == 0 ||
193 strcasecmp ("0", optarg) == 0 ||
194 strcasecmp ("(null)", optarg) == 0 ) {
196 // steph
197 if (strcmp ("false", optarg) == 0 ||
198 strcmp ("off", optarg) == 0 ||
199 strcmp ("no", optarg) == 0 ||
200 strcmp ("0", optarg) == 0 ||
201 strcmp ("(null)", optarg) == 0 ) {
202 driver_param->value.i = false;
204 } else {
206 driver_param->value.i = true;
209 break;
211 } else {
212 if (desc->params[param_index].type == JackDriverParamBool) {
213 driver_param->value.i = true;
214 } else {
215 driver_param->value = desc->params[param_index].value;
219 params = jack_slist_append (params, driver_param);
222 free (options);
223 free (long_options);
225 if (param_ptr)
226 *param_ptr = params;
228 return 0;
231 SERVER_EXPORT int
232 jackctl_parse_driver_params (jackctl_driver *driver_ptr, int argc, char* argv[])
234 struct option * long_options;
235 char * options, * options_ptr;
236 unsigned long i;
237 int opt;
238 JSList * node_ptr;
239 jackctl_parameter_t * param = NULL;
240 union jackctl_parameter_value value;
242 if (argc <= 1)
243 return 0;
245 const JSList * driver_params = jackctl_driver_get_parameters(driver_ptr);
246 if (driver_params == NULL)
247 return 1;
249 jack_driver_desc_t * desc = jackctl_driver_get_desc(driver_ptr);
251 /* check for help */
252 if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) {
253 if (argc > 2) {
254 for (i = 0; i < desc->nparams; i++) {
255 if (strcmp (desc->params[i].name, argv[2]) == 0) {
256 jack_print_driver_param_usage (desc, i, stdout);
257 return 1;
261 fprintf (stderr, "jackd: unknown option '%s' "
262 "for driver '%s'\n", argv[2],
263 desc->name);
266 printf ("Parameters for driver '%s' (all parameters are optional):\n", desc->name);
267 jack_print_driver_options (desc, stdout);
268 return 1;
271 /* set up the stuff for getopt */
272 options = (char*)calloc (desc->nparams * 3 + 1, sizeof (char));
273 long_options = (option*)calloc (desc->nparams + 1, sizeof (struct option));
275 options_ptr = options;
276 for (i = 0; i < desc->nparams; i++) {
277 sprintf (options_ptr, "%c::", desc->params[i].character);
278 options_ptr += 3;
279 long_options[i].name = desc->params[i].name;
280 long_options[i].flag = NULL;
281 long_options[i].val = desc->params[i].character;
282 long_options[i].has_arg = optional_argument;
285 /* create the params */
286 optind = 0;
287 opterr = 0;
288 while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) {
290 if (opt == ':' || opt == '?') {
291 if (opt == ':') {
292 fprintf (stderr, "Missing option to argument '%c'\n", optopt);
293 } else {
294 fprintf (stderr, "Unknownage with option '%c'\n", optopt);
297 fprintf (stderr, "Options for driver '%s':\n", desc->name);
298 jack_print_driver_options(desc, stderr);
299 return 1;
302 node_ptr = (JSList *)driver_params;
303 while (node_ptr) {
304 param = (jackctl_parameter_t*)node_ptr->data;
305 if (opt == jackctl_parameter_get_id(param)) {
306 break;
308 node_ptr = node_ptr->next;
311 if (!optarg && optind < argc &&
312 strlen(argv[optind]) &&
313 argv[optind][0] != '-') {
314 optarg = argv[optind];
317 if (optarg) {
318 switch (jackctl_parameter_get_type(param)) {
319 case JackDriverParamInt:
320 value.i = atoi (optarg);
321 jackctl_parameter_set_value(param, &value);
322 break;
323 case JackDriverParamUInt:
324 value.ui = strtoul (optarg, NULL, 10);
325 jackctl_parameter_set_value(param, &value);
326 break;
327 case JackDriverParamChar:
328 value.c = optarg[0];
329 jackctl_parameter_set_value(param, &value);
330 break;
331 case JackDriverParamString:
332 strncpy (value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX);
333 jackctl_parameter_set_value(param, &value);
334 break;
335 case JackDriverParamBool:
337 if (strcasecmp ("false", optarg) == 0 ||
338 strcasecmp ("off", optarg) == 0 ||
339 strcasecmp ("no", optarg) == 0 ||
340 strcasecmp ("0", optarg) == 0 ||
341 strcasecmp ("(null)", optarg) == 0 ) {
343 // steph
344 if (strcmp ("false", optarg) == 0 ||
345 strcmp ("off", optarg) == 0 ||
346 strcmp ("no", optarg) == 0 ||
347 strcmp ("0", optarg) == 0 ||
348 strcmp ("(null)", optarg) == 0 ) {
349 value.i = false;
350 } else {
351 value.i = true;
353 jackctl_parameter_set_value(param, &value);
354 break;
356 } else {
357 if (jackctl_parameter_get_type(param) == JackParamBool) {
358 value.i = true;
359 } else {
360 value = jackctl_parameter_get_default_value(param);
362 jackctl_parameter_set_value(param, &value);
366 free(options);
367 free(long_options);
368 return 0;
371 jack_driver_desc_t *
372 jack_find_driver_descriptor (JSList * drivers, const char * name)
374 jack_driver_desc_t * desc = 0;
375 JSList * node;
377 for (node = drivers; node; node = jack_slist_next (node)) {
378 desc = (jack_driver_desc_t *) node->data;
380 if (strcmp (desc->name, name) != 0) {
381 desc = NULL;
382 } else {
383 break;
387 return desc;
390 static jack_driver_desc_t *
391 jack_get_descriptor (JSList * drivers, const char * sofile, const char * symbol)
393 jack_driver_desc_t * descriptor, * other_descriptor;
394 JackDriverDescFunction so_get_descriptor = NULL;
395 JSList * node;
396 void * dlhandle;
397 char * filename;
398 #ifdef WIN32
399 int dlerr;
400 #else
401 const char * dlerr;
402 #endif
404 int err;
405 const char* driver_dir;
407 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
408 // for WIN32 ADDON_DIR is defined in JackConstants.h as relative path
409 // for posix systems, it is absolute path of default driver dir
410 #ifdef WIN32
411 char temp_driver_dir1[512];
412 char temp_driver_dir2[512];
413 GetCurrentDirectory(512, temp_driver_dir1);
414 sprintf(temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR);
415 driver_dir = temp_driver_dir2;
416 #else
417 driver_dir = ADDON_DIR;
418 #endif
421 filename = (char *)malloc(strlen (driver_dir) + 1 + strlen(sofile) + 1);
422 sprintf (filename, "%s/%s", driver_dir, sofile);
424 if ((dlhandle = LoadDriverModule(filename)) == NULL) {
425 #ifdef WIN32
426 jack_error ("could not open driver .dll '%s': %ld", filename, GetLastError());
427 #else
428 jack_error ("could not open driver .so '%s': %s", filename, dlerror());
429 #endif
431 free(filename);
432 return NULL;
435 so_get_descriptor = (JackDriverDescFunction)GetDriverProc(dlhandle, symbol);
437 #ifdef WIN32
438 if ((so_get_descriptor == NULL) && (dlerr = GetLastError()) != 0) {
439 jack_log("jack_get_descriptor : dll is not a driver, err = %ld", dlerr);
440 #else
441 if ((so_get_descriptor == NULL) && (dlerr = dlerror ()) != NULL) {
442 jack_log("jack_get_descriptor err = %s", dlerr);
443 #endif
445 UnloadDriverModule(dlhandle);
446 free(filename);
447 return NULL;
450 if ((descriptor = so_get_descriptor ()) == NULL) {
451 jack_error("driver from '%s' returned NULL descriptor", filename);
452 UnloadDriverModule(dlhandle);
453 free(filename);
454 return NULL;
457 #ifdef WIN32
458 if ((err = UnloadDriverModule(dlhandle)) == 0) {
459 jack_error ("error closing driver .so '%s': %ld", filename, GetLastError ());
461 #else
462 if ((err = UnloadDriverModule(dlhandle)) != 0) {
463 jack_error ("error closing driver .so '%s': %s", filename, dlerror ());
465 #endif
467 /* check it doesn't exist already */
468 for (node = drivers; node; node = jack_slist_next (node)) {
469 other_descriptor = (jack_driver_desc_t *) node->data;
471 if (strcmp(descriptor->name, other_descriptor->name) == 0) {
472 jack_error("the drivers in '%s' and '%s' both have the name '%s'; using the first",
473 other_descriptor->file, filename, other_descriptor->name);
474 /* FIXME: delete the descriptor */
475 free(filename);
476 return NULL;
480 strncpy(descriptor->file, filename, JACK_PATH_MAX);
481 free(filename);
482 return descriptor;
485 static bool check_symbol(const char* sofile, const char* symbol)
487 void * dlhandle;
488 bool res = false;
489 const char* driver_dir;
491 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
492 // for WIN32 ADDON_DIR is defined in JackConstants.h as relative path
493 // for posix systems, it is absolute path of default driver dir
494 #ifdef WIN32
495 char temp_driver_dir1[512];
496 char temp_driver_dir2[512];
497 GetCurrentDirectory(512, temp_driver_dir1);
498 sprintf(temp_driver_dir2, "%s/%s", temp_driver_dir1, ADDON_DIR);
499 driver_dir = temp_driver_dir2;
500 #else
501 driver_dir = ADDON_DIR;
502 #endif
505 char* filename = (char *)malloc(strlen (driver_dir) + 1 + strlen(sofile) + 1);
506 sprintf (filename, "%s/%s", driver_dir, sofile);
508 if ((dlhandle = LoadDriverModule(filename)) == NULL) {
509 #ifdef WIN32
510 jack_error ("could not open component .dll '%s': %ld", filename, GetLastError());
511 #else
512 jack_error ("could not open component .so '%s': %s", filename, dlerror());
513 #endif
514 } else {
515 res = (GetDriverProc(dlhandle, symbol)) ? true : false;
516 UnloadDriverModule(dlhandle);
519 free(filename);
520 return res;
523 #ifdef WIN32
525 JSList *
526 jack_drivers_load (JSList * drivers) {
527 char * driver_dir;
528 char driver_dir_storage[512];
529 char dll_filename[512];
530 WIN32_FIND_DATA filedata;
531 HANDLE file;
532 const char * ptr = NULL;
533 JSList * driver_list = NULL;
534 jack_driver_desc_t * desc;
536 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
537 // for WIN32 ADDON_DIR is defined in JackConstants.h as relative path
538 GetCurrentDirectory(512, driver_dir_storage);
539 strcat(driver_dir_storage, "/");
540 strcat(driver_dir_storage, ADDON_DIR);
541 driver_dir = driver_dir_storage;
544 sprintf(dll_filename, "%s/*.dll", driver_dir);
546 file = (HANDLE )FindFirstFile(dll_filename, &filedata);
548 if (file == INVALID_HANDLE_VALUE) {
549 jack_error("error invalid handle");
550 return NULL;
553 do {
554 ptr = strrchr (filedata.cFileName, '.');
555 if (!ptr) {
556 continue;
558 ptr++;
559 if (strncmp ("dll", ptr, 3) != 0) {
560 continue;
563 desc = jack_get_descriptor (drivers, filedata.cFileName, "driver_get_descriptor");
564 if (desc) {
565 driver_list = jack_slist_append (driver_list, desc);
568 } while (FindNextFile(file, &filedata));
570 if (!driver_list) {
571 jack_error ("could not find any drivers in %s!", driver_dir);
572 return NULL;
575 return driver_list;
578 #else
580 JSList *
581 jack_drivers_load (JSList * drivers) {
582 struct dirent * dir_entry;
583 DIR * dir_stream;
584 const char * ptr;
585 int err;
586 JSList * driver_list = NULL;
587 jack_driver_desc_t * desc;
589 const char* driver_dir;
590 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
591 driver_dir = ADDON_DIR;
594 /* search through the driver_dir and add get descriptors
595 from the .so files in it */
596 dir_stream = opendir (driver_dir);
597 if (!dir_stream) {
598 jack_error ("could not open driver directory %s: %s",
599 driver_dir, strerror (errno));
600 return NULL;
603 while ((dir_entry = readdir(dir_stream))) {
605 /* check the filename is of the right format */
606 if (strncmp ("jack_", dir_entry->d_name, 5) != 0) {
607 continue;
610 ptr = strrchr (dir_entry->d_name, '.');
611 if (!ptr) {
612 continue;
614 ptr++;
615 if (strncmp ("so", ptr, 2) != 0) {
616 continue;
619 desc = jack_get_descriptor (drivers, dir_entry->d_name, "driver_get_descriptor");
620 if (desc) {
621 driver_list = jack_slist_append (driver_list, desc);
625 err = closedir (dir_stream);
626 if (err) {
627 jack_error ("error closing driver directory %s: %s",
628 driver_dir, strerror (errno));
631 if (!driver_list) {
632 jack_error ("could not find any drivers in %s!", driver_dir);
633 return NULL;
636 return driver_list;
639 #endif
641 #ifdef WIN32
643 JSList *
644 jack_internals_load (JSList * internals) {
645 char * driver_dir;
646 char driver_dir_storage[512];
647 char dll_filename[512];
648 WIN32_FIND_DATA filedata;
649 HANDLE file;
650 const char * ptr = NULL;
651 JSList * driver_list = NULL;
652 jack_driver_desc_t * desc;
654 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
655 // for WIN32 ADDON_DIR is defined in JackConstants.h as relative path
656 GetCurrentDirectory(512, driver_dir_storage);
657 strcat(driver_dir_storage, "/");
658 strcat(driver_dir_storage, ADDON_DIR);
659 driver_dir = driver_dir_storage;
662 sprintf(dll_filename, "%s/*.dll", driver_dir);
664 file = (HANDLE )FindFirstFile(dll_filename, &filedata);
666 if (file == INVALID_HANDLE_VALUE) {
667 jack_error("error");
668 return NULL;
671 do {
673 ptr = strrchr (filedata.cFileName, '.');
674 if (!ptr) {
675 continue;
677 ptr++;
678 if (strncmp ("dll", ptr, 3) != 0) {
679 continue;
682 /* check if dll is an internal client */
683 if (!check_symbol(filedata.cFileName, "jack_internal_initialize")) {
684 continue;
687 desc = jack_get_descriptor (internals, filedata.cFileName, "jack_get_descriptor");
688 if (desc) {
689 driver_list = jack_slist_append (driver_list, desc);
692 } while (FindNextFile(file, &filedata));
694 if (!driver_list) {
695 jack_error ("could not find any internals in %s!", driver_dir);
696 return NULL;
699 return driver_list;
702 #else
704 JSList *
705 jack_internals_load (JSList * internals) {
706 struct dirent * dir_entry;
707 DIR * dir_stream;
708 const char * ptr;
709 int err;
710 JSList * driver_list = NULL;
711 jack_driver_desc_t * desc;
713 const char* driver_dir;
714 if ((driver_dir = getenv("JACK_DRIVER_DIR")) == 0) {
715 driver_dir = ADDON_DIR;
718 /* search through the driver_dir and add get descriptors
719 from the .so files in it */
720 dir_stream = opendir (driver_dir);
721 if (!dir_stream) {
722 jack_error ("could not open driver directory %s: %s\n",
723 driver_dir, strerror (errno));
724 return NULL;
727 while ((dir_entry = readdir(dir_stream))) {
729 ptr = strrchr (dir_entry->d_name, '.');
730 if (!ptr) {
731 continue;
733 ptr++;
734 if (strncmp ("so", ptr, 2) != 0) {
735 continue;
738 /* check if dll is an internal client */
739 if (!check_symbol(dir_entry->d_name, "jack_internal_initialize")) {
740 continue;
743 desc = jack_get_descriptor (internals, dir_entry->d_name, "jack_get_descriptor");
744 if (desc) {
745 driver_list = jack_slist_append (driver_list, desc);
749 err = closedir (dir_stream);
750 if (err) {
751 jack_error ("error closing internal directory %s: %s\n",
752 driver_dir, strerror (errno));
755 if (!driver_list) {
756 jack_error ("could not find any internals in %s!", driver_dir);
757 return NULL;
760 return driver_list;
763 #endif
765 Jack::JackDriverClientInterface* JackDriverInfo::Open(jack_driver_desc_t* driver_desc,
766 Jack::JackLockedEngine* engine,
767 Jack::JackSynchro* synchro,
768 const JSList* params)
770 #ifdef WIN32
771 int errstr;
772 #else
773 const char * errstr;
774 #endif
776 fHandle = LoadDriverModule (driver_desc->file);
778 if (fHandle == NULL) {
779 #ifdef WIN32
780 if ((errstr = GetLastError ()) != 0) {
781 jack_error ("can't load \"%s\": %ld", driver_desc->file, errstr);
782 #else
783 if ((errstr = dlerror ()) != 0) {
784 jack_error ("can't load \"%s\": %s", driver_desc->file, errstr);
785 #endif
787 } else {
788 jack_error ("bizarre error loading driver shared object %s", driver_desc->file);
790 return NULL;
793 fInitialize = (driverInitialize)GetDriverProc(fHandle, "driver_initialize");
795 #ifdef WIN32
796 if ((fInitialize == NULL) && (errstr = GetLastError ()) != 0) {
797 #else
798 if ((fInitialize == NULL) && (errstr = dlerror ()) != 0) {
799 #endif
800 jack_error("no initialize function in shared object %s\n", driver_desc->file);
801 return NULL;
804 fBackend = fInitialize(engine, synchro, params);
805 return fBackend;