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.

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