jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

830 lines
20KB

  1. /* -*- Mode: C ; c-basic-offset: 4 -*- */
  2. /*
  3. Copyright (C) 2007,2008 Nedko Arnaudov
  4. Copyright (C) 2007-2008 Juuso Alasuutari
  5. Copyright (C) 2008 Marc-Olivier Barre
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. */
  17. #include <stdbool.h>
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <errno.h>
  22. #include <sys/stat.h>
  23. #include <signal.h>
  24. #include <dbus/dbus.h>
  25. #include <pthread.h>
  26. #include <unistd.h>
  27. #include "config.h"
  28. #include "jackdbus.h"
  29. #include "controller.h"
  30. #include "jack/jack.h"
  31. #include "jack/jslist.h"
  32. #include "jack/control.h"
  33. #include "sigsegv.h"
  34. FILE *g_logfile;
  35. char *g_jackdbus_config_dir;
  36. size_t g_jackdbus_config_dir_len; /* without terminating '\0' char */
  37. char *g_jackdbus_log_dir;
  38. size_t g_jackdbus_log_dir_len; /* without terminating '\0' char */
  39. int g_exit_command;
  40. DBusConnection *g_connection;
  41. void
  42. jack_dbus_send_signal(
  43. const char *sender_object_path,
  44. const char *iface,
  45. const char *signal_name,
  46. int first_arg_type,
  47. ...)
  48. {
  49. DBusMessage *message_ptr;
  50. va_list ap;
  51. va_start(ap, first_arg_type);
  52. message_ptr = dbus_message_new_signal(sender_object_path, iface, signal_name);
  53. if (message_ptr == NULL)
  54. {
  55. jack_error("dbus_message_new_signal() failed.");
  56. goto exit;
  57. }
  58. if (!dbus_message_append_args_valist(message_ptr, first_arg_type, ap))
  59. {
  60. jack_error("dbus_message_append_args_valist() failed.");
  61. goto unref;
  62. }
  63. /* Add message to outgoing message queue */
  64. if (!dbus_connection_send(g_connection, message_ptr, NULL))
  65. {
  66. jack_error("dbus_connection_send() failed.");
  67. goto unref;
  68. }
  69. unref:
  70. dbus_message_unref(message_ptr);
  71. exit:
  72. va_end(ap);
  73. }
  74. /*
  75. * Send a method return.
  76. *
  77. * If call->reply is NULL (i.e. a message construct method failed
  78. * due to lack of memory) attempt to send a void method return.
  79. */
  80. static
  81. void
  82. jack_dbus_send_method_return(
  83. struct jack_dbus_method_call * call)
  84. {
  85. if (call->reply)
  86. {
  87. retry_send:
  88. if (!dbus_connection_send (call->connection, call->reply, NULL))
  89. {
  90. jack_error ("Ran out of memory trying to queue method return");
  91. }
  92. dbus_connection_flush (call->connection);
  93. dbus_message_unref (call->reply);
  94. call->reply = NULL;
  95. }
  96. else
  97. {
  98. jack_error ("send_method_return() called with a NULL message,"
  99. " trying to construct a void return...");
  100. if ((call->reply = dbus_message_new_method_return (call->message)))
  101. {
  102. goto retry_send;
  103. }
  104. else
  105. {
  106. jack_error ("Failed to construct method return!");
  107. }
  108. }
  109. }
  110. #define object_ptr ((struct jack_dbus_object_descriptor *)data)
  111. /*
  112. * The D-Bus message handler for object path /org/jackaudio/Controller.
  113. */
  114. DBusHandlerResult
  115. jack_dbus_message_handler(
  116. DBusConnection *connection,
  117. DBusMessage *message,
  118. void *data)
  119. {
  120. struct jack_dbus_method_call call;
  121. const char *interface_name;
  122. struct jack_dbus_interface_descriptor ** interface_ptr_ptr;
  123. /* Check if the message is a method call. If not, ignore it. */
  124. if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
  125. {
  126. goto handled;
  127. }
  128. /* Get the invoked method's name and make sure it's non-NULL. */
  129. if (!(call.method_name = dbus_message_get_member (message)))
  130. {
  131. jack_dbus_error(
  132. &call,
  133. JACK_DBUS_ERROR_UNKNOWN_METHOD,
  134. "Received method call with empty method name");
  135. goto send_return;
  136. }
  137. /* Initialize our data. */
  138. call.context = object_ptr->context;
  139. call.connection = connection;
  140. call.message = message;
  141. call.reply = NULL;
  142. /* Check if there's an interface specified for this method call. */
  143. interface_name = dbus_message_get_interface (message);
  144. if (interface_name != NULL)
  145. {
  146. /* Check if we can match the interface and method.
  147. * The inteface handler functions only return false if the
  148. * method name was unknown, otherwise they run the specified
  149. * method and return TRUE.
  150. */
  151. interface_ptr_ptr = object_ptr->interfaces;
  152. while (*interface_ptr_ptr != NULL)
  153. {
  154. if (strcmp(interface_name, (*interface_ptr_ptr)->name) == 0)
  155. {
  156. if (!(*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
  157. {
  158. break;
  159. }
  160. goto send_return;
  161. }
  162. interface_ptr_ptr++;
  163. }
  164. }
  165. else
  166. {
  167. /* No interface was specified so we have to try them all. This is
  168. * dictated by the D-Bus specification which states that method calls
  169. * omitting the interface must never be rejected.
  170. */
  171. interface_ptr_ptr = object_ptr->interfaces;
  172. while (*interface_ptr_ptr != NULL)
  173. {
  174. if ((*interface_ptr_ptr)->handler(&call, (*interface_ptr_ptr)->methods))
  175. {
  176. goto send_return;
  177. }
  178. interface_ptr_ptr++;
  179. }
  180. }
  181. jack_dbus_error(
  182. &call,
  183. JACK_DBUS_ERROR_UNKNOWN_METHOD,
  184. "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist",
  185. call.method_name,
  186. dbus_message_get_signature(message),
  187. interface_name);
  188. send_return:
  189. jack_dbus_send_method_return(&call);
  190. handled:
  191. return DBUS_HANDLER_RESULT_HANDLED;
  192. }
  193. void
  194. jack_dbus_message_handler_unregister(
  195. DBusConnection *connection,
  196. void *data)
  197. {
  198. jack_info ("Message handler was unregistered");
  199. }
  200. #undef object_ptr
  201. /*
  202. * Check if the supplied method name exists in org.jackaudio.JackConfigure,
  203. * if it does execute it and return TRUE. Otherwise return FALSE.
  204. */
  205. bool
  206. jack_dbus_run_method(
  207. struct jack_dbus_method_call *call,
  208. const struct jack_dbus_interface_method_descriptor * methods)
  209. {
  210. const struct jack_dbus_interface_method_descriptor * method_ptr;
  211. method_ptr = methods;
  212. while (method_ptr->name != NULL)
  213. {
  214. if (strcmp(call->method_name, method_ptr->name) == 0)
  215. {
  216. method_ptr->handler(call);
  217. return TRUE;
  218. }
  219. method_ptr++;
  220. }
  221. return FALSE;
  222. }
  223. /*
  224. * Read arguments from a method call.
  225. * If the operation fails construct an error and return false,
  226. * otherwise return true.
  227. */
  228. bool
  229. jack_dbus_get_method_args(
  230. struct jack_dbus_method_call *call,
  231. int type,
  232. ...)
  233. {
  234. va_list args;
  235. DBusError error;
  236. bool retval = true;
  237. va_start (args, type);
  238. dbus_error_init (&error);
  239. if (!dbus_message_get_args_valist (call->message, &error, type, args))
  240. {
  241. jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
  242. "Invalid arguments to method \"%s\"",
  243. call->method_name);
  244. retval = false;
  245. }
  246. dbus_error_free (&error);
  247. va_end (args);
  248. return retval;
  249. }
  250. /*
  251. * Read a string and a variant argument from a method call.
  252. * If the operation fails construct an error and return false,
  253. * otherwise return true.
  254. */
  255. bool
  256. jack_dbus_get_method_args_string_and_variant(
  257. struct jack_dbus_method_call *call,
  258. const char **arg1,
  259. message_arg_t *arg2,
  260. int *type_ptr)
  261. {
  262. DBusMessageIter iter, sub_iter;
  263. /* First we want a string... */
  264. if (dbus_message_iter_init (call->message, &iter)
  265. && dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING)
  266. {
  267. dbus_message_iter_get_basic (&iter, arg1);
  268. dbus_message_iter_next (&iter);
  269. /* ...and then a variant. */
  270. if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT)
  271. {
  272. dbus_message_iter_recurse (&iter, &sub_iter);
  273. dbus_message_iter_get_basic (&sub_iter, arg2);
  274. *type_ptr = dbus_message_iter_get_arg_type (&sub_iter);
  275. /* Got what we wanted. */
  276. return true;
  277. }
  278. }
  279. jack_dbus_error (call, JACK_DBUS_ERROR_INVALID_ARGS,
  280. "Invalid arguments to method \"%s\"",
  281. call->method_name);
  282. return false;
  283. }
  284. /*
  285. * Append a variant type to a D-Bus message.
  286. * Return false if something fails, true otherwise.
  287. */
  288. bool
  289. jack_dbus_message_append_variant(
  290. DBusMessageIter *iter,
  291. int type,
  292. const char *signature,
  293. message_arg_t *arg)
  294. {
  295. DBusMessageIter sub_iter;
  296. /* Open a variant container. */
  297. if (!dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, signature, &sub_iter))
  298. {
  299. goto fail;
  300. }
  301. /* Append the supplied value. */
  302. if (!dbus_message_iter_append_basic (&sub_iter, type, (const void *) arg))
  303. {
  304. dbus_message_iter_close_container (iter, &sub_iter);
  305. goto fail;
  306. }
  307. /* Close the container. */
  308. if (!dbus_message_iter_close_container (iter, &sub_iter))
  309. {
  310. goto fail;
  311. }
  312. return true;
  313. fail:
  314. return false;
  315. }
  316. /*
  317. * Construct an empty method return message.
  318. *
  319. * The operation can only fail due to lack of memory, in which case
  320. * there's no sense in trying to construct an error return. Instead,
  321. * call->reply will be set to NULL and handled in send_method_return().
  322. */
  323. void
  324. jack_dbus_construct_method_return_empty(
  325. struct jack_dbus_method_call * call)
  326. {
  327. call->reply = dbus_message_new_method_return (call->message);
  328. if (call->reply == NULL)
  329. {
  330. jack_error ("Ran out of memory trying to construct method return");
  331. }
  332. }
  333. /*
  334. * Construct a method return which holds a single argument or, if
  335. * the type parameter is DBUS_TYPE_INVALID, no arguments at all
  336. * (a void message).
  337. *
  338. * The operation can only fail due to lack of memory, in which case
  339. * there's no sense in trying to construct an error return. Instead,
  340. * call->reply will be set to NULL and handled in send_method_return().
  341. */
  342. void
  343. jack_dbus_construct_method_return_single(
  344. struct jack_dbus_method_call *call,
  345. int type,
  346. message_arg_t arg)
  347. {
  348. DBusMessageIter iter;
  349. call->reply = dbus_message_new_method_return (call->message);
  350. if (call->reply == NULL)
  351. {
  352. goto fail_no_mem;
  353. }
  354. /* Void method return requested by caller. */
  355. if (type == DBUS_TYPE_INVALID)
  356. {
  357. return;
  358. }
  359. /* Prevent crash on NULL input string. */
  360. else if (type == DBUS_TYPE_STRING && arg.string == NULL)
  361. {
  362. arg.string = "";
  363. }
  364. dbus_message_iter_init_append (call->reply, &iter);
  365. if (!dbus_message_iter_append_basic (&iter, type, (const void *) &arg))
  366. {
  367. dbus_message_unref (call->reply);
  368. call->reply = NULL;
  369. goto fail_no_mem;
  370. }
  371. return;
  372. fail_no_mem:
  373. jack_error ("Ran out of memory trying to construct method return");
  374. }
  375. /*
  376. * Construct a method return which holds an array of strings.
  377. *
  378. * The operation can only fail due to lack of memory, in which case
  379. * there's no sense in trying to construct an error return. Instead,
  380. * call->reply will be set to NULL and handled in send_method_return().
  381. */
  382. void
  383. jack_dbus_construct_method_return_array_of_strings(
  384. struct jack_dbus_method_call *call,
  385. unsigned int num_members,
  386. const char **array)
  387. {
  388. DBusMessageIter iter, sub_iter;
  389. unsigned int i;
  390. call->reply = dbus_message_new_method_return (call->message);
  391. if (!call->reply)
  392. {
  393. goto fail;
  394. }
  395. dbus_message_iter_init_append (call->reply, &iter);
  396. if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub_iter))
  397. {
  398. goto fail_unref;
  399. }
  400. for (i = 0; i < num_members; ++i)
  401. {
  402. if (!dbus_message_iter_append_basic (&sub_iter, DBUS_TYPE_STRING, (const void *) &array[i]))
  403. {
  404. dbus_message_iter_close_container (&iter, &sub_iter);
  405. goto fail_unref;
  406. }
  407. }
  408. if (!dbus_message_iter_close_container (&iter, &sub_iter))
  409. {
  410. goto fail_unref;
  411. }
  412. return;
  413. fail_unref:
  414. dbus_message_unref (call->reply);
  415. call->reply = NULL;
  416. fail:
  417. jack_error ("Ran out of memory trying to construct method return");
  418. }
  419. void
  420. jack_dbus_info_callback(const char *msg)
  421. {
  422. time_t timestamp;
  423. char timestamp_str[26];
  424. time(&timestamp);
  425. ctime_r(&timestamp, timestamp_str);
  426. timestamp_str[24] = 0;
  427. fprintf(g_logfile, "%s: %s\n", timestamp_str, msg);
  428. fflush(g_logfile);
  429. }
  430. #define ANSI_BOLD_ON "\033[1m"
  431. #define ANSI_BOLD_OFF "\033[22m"
  432. #define ANSI_COLOR_RED "\033[31m"
  433. #define ANSI_RESET "\033[0m"
  434. void
  435. jack_dbus_error_callback(const char *msg)
  436. {
  437. time_t timestamp;
  438. char timestamp_str[26];
  439. time(&timestamp);
  440. ctime_r(&timestamp, timestamp_str);
  441. timestamp_str[24] = 0;
  442. fprintf(g_logfile, "%s: " ANSI_BOLD_ON ANSI_COLOR_RED "ERROR: %s" ANSI_RESET "\n", timestamp_str, msg);
  443. fflush(g_logfile);
  444. }
  445. bool
  446. ensure_dir_exist(const char *dirname, int mode)
  447. {
  448. struct stat st;
  449. if (stat(dirname, &st) != 0)
  450. {
  451. if (errno == ENOENT)
  452. {
  453. printf("Directory \"%s\" does not exist. Creating...\n", dirname);
  454. if (mkdir(dirname, mode) != 0)
  455. {
  456. fprintf(stderr, "Failed to create \"%s\" directory: %d (%s)\n", dirname, errno, strerror(errno));
  457. return false;
  458. }
  459. }
  460. else
  461. {
  462. fprintf(stderr, "Failed to stat \"%s\": %d (%s)\n", dirname, errno, strerror(errno));
  463. return false;
  464. }
  465. }
  466. else
  467. {
  468. if (!S_ISDIR(st.st_mode))
  469. {
  470. fprintf(stderr, "\"%s\" exists but is not directory.\n", dirname);
  471. return false;
  472. }
  473. }
  474. return true;
  475. }
  476. char *
  477. pathname_cat(const char *pathname_a, const char *pathname_b)
  478. {
  479. char *pathname;
  480. int pathname_a_len, pathname_b_len, pathname_len;
  481. pathname_a_len = strlen(pathname_a);
  482. pathname_b_len = strlen(pathname_b);
  483. pathname = malloc(pathname_a_len + pathname_b_len + 1);
  484. if (pathname == NULL)
  485. {
  486. fprintf(stderr, "Out of memory\n");
  487. return NULL;
  488. }
  489. memcpy(pathname, pathname_a, pathname_a_len);
  490. memcpy(pathname + pathname_a_len, pathname_b, pathname_b_len);
  491. pathname_len = pathname_a_len + pathname_b_len;
  492. pathname[pathname_len] = 0;
  493. return pathname;
  494. }
  495. bool
  496. paths_init()
  497. {
  498. const char *home_dir, *xdg_config_home, *xdg_log_home;
  499. home_dir = getenv("HOME");
  500. if (home_dir == NULL)
  501. {
  502. fprintf(stderr, "Environment variable HOME not set\n");
  503. goto fail;
  504. }
  505. xdg_config_home = getenv("XDG_CONFIG_HOME");
  506. if (xdg_config_home == NULL)
  507. {
  508. if (!(xdg_config_home = pathname_cat(home_dir, DEFAULT_XDG_CONFIG))) goto fail;
  509. }
  510. if (!(xdg_log_home = pathname_cat(home_dir, DEFAULT_XDG_LOG))) goto fail;
  511. if (!(g_jackdbus_config_dir = pathname_cat(xdg_config_home, JACKDBUS_DIR))) goto fail;
  512. if (!(g_jackdbus_log_dir = pathname_cat(xdg_log_home, JACKDBUS_DIR))) goto fail;
  513. if (!ensure_dir_exist(xdg_config_home, 0700))
  514. {
  515. goto fail;
  516. }
  517. if (!ensure_dir_exist(xdg_log_home, 0700))
  518. {
  519. goto fail;
  520. }
  521. if (!ensure_dir_exist(g_jackdbus_config_dir, 0700))
  522. {
  523. free(g_jackdbus_config_dir);
  524. goto fail;
  525. }
  526. g_jackdbus_config_dir_len = strlen(g_jackdbus_config_dir);
  527. if (!ensure_dir_exist(g_jackdbus_log_dir, 0700))
  528. {
  529. free(g_jackdbus_log_dir);
  530. goto fail;
  531. }
  532. g_jackdbus_log_dir_len = strlen(g_jackdbus_log_dir);
  533. return true;
  534. fail:
  535. return false;
  536. }
  537. void
  538. paths_uninit()
  539. {
  540. free(g_jackdbus_config_dir);
  541. free(g_jackdbus_log_dir);
  542. }
  543. int
  544. log_init()
  545. {
  546. char *log_filename;
  547. size_t log_len;
  548. log_len = strlen(JACKDBUS_LOG);
  549. log_filename = malloc(g_jackdbus_log_dir_len + log_len + 1);
  550. if (log_filename == NULL)
  551. {
  552. fprintf(stderr, "Out of memory\n");
  553. return FALSE;
  554. }
  555. memcpy(log_filename, g_jackdbus_log_dir, g_jackdbus_log_dir_len);
  556. memcpy(log_filename + g_jackdbus_log_dir_len, JACKDBUS_LOG, log_len);
  557. log_filename[g_jackdbus_log_dir_len + log_len] = 0;
  558. g_logfile = fopen(log_filename, "a");
  559. if (g_logfile == NULL)
  560. {
  561. fprintf(stderr, "Cannot open jackdbus log file \"%s\": %d (%s)\n", log_filename, errno, strerror(errno));
  562. free(log_filename);
  563. return FALSE;
  564. }
  565. free(log_filename);
  566. return TRUE;
  567. }
  568. void
  569. log_uninit()
  570. {
  571. fclose(g_logfile);
  572. }
  573. void
  574. jack_dbus_error(
  575. void *dbus_call_context_ptr,
  576. const char *error_name,
  577. const char *format,
  578. ...)
  579. {
  580. va_list ap;
  581. char buffer[300];
  582. va_start(ap, format);
  583. vsnprintf(buffer, sizeof(buffer), format, ap);
  584. jack_error_callback(buffer);
  585. if (dbus_call_context_ptr != NULL)
  586. {
  587. ((struct jack_dbus_method_call *)dbus_call_context_ptr)->reply = dbus_message_new_error(
  588. ((struct jack_dbus_method_call *)dbus_call_context_ptr)->message,
  589. error_name,
  590. buffer);
  591. }
  592. va_end(ap);
  593. }
  594. int
  595. main (int argc, char **argv)
  596. {
  597. DBusError error;
  598. int ret;
  599. void *controller_ptr;
  600. struct stat st;
  601. char timestamp_str[26];
  602. st.st_mtime = 0;
  603. stat(argv[0], &st);
  604. ctime_r(&st.st_mtime, timestamp_str);
  605. timestamp_str[24] = 0;
  606. if (!jack_controller_settings_init())
  607. {
  608. ret = 1;
  609. goto fail;
  610. }
  611. if (argc != 2 || strcmp(argv[1], "auto") != 0)
  612. {
  613. ret = 0;
  614. fprintf(
  615. stderr,
  616. "jackdbus should be auto-executed by D-Bus message bus daemon.\n"
  617. "If you want to run it manually anyway, specify \"auto\" as only parameter\n");
  618. goto fail_uninit_xml;
  619. }
  620. if (!paths_init())
  621. {
  622. ret = 1;
  623. goto fail_uninit_xml;
  624. }
  625. if (!log_init())
  626. {
  627. ret = 1;
  628. goto fail_uninit_paths;
  629. }
  630. #if !defined(DISABLE_SIGNAL_MAGIC)
  631. jackctl_setup_signals(0);
  632. #endif
  633. jack_set_error_function(jack_dbus_error_callback);
  634. jack_set_info_function(jack_dbus_info_callback);
  635. /* setup our SIGSEGV magic that prints nice stack in our logfile */
  636. setup_sigsegv();
  637. jack_info("------------------");
  638. jack_info("Controller activated. Version %s (%s) built on %s", jack_get_version_string(), JACK_SVNREVISION, timestamp_str);
  639. if (!dbus_threads_init_default())
  640. {
  641. jack_error("dbus_threads_init_default() failed");
  642. ret = 1;
  643. goto fail_uninit_log;
  644. }
  645. dbus_error_init (&error);
  646. g_connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
  647. if (dbus_error_is_set (&error))
  648. {
  649. jack_error("Cannot connect to D-Bus session bus: %s", error.message);
  650. ret = 1;
  651. goto fail_uninit_log;
  652. }
  653. ret = dbus_bus_request_name(
  654. g_connection,
  655. "org.jackaudio.service",
  656. DBUS_NAME_FLAG_DO_NOT_QUEUE,
  657. &error);
  658. if (ret == -1)
  659. {
  660. jack_error("Cannot request service name: %s", error.message);
  661. dbus_error_free(&error);
  662. ret = 1;
  663. goto fail_unref_connection;
  664. }
  665. else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
  666. {
  667. jack_error("Requested D-Bus service name already exists");
  668. ret = 1;
  669. goto fail_unref_connection;
  670. }
  671. controller_ptr = jack_controller_create(g_connection);
  672. if (controller_ptr == NULL)
  673. {
  674. ret = 1;
  675. goto fail_unref_connection;
  676. }
  677. jack_info("Listening for D-Bus messages");
  678. g_exit_command = FALSE;
  679. while (!g_exit_command && dbus_connection_read_write_dispatch (g_connection, 200));
  680. jack_controller_destroy(controller_ptr);
  681. jack_info("Controller deactivated.");
  682. ret = 0;
  683. fail_unref_connection:
  684. dbus_connection_unref(g_connection);
  685. fail_uninit_log:
  686. log_uninit();
  687. fail_uninit_paths:
  688. paths_uninit();
  689. fail_uninit_xml:
  690. jack_controller_settings_uninit();
  691. fail:
  692. return ret;
  693. }