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.

577 lines
16KB

  1. /* -*- Mode: C ; c-basic-offset: 4 -*- */
  2. /*
  3. Copyright (C) 2007,2008 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 <stdbool.h>
  19. #include <string.h>
  20. #include <dbus/dbus.h>
  21. #include <libxml/xmlwriter.h>
  22. #include <libxml/parser.h>
  23. #include <libxml/xpath.h>
  24. #include <jack/driver.h>
  25. #include <jack/engine.h>
  26. #include "controller_internal.h"
  27. #include "dbus.h"
  28. /* XPath expression used for engine options selection */
  29. #define XPATH_ENGINE_OPTIONS_EXPRESSION "/jack/engine/option"
  30. /* XPath expression used for drivers selection */
  31. #define XPATH_DRIVERS_EXPRESSION "/jack/drivers/driver"
  32. /* XPath expression used for driver options selection */
  33. #define XPATH_DRIVER_OPTIONS_EXPRESSION "/jack/drivers/driver[@name = '%s']/option"
  34. bool
  35. jack_controller_settings_init()
  36. {
  37. /*
  38. * this initialize the library and check potential ABI mismatches
  39. * between the version it was compiled for and the actual shared
  40. * library used.
  41. */
  42. LIBXML_TEST_VERSION;
  43. return true;
  44. }
  45. void
  46. jack_controller_settings_uninit()
  47. {
  48. }
  49. #define writer ((xmlTextWriterPtr)context)
  50. bool
  51. jack_controller_settings_write_option(
  52. void *context,
  53. const char *name,
  54. const char *content,
  55. void *dbus_call_context_ptr)
  56. {
  57. if (xmlTextWriterStartElement(writer, BAD_CAST "option") == -1)
  58. {
  59. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  60. return false;
  61. }
  62. if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST name) == -1)
  63. {
  64. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteAttribute() failed.");
  65. return false;
  66. }
  67. if (xmlTextWriterWriteString(writer, BAD_CAST content) == -1)
  68. {
  69. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteString() failed.");
  70. return false;
  71. }
  72. if (xmlTextWriterEndElement(writer) == -1)
  73. {
  74. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed.");
  75. return false;
  76. }
  77. return true;
  78. }
  79. #undef writer
  80. bool
  81. jack_controller_settings_write_engine(
  82. struct jack_controller * controller_ptr,
  83. xmlTextWriterPtr writer,
  84. void *dbus_call_context_ptr)
  85. {
  86. /* jack_info("engine settings begin"); */
  87. /* if (xmlTextWriterWriteComment(writer, BAD_CAST "engine parameters") == -1) */
  88. /* { */
  89. /* jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); */
  90. /* return false; */
  91. /* } */
  92. if (xmlTextWriterStartElement(writer, BAD_CAST "engine") == -1)
  93. {
  94. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  95. return false;
  96. }
  97. if (!jack_controller_settings_save_engine_options(writer, controller_ptr, dbus_call_context_ptr))
  98. {
  99. return false;
  100. }
  101. if (xmlTextWriterEndElement(writer) == -1)
  102. {
  103. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed.");
  104. return false;
  105. }
  106. /* jack_info("engine settings end"); */
  107. return true;
  108. }
  109. bool
  110. jack_controller_settings_write_driver(
  111. struct jack_controller * controller_ptr,
  112. xmlTextWriterPtr writer,
  113. jackctl_driver driver,
  114. void *dbus_call_context_ptr)
  115. {
  116. /* if (xmlTextWriterWriteComment(writer, BAD_CAST "driver parameters") == -1) */
  117. /* { */
  118. /* jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed."); */
  119. /* return false; */
  120. /* } */
  121. if (xmlTextWriterStartElement(writer, BAD_CAST "driver") == -1)
  122. {
  123. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  124. return false;
  125. }
  126. if (xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST jackctl_driver_get_name(driver)) == -1)
  127. {
  128. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteAttribute() failed.");
  129. return false;
  130. }
  131. if (!jack_controller_settings_save_driver_options(writer, driver, dbus_call_context_ptr))
  132. {
  133. return false;
  134. }
  135. if (xmlTextWriterEndElement(writer) == -1)
  136. {
  137. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed.");
  138. return false;
  139. }
  140. return true;
  141. }
  142. bool
  143. jack_controller_settings_write_drivers(
  144. struct jack_controller * controller_ptr,
  145. xmlTextWriterPtr writer,
  146. void *dbus_call_context_ptr)
  147. {
  148. const JSList * node_ptr;
  149. jackctl_driver driver;
  150. if (xmlTextWriterStartElement(writer, BAD_CAST "drivers") == -1)
  151. {
  152. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  153. return false;
  154. }
  155. node_ptr = jackctl_server_get_drivers_list(controller_ptr->server);
  156. while (node_ptr != NULL)
  157. {
  158. driver = (jackctl_driver)node_ptr->data;
  159. if (!jack_controller_settings_write_driver(
  160. controller_ptr,
  161. writer,
  162. driver,
  163. dbus_call_context_ptr))
  164. {
  165. return false;
  166. }
  167. node_ptr = jack_slist_next(node_ptr);
  168. }
  169. if (xmlTextWriterEndElement(writer) == -1)
  170. {
  171. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndElement() failed.");
  172. return false;
  173. }
  174. return true;
  175. }
  176. bool
  177. jack_controller_settings_save(
  178. struct jack_controller * controller_ptr,
  179. void *dbus_call_context_ptr)
  180. {
  181. xmlTextWriterPtr writer;
  182. char *filename;
  183. size_t conf_len;
  184. bool ret;
  185. time_t timestamp;
  186. char timestamp_str[28];
  187. time(&timestamp);
  188. timestamp_str[0] = ' ';
  189. ctime_r(&timestamp, timestamp_str + 1);
  190. timestamp_str[25] = ' ';
  191. ret = false;
  192. conf_len = strlen(JACKDBUS_CONF);
  193. filename = malloc(g_jackdbus_dir_len + conf_len + 1);
  194. if (filename == NULL)
  195. {
  196. jack_error("Out of memory.");
  197. goto fail;
  198. }
  199. memcpy(filename, g_jackdbus_dir, g_jackdbus_dir_len);
  200. memcpy(filename + g_jackdbus_dir_len, JACKDBUS_CONF, conf_len);
  201. filename[g_jackdbus_dir_len + conf_len] = 0;
  202. jack_info("saving settings to \"%s\"", filename);
  203. writer = xmlNewTextWriterFilename(filename, 0);
  204. if (writer == NULL)
  205. {
  206. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "Error creating the xml writer.");
  207. goto fail_free_filename;
  208. }
  209. if (xmlTextWriterSetIndent(writer, 1) == -1)
  210. {
  211. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterSetIndent() failed.");
  212. goto fail_free_writter;
  213. }
  214. if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) == -1)
  215. {
  216. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartDocument() failed.");
  217. goto fail_free_writter;
  218. }
  219. if (xmlTextWriterWriteComment(writer, BAD_CAST "\n" JACK_CONF_HEADER_TEXT) == -1)
  220. {
  221. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed.");
  222. goto fail_free_writter;
  223. }
  224. if (xmlTextWriterWriteComment(writer, BAD_CAST timestamp_str) == -1)
  225. {
  226. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterWriteComment() failed.");
  227. goto fail_free_writter;
  228. }
  229. if (xmlTextWriterStartElement(writer, BAD_CAST "jack") == -1)
  230. {
  231. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  232. goto fail_free_writter;
  233. }
  234. if (!jack_controller_settings_write_engine(controller_ptr, writer, dbus_call_context_ptr))
  235. {
  236. goto fail_free_writter;
  237. }
  238. if (!jack_controller_settings_write_drivers(controller_ptr, writer, dbus_call_context_ptr))
  239. {
  240. goto fail_free_writter;
  241. }
  242. if (xmlTextWriterEndElement(writer) == -1)
  243. {
  244. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterStartElement() failed.");
  245. goto fail_free_writter;
  246. }
  247. if (xmlTextWriterEndDocument(writer) == -1)
  248. {
  249. jack_dbus_error(dbus_call_context_ptr, JACK_DBUS_ERROR_GENERIC, "xmlTextWriterEndDocument() failed.");
  250. goto fail_free_writter;
  251. }
  252. ret = true;
  253. fail_free_writter:
  254. xmlFreeTextWriter(writer);
  255. fail_free_filename:
  256. free(filename);
  257. fail:
  258. return ret;
  259. }
  260. void
  261. jack_controller_settings_read_engine(
  262. struct jack_controller * controller_ptr,
  263. xmlXPathContextPtr xpath_ctx_ptr)
  264. {
  265. xmlXPathObjectPtr xpath_obj_ptr;
  266. xmlBufferPtr content_buffer_ptr;
  267. int i;
  268. const char *option_name;
  269. const char *option_value;
  270. /* Evaluate xpath expression */
  271. xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_ENGINE_OPTIONS_EXPRESSION, xpath_ctx_ptr);
  272. if (xpath_obj_ptr == NULL)
  273. {
  274. jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_ENGINE_OPTIONS_EXPRESSION);
  275. goto exit;
  276. }
  277. if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0)
  278. {
  279. jack_error("XPath \"%s\" evaluation returned no data", XPATH_ENGINE_OPTIONS_EXPRESSION);
  280. goto free_xpath_obj;
  281. }
  282. content_buffer_ptr = xmlBufferCreate();
  283. if (content_buffer_ptr == NULL)
  284. {
  285. jack_error("xmlBufferCreate() failed.");
  286. goto free_xpath_obj;
  287. }
  288. for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++)
  289. {
  290. //jack_info("engine option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i);
  291. if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1)
  292. {
  293. jack_error("xmlNodeBufGetContent() failed.");
  294. goto next_option;
  295. }
  296. option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name");
  297. option_value = (const char *)xmlBufferContent(content_buffer_ptr);
  298. jack_controller_settings_set_engine_option(controller_ptr, option_name, option_value);
  299. next_option:
  300. xmlBufferEmpty(content_buffer_ptr);
  301. }
  302. //free_buffer:
  303. xmlBufferFree(content_buffer_ptr);
  304. free_xpath_obj:
  305. xmlXPathFreeObject(xpath_obj_ptr);
  306. exit:
  307. return;
  308. }
  309. void
  310. jack_controller_settings_read_driver(
  311. struct jack_controller * controller_ptr,
  312. xmlXPathContextPtr xpath_ctx_ptr,
  313. jackctl_driver driver)
  314. {
  315. char *xpath;
  316. size_t xpath_len;
  317. xmlXPathObjectPtr xpath_obj_ptr;
  318. xmlBufferPtr content_buffer_ptr;
  319. int i;
  320. const char *option_name;
  321. const char *option_value;
  322. const char *driver_name;
  323. driver_name = jackctl_driver_get_name(driver);
  324. jack_info("reading options for driver \"%s\"", driver_name);
  325. xpath_len = snprintf(NULL, 0, XPATH_DRIVER_OPTIONS_EXPRESSION, driver_name);
  326. xpath = malloc(xpath_len);
  327. if (xpath == NULL)
  328. {
  329. jack_error("Out of memory.");
  330. goto exit;
  331. }
  332. snprintf(xpath, xpath_len, XPATH_DRIVER_OPTIONS_EXPRESSION, driver_name);
  333. //jack_info("xpath = \"%s\"", xpath);
  334. /* Evaluate xpath expression */
  335. xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)xpath, xpath_ctx_ptr);
  336. if (xpath_obj_ptr == NULL)
  337. {
  338. jack_error("Unable to evaluate XPath expression \"%s\"", xpath);
  339. goto free_xpath;
  340. }
  341. if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0)
  342. {
  343. //jack_info("XPath \"%s\" evaluation returned no data", xpath);
  344. goto free_xpath_obj;
  345. }
  346. content_buffer_ptr = xmlBufferCreate();
  347. if (content_buffer_ptr == NULL)
  348. {
  349. jack_error("xmlBufferCreate() failed.");
  350. goto free_xpath_obj;
  351. }
  352. for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++)
  353. {
  354. //jack_info("driver option \"%s\" at index %d", xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name"), i);
  355. if (xmlNodeBufGetContent(content_buffer_ptr, xpath_obj_ptr->nodesetval->nodeTab[i]) == -1)
  356. {
  357. jack_error("xmlNodeBufGetContent() failed.");
  358. goto next_option;
  359. }
  360. option_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name");
  361. option_value = (const char *)xmlBufferContent(content_buffer_ptr);
  362. jack_controller_settings_set_driver_option(driver, option_name, option_value);
  363. next_option:
  364. xmlBufferEmpty(content_buffer_ptr);
  365. }
  366. //free_buffer:
  367. xmlBufferFree(content_buffer_ptr);
  368. free_xpath_obj:
  369. xmlXPathFreeObject(xpath_obj_ptr);
  370. free_xpath:
  371. free(xpath);
  372. exit:
  373. return;
  374. }
  375. void
  376. jack_controller_settings_read_drivers(
  377. struct jack_controller * controller_ptr,
  378. xmlXPathContextPtr xpath_ctx_ptr)
  379. {
  380. xmlXPathObjectPtr xpath_obj_ptr;
  381. int i;
  382. const char *driver_name;
  383. jackctl_driver driver;
  384. /* Evaluate xpath expression */
  385. xpath_obj_ptr = xmlXPathEvalExpression((const xmlChar *)XPATH_DRIVERS_EXPRESSION, xpath_ctx_ptr);
  386. if (xpath_obj_ptr == NULL)
  387. {
  388. jack_error("Unable to evaluate XPath expression \"%s\"", XPATH_DRIVERS_EXPRESSION);
  389. goto exit;
  390. }
  391. if (xpath_obj_ptr->nodesetval == NULL || xpath_obj_ptr->nodesetval->nodeNr == 0)
  392. {
  393. jack_error("XPath \"%s\" evaluation returned no data", XPATH_DRIVERS_EXPRESSION);
  394. goto free_xpath_obj;
  395. }
  396. for (i = 0 ; i < xpath_obj_ptr->nodesetval->nodeNr ; i++)
  397. {
  398. driver_name = (const char *)xmlGetProp(xpath_obj_ptr->nodesetval->nodeTab[i], BAD_CAST "name");
  399. driver = jack_controller_find_driver(controller_ptr->server, driver_name);
  400. if (driver == NULL)
  401. {
  402. jack_error("ignoring settings for unknown driver \"%s\"", driver_name);
  403. }
  404. else
  405. {
  406. jack_info("setting for driver \"%s\" found", driver_name);
  407. jack_controller_settings_read_driver(controller_ptr, xpath_ctx_ptr, driver);
  408. }
  409. }
  410. free_xpath_obj:
  411. xmlXPathFreeObject(xpath_obj_ptr);
  412. exit:
  413. return;
  414. }
  415. void
  416. jack_controller_settings_load(
  417. struct jack_controller * controller_ptr)
  418. {
  419. char *filename;
  420. size_t conf_len;
  421. xmlDocPtr doc_ptr;
  422. xmlXPathContextPtr xpath_ctx_ptr;
  423. conf_len = strlen(JACKDBUS_CONF);
  424. filename = malloc(g_jackdbus_dir_len + conf_len + 1);
  425. if (filename == NULL)
  426. {
  427. jack_error("Out of memory.");
  428. goto exit;
  429. }
  430. memcpy(filename, g_jackdbus_dir, g_jackdbus_dir_len);
  431. memcpy(filename + g_jackdbus_dir_len, JACKDBUS_CONF, conf_len);
  432. filename[g_jackdbus_dir_len + conf_len] = 0;
  433. jack_info("loading settings from \"%s\"", filename);
  434. doc_ptr = xmlParseFile(filename);
  435. if (doc_ptr == NULL)
  436. {
  437. jack_error("Failed to parse \"%s\"", filename);
  438. goto free_filename;
  439. }
  440. /* Create xpath evaluation context */
  441. xpath_ctx_ptr = xmlXPathNewContext(doc_ptr);
  442. if (xpath_ctx_ptr == NULL)
  443. {
  444. jack_error("Unable to create new XPath context");
  445. goto free_doc;
  446. }
  447. jack_controller_settings_read_engine(controller_ptr, xpath_ctx_ptr);
  448. jack_controller_settings_read_drivers(controller_ptr, xpath_ctx_ptr);
  449. xmlXPathFreeContext(xpath_ctx_ptr);
  450. free_doc:
  451. xmlFreeDoc(doc_ptr);
  452. free_filename:
  453. free(filename);
  454. exit:
  455. return;
  456. }
  457. void
  458. jack_controller_settings_save_auto(
  459. struct jack_controller * controller_ptr)
  460. {
  461. jack_controller_settings_save(controller_ptr, NULL);
  462. }