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.

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