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.

574 lines
17KB

  1. /* -*- Mode: C ; c-basic-offset: 4 -*- */
  2. /*
  3. Copyright (C) 2011 Nedko Arnaudov
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #if defined(HAVE_CONFIG_H)
  16. #include "config.h"
  17. #endif
  18. #include <stdint.h>
  19. #include <string.h>
  20. #include <stdio.h>
  21. #include <assert.h>
  22. #include <dbus/dbus.h>
  23. #include "jackdbus.h"
  24. #include "controller_internal.h"
  25. #include "jack/session.h"
  26. #include "common/JackError.h"
  27. #define JACK_DBUS_IFACE_NAME "org.jackaudio.SessionManager"
  28. static
  29. void
  30. jack_controller_control_send_signal_session_state_changed(
  31. jack_session_event_type_t type,
  32. const char * target)
  33. {
  34. dbus_uint32_t u32;
  35. u32 = type;
  36. if (target == NULL)
  37. {
  38. target = "";
  39. }
  40. jack_dbus_send_signal(
  41. JACK_CONTROLLER_OBJECT_PATH,
  42. JACK_DBUS_IFACE_NAME,
  43. "StateChanged",
  44. DBUS_TYPE_UINT32,
  45. &u32,
  46. DBUS_TYPE_STRING,
  47. &target,
  48. DBUS_TYPE_INVALID);
  49. }
  50. static bool start_detached_thread(void * (* start_routine)(void *), void * arg)
  51. {
  52. int ret;
  53. static pthread_attr_t attr;
  54. pthread_t tid;
  55. ret = pthread_attr_init(&attr);
  56. if (ret != 0)
  57. {
  58. jack_error("pthread_attr_init() failed with %d", ret);
  59. goto exit;
  60. }
  61. ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  62. if (ret != 0)
  63. {
  64. jack_error("pthread_attr_setdetachstate() failed with %d", ret);
  65. goto destroy_attr;
  66. }
  67. ret = pthread_create(&tid, &attr, start_routine, arg);
  68. if (ret != 0)
  69. {
  70. jack_error("pthread_create() failed with %d", ret);
  71. goto destroy_attr;
  72. }
  73. jack_log("Detached thread %d created", (int)tid);
  74. destroy_attr:
  75. pthread_attr_destroy(&attr);
  76. exit:
  77. return ret == 0;
  78. }
  79. static void send_session_notify_reply(struct jack_session_pending_command * pending_cmd_ptr, jack_session_command_t * commands)
  80. {
  81. struct jack_dbus_method_call call;
  82. const jack_session_command_t * cmd_ptr;
  83. DBusMessageIter top_iter, array_iter, struct_iter;
  84. dbus_uint32_t u32;
  85. /* jack_dbus_error() wants call struct */
  86. call.message = pending_cmd_ptr->message;
  87. call.connection = pending_cmd_ptr->connection;
  88. if (commands == NULL)
  89. {
  90. jack_dbus_error(&call, JACK_DBUS_ERROR_GENERIC, "jack_session_notify() failed");
  91. goto send_reply;
  92. }
  93. jack_info("Session notify complete, commands follow:");
  94. call.reply = dbus_message_new_method_return(pending_cmd_ptr->message);
  95. if (call.reply == NULL)
  96. {
  97. goto oom;
  98. }
  99. dbus_message_iter_init_append(call.reply, &top_iter);
  100. if (!dbus_message_iter_open_container(&top_iter, DBUS_TYPE_ARRAY, "(sssu)", &array_iter))
  101. {
  102. goto unref;
  103. }
  104. for (cmd_ptr = commands; cmd_ptr->uuid != NULL; cmd_ptr++)
  105. {
  106. if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
  107. {
  108. goto close_array;
  109. }
  110. if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->uuid))
  111. {
  112. goto close_struct;
  113. }
  114. if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->client_name))
  115. {
  116. goto close_struct;
  117. }
  118. if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &cmd_ptr->command))
  119. {
  120. goto close_struct;
  121. }
  122. u32 = cmd_ptr->flags;
  123. if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &u32))
  124. {
  125. goto close_struct;
  126. }
  127. jack_info("uuid='%s', client='%s', command='%s', flags=0x%"PRIX32, cmd_ptr->uuid, cmd_ptr->client_name, cmd_ptr->command, u32);
  128. if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
  129. {
  130. goto close_array;
  131. }
  132. }
  133. jack_info("End of session commands.");
  134. if (!dbus_message_iter_close_container(&top_iter, &array_iter))
  135. {
  136. goto unref;
  137. }
  138. goto send_reply;
  139. close_struct:
  140. dbus_message_iter_close_container(&array_iter, &struct_iter);
  141. close_array:
  142. dbus_message_iter_close_container(&top_iter, &array_iter);
  143. unref:
  144. dbus_message_unref(call.reply);
  145. goto oom;
  146. send_reply:
  147. if (call.reply != NULL)
  148. {
  149. if (!dbus_connection_send(pending_cmd_ptr->connection, call.reply, NULL))
  150. {
  151. jack_error("Ran out of memory trying to queue method return");
  152. }
  153. dbus_connection_flush(pending_cmd_ptr->connection);
  154. dbus_message_unref(call.reply);
  155. }
  156. else
  157. {
  158. oom:
  159. jack_error("Ran out of memory trying to construct method return");
  160. }
  161. }
  162. #define controller_ptr ((struct jack_controller *)context)
  163. void * jack_controller_process_session_command_thread(void * context)
  164. {
  165. struct jack_session_pending_command * pending_cmd_ptr;
  166. jack_session_command_t * commands;
  167. jack_log("jack_controller_process_session_command_thread enter");
  168. pthread_mutex_lock(&controller_ptr->lock);
  169. loop:
  170. /* get next command */
  171. assert(!list_empty(&controller_ptr->session_pending_commands));
  172. pending_cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings);
  173. pthread_mutex_unlock(&controller_ptr->lock);
  174. jack_info("Session notify initiated. target='%s', type=%d, path='%s'", pending_cmd_ptr->target, (int)pending_cmd_ptr->type, pending_cmd_ptr->path);
  175. jack_controller_control_send_signal_session_state_changed(pending_cmd_ptr->type, pending_cmd_ptr->target);
  176. commands = jack_session_notify(controller_ptr->client, pending_cmd_ptr->target, pending_cmd_ptr->type, pending_cmd_ptr->path);
  177. usleep(5000000);
  178. send_session_notify_reply(pending_cmd_ptr, commands);
  179. if (commands != NULL)
  180. {
  181. jack_session_commands_free(commands);
  182. }
  183. pthread_mutex_lock(&controller_ptr->lock);
  184. /* keep state consistent by sending signal after to lock */
  185. /* otherwise the main thread may receive not-to-be-queued request and fail */
  186. jack_controller_control_send_signal_session_state_changed(0, NULL);
  187. /* remove the head of the list (queue) */
  188. assert(!list_empty(&controller_ptr->session_pending_commands));
  189. assert(pending_cmd_ptr == list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings));
  190. list_del(&pending_cmd_ptr->siblings);
  191. /* command cleanup */
  192. dbus_message_unref(pending_cmd_ptr->message);
  193. dbus_connection_ref(pending_cmd_ptr->connection);
  194. free(pending_cmd_ptr);
  195. /* If there are more commands, process them. Otherwise - exit the thread */
  196. if (!list_empty(&controller_ptr->session_pending_commands))
  197. {
  198. goto loop;
  199. }
  200. pthread_mutex_unlock(&controller_ptr->lock);
  201. jack_log("jack_controller_process_session_command_thread exit");
  202. return NULL;
  203. }
  204. #undef controller_ptr
  205. #define controller_ptr ((struct jack_controller *)call->context)
  206. static
  207. void
  208. jack_controller_dbus_session_notify(
  209. struct jack_dbus_method_call * call)
  210. {
  211. dbus_bool_t queue;
  212. const char * target;
  213. dbus_uint32_t u32;
  214. const char * path;
  215. jack_session_event_type_t type;
  216. struct jack_session_pending_command * cmd_ptr;
  217. if (!controller_ptr->started)
  218. {
  219. jack_dbus_only_error(call, JACK_DBUS_ERROR_SERVER_NOT_RUNNING, "Can't execute method '%s' with stopped JACK server", call->method_name);
  220. return;
  221. }
  222. if (!jack_dbus_get_method_args(
  223. call,
  224. DBUS_TYPE_BOOLEAN,
  225. &queue,
  226. DBUS_TYPE_STRING,
  227. &target,
  228. DBUS_TYPE_UINT32,
  229. &u32,
  230. DBUS_TYPE_STRING,
  231. &path,
  232. DBUS_TYPE_INVALID))
  233. {
  234. /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
  235. return;
  236. }
  237. if (*target == 0)
  238. {
  239. target = NULL;
  240. }
  241. type = (jack_session_event_type_t)u32;
  242. if (type != JackSessionSave &&
  243. type != JackSessionSaveAndQuit &&
  244. type != JackSessionSaveTemplate)
  245. {
  246. jack_dbus_error(call, JACK_DBUS_ERROR_INVALID_ARGS, "Invalid session event type %" PRIu32, u32);
  247. return;
  248. }
  249. pthread_mutex_lock(&controller_ptr->lock);
  250. if (list_empty(&controller_ptr->session_pending_commands))
  251. {
  252. if (!start_detached_thread(jack_controller_process_session_command_thread, controller_ptr))
  253. {
  254. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Cannot start thread to process the command");
  255. goto unlock;
  256. }
  257. jack_log("Session notify thread started");
  258. }
  259. else if (!queue)
  260. {
  261. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "Busy");
  262. goto unlock;
  263. }
  264. cmd_ptr = malloc(sizeof(struct jack_session_pending_command));
  265. if (cmd_ptr == NULL)
  266. {
  267. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "malloc() failed for jack_session_pending_command struct");
  268. goto unlock;
  269. }
  270. cmd_ptr->message = dbus_message_ref(call->message);
  271. call->message = NULL; /* mark that reply will be sent asynchronously */
  272. cmd_ptr->connection = dbus_connection_ref(call->connection);
  273. /* it is safe to use the retrived pointers because we already made an additional message reference */
  274. cmd_ptr->type = type;
  275. cmd_ptr->target = target;
  276. cmd_ptr->path = path;
  277. list_add_tail(&cmd_ptr->siblings, &controller_ptr->session_pending_commands);
  278. jack_log("Session notify scheduled. target='%s', type=%"PRIu32", path='%s'", target, u32, path);
  279. unlock:
  280. pthread_mutex_unlock(&controller_ptr->lock);
  281. }
  282. static
  283. void
  284. jack_controller_dbus_get_uuid_for_client_name(
  285. struct jack_dbus_method_call * call)
  286. {
  287. const char * client_name;
  288. char * client_uuid;
  289. if (!jack_dbus_get_method_args(
  290. call,
  291. DBUS_TYPE_STRING,
  292. &client_name,
  293. DBUS_TYPE_INVALID))
  294. {
  295. /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
  296. return;
  297. }
  298. client_uuid = jack_get_uuid_for_client_name(controller_ptr->client, client_name);
  299. if (client_uuid == NULL)
  300. {
  301. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_uuid_for_client_name(\"%s\") failed", client_name);
  302. return;
  303. }
  304. jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_uuid);
  305. free(client_uuid);
  306. }
  307. static
  308. void
  309. jack_controller_dbus_get_client_name_by_uuid(
  310. struct jack_dbus_method_call * call)
  311. {
  312. const char * client_uuid;
  313. char * client_name;
  314. if (!jack_dbus_get_method_args(
  315. call,
  316. DBUS_TYPE_STRING,
  317. &client_uuid,
  318. DBUS_TYPE_INVALID))
  319. {
  320. /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
  321. return;
  322. }
  323. client_name = jack_get_client_name_by_uuid(controller_ptr->client, client_uuid);
  324. if (client_name == NULL)
  325. {
  326. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_get_client_name_by_uuid(\"%s\") failed", client_uuid);
  327. return;
  328. }
  329. jack_dbus_construct_method_return_single(call, DBUS_TYPE_STRING, (message_arg_t)(const char *)client_name);
  330. free(client_name);
  331. }
  332. static
  333. void
  334. jack_controller_dbus_reserve_client_name(
  335. struct jack_dbus_method_call * call)
  336. {
  337. int ret;
  338. const char * client_name;
  339. const char * client_uuid;
  340. if (!jack_dbus_get_method_args(
  341. call,
  342. DBUS_TYPE_STRING,
  343. &client_name,
  344. DBUS_TYPE_STRING,
  345. &client_uuid,
  346. DBUS_TYPE_INVALID))
  347. {
  348. /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
  349. return;
  350. }
  351. ret = jack_reserve_client_name(controller_ptr->client, client_name, client_uuid);
  352. if (ret < 0)
  353. {
  354. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_reserve_client_name(name=\"%s\", uuid=\"%s\") failed (%d)", client_name, client_uuid, ret);
  355. return;
  356. }
  357. jack_dbus_construct_method_return_empty(call);
  358. }
  359. static
  360. void
  361. jack_controller_dbus_has_session_callback(
  362. struct jack_dbus_method_call * call)
  363. {
  364. int ret;
  365. const char * client_name;
  366. message_arg_t retval;
  367. if (!jack_dbus_get_method_args(
  368. call,
  369. DBUS_TYPE_STRING,
  370. &client_name,
  371. DBUS_TYPE_INVALID))
  372. {
  373. /* The method call had invalid arguments meaning that jack_dbus_get_method_args() has constructed an error for us. */
  374. return;
  375. }
  376. ret = jack_client_has_session_callback(controller_ptr->client, client_name);
  377. if (ret < 0)
  378. {
  379. jack_dbus_error(call, JACK_DBUS_ERROR_GENERIC, "jack_client_has_session_callback(\"%s\") failed (%d)", client_name, ret);
  380. return;
  381. }
  382. retval.boolean = ret;
  383. jack_dbus_construct_method_return_single(call, DBUS_TYPE_BOOLEAN, retval);
  384. }
  385. static
  386. void
  387. jack_controller_dbus_get_session_state(
  388. struct jack_dbus_method_call * call)
  389. {
  390. DBusMessageIter iter;
  391. struct jack_session_pending_command * cmd_ptr;
  392. const char * target;
  393. dbus_uint32_t type;
  394. bool append_failed;
  395. call->reply = dbus_message_new_method_return(call->message);
  396. if (call->reply == NULL)
  397. {
  398. goto oom;
  399. }
  400. dbus_message_iter_init_append(call->reply, &iter);
  401. pthread_mutex_lock(&controller_ptr->lock);
  402. if (list_empty(&controller_ptr->session_pending_commands))
  403. {
  404. type = 0;
  405. target = "";
  406. }
  407. else
  408. {
  409. cmd_ptr = list_entry(controller_ptr->session_pending_commands.next, struct jack_session_pending_command, siblings);
  410. type = (dbus_uint32_t)cmd_ptr->type;
  411. target = cmd_ptr->target;
  412. }
  413. append_failed =
  414. !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &type) ||
  415. !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target);
  416. pthread_mutex_unlock(&controller_ptr->lock);
  417. if (!append_failed)
  418. {
  419. return;
  420. }
  421. dbus_message_unref(call->reply);
  422. call->reply = NULL;
  423. oom:
  424. jack_error("Ran out of memory trying to construct method return");
  425. }
  426. #undef controller_ptr
  427. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(Notify)
  428. JACK_DBUS_METHOD_ARGUMENT("queue", DBUS_TYPE_BOOLEAN_AS_STRING, false)
  429. JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, false)
  430. JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, false)
  431. JACK_DBUS_METHOD_ARGUMENT("path", DBUS_TYPE_STRING_AS_STRING, false)
  432. JACK_DBUS_METHOD_ARGUMENT("result", "a(sssu)", true)
  433. JACK_DBUS_METHOD_ARGUMENTS_END
  434. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetUuidForClientName)
  435. JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false)
  436. JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, true)
  437. JACK_DBUS_METHOD_ARGUMENTS_END
  438. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetClientNameByUuid)
  439. JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false)
  440. JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, true)
  441. JACK_DBUS_METHOD_ARGUMENTS_END
  442. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(ReserveClientName)
  443. JACK_DBUS_METHOD_ARGUMENT("name", DBUS_TYPE_STRING_AS_STRING, false)
  444. JACK_DBUS_METHOD_ARGUMENT("uuid", DBUS_TYPE_STRING_AS_STRING, false)
  445. JACK_DBUS_METHOD_ARGUMENTS_END
  446. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(HasSessionCallback)
  447. JACK_DBUS_METHOD_ARGUMENT("client_name", DBUS_TYPE_STRING_AS_STRING, false)
  448. JACK_DBUS_METHOD_ARGUMENT("has_session_callback", DBUS_TYPE_BOOLEAN_AS_STRING, true)
  449. JACK_DBUS_METHOD_ARGUMENTS_END
  450. JACK_DBUS_METHOD_ARGUMENTS_BEGIN(GetState)
  451. JACK_DBUS_METHOD_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING, true)
  452. JACK_DBUS_METHOD_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING, true)
  453. JACK_DBUS_METHOD_ARGUMENTS_END
  454. JACK_DBUS_SIGNAL_ARGUMENTS_BEGIN(StateChanged)
  455. JACK_DBUS_SIGNAL_ARGUMENT("type", DBUS_TYPE_UINT32_AS_STRING)
  456. JACK_DBUS_SIGNAL_ARGUMENT("target", DBUS_TYPE_STRING_AS_STRING)
  457. JACK_DBUS_SIGNAL_ARGUMENTS_END
  458. JACK_DBUS_METHODS_BEGIN
  459. JACK_DBUS_METHOD_DESCRIBE(Notify, jack_controller_dbus_session_notify)
  460. JACK_DBUS_METHOD_DESCRIBE(GetUuidForClientName, jack_controller_dbus_get_uuid_for_client_name)
  461. JACK_DBUS_METHOD_DESCRIBE(GetClientNameByUuid, jack_controller_dbus_get_client_name_by_uuid)
  462. JACK_DBUS_METHOD_DESCRIBE(ReserveClientName, jack_controller_dbus_reserve_client_name)
  463. JACK_DBUS_METHOD_DESCRIBE(HasSessionCallback, jack_controller_dbus_has_session_callback)
  464. JACK_DBUS_METHOD_DESCRIBE(GetState, jack_controller_dbus_get_session_state)
  465. JACK_DBUS_METHODS_END
  466. JACK_DBUS_SIGNALS_BEGIN
  467. JACK_DBUS_SIGNAL_DESCRIBE(StateChanged)
  468. JACK_DBUS_SIGNALS_END
  469. JACK_DBUS_IFACE_BEGIN(g_jack_controller_iface_session_manager, JACK_DBUS_IFACE_NAME)
  470. JACK_DBUS_IFACE_EXPOSE_METHODS
  471. JACK_DBUS_IFACE_EXPOSE_SIGNALS
  472. JACK_DBUS_IFACE_END