PageRenderTime 73ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/src/core/main.c

https://github.com/psycho-nico/ummd
C | 541 lines | 386 code | 111 blank | 44 comment | 54 complexity | f3caa534e6dfcf4218229c3209eeb45a MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * ummd ( Micro MultiMedia Daemon )
  3. *
  4. * Copyright (C) 2010 Nicolas Thill <nicolas.thill@gmail.com>
  5. */
  6. /*
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include <err.h>
  22. #include <errno.h>
  23. #include <signal.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <stdint.h>
  27. #include <unistd.h>
  28. #include <sys/times.h>
  29. #include "core.h"
  30. #include "util/log.h"
  31. #include "util/mem.h"
  32. #include "util/list.h"
  33. #define EVENT_LIST_RESOLUTION 250000 /* microseconds */
  34. typedef struct my_core_priv my_core_priv_t;
  35. struct my_core_priv {
  36. my_core_t base;
  37. int running;
  38. fd_set watching_fds;
  39. int max_sock;
  40. int64_t curr_time;
  41. my_list_t *watched_fd_list;
  42. my_list_t *alarm_list;
  43. uint32_t system_tick;
  44. };
  45. struct watch_entry {
  46. int fd;
  47. my_event_handler_t callback_fn;
  48. void *data;
  49. };
  50. struct alarm_entry {
  51. uint64_t alarm_time;
  52. uint64_t reoccurring;
  53. my_alarm_handler_t callback_fn;
  54. void *data;
  55. };
  56. #define MY_CORE_PRIV(p) ((my_core_priv_t *)p)
  57. static void my_core_exit(my_core_t *core)
  58. {
  59. MY_CORE_PRIV(core)->running = 0;
  60. }
  61. static void my_core_handle_shutdown(int sig, short event, void *p)
  62. {
  63. my_log(MY_LOG_NOTICE, "core: received signal %d" , sig);
  64. my_core_exit(MY_CORE(p));
  65. }
  66. static void my_core_watched_fds_update(my_core_t *core)
  67. {
  68. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  69. struct watch_entry *watch_entry;
  70. my_node_t *node;
  71. int tmp_max_sock = 0;
  72. fd_set tmp_watched_fds;
  73. FD_ZERO(&tmp_watched_fds);
  74. my_list_for_each(core_priv->watched_fd_list, node, watch_entry) {
  75. if (watch_entry->fd > tmp_max_sock)
  76. tmp_max_sock = watch_entry->fd;
  77. FD_SET(watch_entry->fd, &tmp_watched_fds);
  78. }
  79. core_priv->max_sock = tmp_max_sock;
  80. memcpy(&core_priv->watching_fds, &tmp_watched_fds, sizeof(fd_set));
  81. }
  82. /* Make times(2) behave rationally on Linux */
  83. static clock_t my_core_times_wrapper(void)
  84. {
  85. struct tms dummy_tms_struct;
  86. int save_errno = errno;
  87. clock_t ret;
  88. /**
  89. * times(2) really returns an unsigned value ...
  90. *
  91. * We don't check to see if we got back the error value (-1), because
  92. * the only possibility for an error would be if the address of
  93. * dummy_tms_struct was invalid. Since it's a
  94. * compiler-generated address, we assume that errors are impossible.
  95. * And, unfortunately, it is quite possible for the correct return
  96. * from times(2) to be exactly (clock_t)-1. Sigh...
  97. *
  98. */
  99. errno = 0;
  100. ret = times(&dummy_tms_struct);
  101. /**
  102. * This is to work around a bug in the system call interface
  103. * for times(2) found in glibc on Linux (and maybe elsewhere)
  104. * It changes the return values from -1 to -4096 all into
  105. * -1 and then dumps the -(return value) into errno.
  106. *
  107. * This totally bizarre behavior seems to be widespread in
  108. * versions of Linux and glibc.
  109. *
  110. * Many thanks to Wolfgang Dumhs <wolfgang.dumhs (at) gmx.at>
  111. * for finding and documenting this bizarre behavior.
  112. */
  113. if (errno != 0) {
  114. ret = (clock_t) (-errno);
  115. }
  116. errno = save_errno;
  117. return ret;
  118. }
  119. static uint64_t my_core_get_time_msec(my_core_t *core)
  120. {
  121. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  122. return (my_core_times_wrapper() * 1000) / core_priv->system_tick;
  123. }
  124. my_core_t *my_core_create(void)
  125. {
  126. my_core_t *core;
  127. my_core_priv_t *core_priv;
  128. core = my_mem_alloc(sizeof(my_core_priv_t));
  129. if (!core) {
  130. MY_ERROR("core: error creating internal data (%s)" , strerror(errno));
  131. goto _MY_ERR_alloc;
  132. }
  133. core->controls = my_list_create();
  134. if (core->controls == NULL) {
  135. MY_ERROR("core: error creating control list (%s)" , strerror(errno));
  136. goto _MY_ERR_create_controls;
  137. }
  138. core->filters = my_list_create();
  139. if (core->filters == NULL) {
  140. MY_ERROR("core: error creating filter list (%s)" , strerror(errno));
  141. goto _MY_ERR_create_filters;
  142. }
  143. core->sources = my_list_create();
  144. if (core->sources == NULL) {
  145. MY_ERROR("core: error creating source list (%s)" , strerror(errno));
  146. goto _MY_ERR_create_sources;
  147. }
  148. core->targets = my_list_create();
  149. if (core->targets == NULL) {
  150. MY_ERROR("core: error creating target list (%s)" , strerror(errno));
  151. goto _MY_ERR_create_targets;
  152. }
  153. core->wirings = my_list_create();
  154. if (core->wirings == NULL) {
  155. MY_ERROR("core: error creating wiring list (%s)" , strerror(errno));
  156. goto _MY_ERR_create_wirings;
  157. }
  158. core_priv = MY_CORE_PRIV(core);
  159. core_priv->watched_fd_list = my_list_create();
  160. if (!core_priv->watched_fd_list) {
  161. MY_ERROR("core: error creating watched fd list (%s)" , strerror(errno));
  162. goto _MY_ERR_create_watched_fds;
  163. }
  164. core_priv->alarm_list = my_list_create();
  165. if (!core_priv->alarm_list) {
  166. MY_ERROR("core: error creating alarm list (%s)" , strerror(errno));
  167. goto _MY_ERR_create_alarm_list;
  168. }
  169. FD_ZERO(&core_priv->watching_fds);
  170. core_priv->max_sock = 0;
  171. core_priv->system_tick = sysconf(_SC_CLK_TCK);
  172. core_priv->curr_time = my_core_get_time_msec(core);
  173. my_audio_codec_init();
  174. my_control_register_all();
  175. my_filter_register_all();
  176. my_source_register_all();
  177. my_target_register_all();
  178. return core;
  179. my_list_destroy(core_priv->alarm_list);
  180. _MY_ERR_create_alarm_list:
  181. my_list_destroy(core_priv->watched_fd_list);
  182. _MY_ERR_create_watched_fds:
  183. my_list_destroy(core->wirings);
  184. _MY_ERR_create_wirings:
  185. my_list_destroy(core->targets);
  186. _MY_ERR_create_targets:
  187. my_list_destroy(core->sources);
  188. _MY_ERR_create_sources:
  189. my_list_destroy(core->filters);
  190. _MY_ERR_create_filters:
  191. my_list_destroy(core->controls);
  192. _MY_ERR_create_controls:
  193. my_mem_free(core);
  194. _MY_ERR_alloc:
  195. return NULL;
  196. }
  197. void my_core_destroy(my_core_t *core)
  198. {
  199. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  200. my_audio_codec_fini();
  201. my_target_close_all(core);
  202. my_source_close_all(core);
  203. my_control_close_all(core);
  204. my_wiring_destroy_all(core);
  205. my_target_destroy_all(core);
  206. my_source_destroy_all(core);
  207. my_filter_destroy_all(core);
  208. my_control_destroy_all(core);
  209. my_list_destroy(core->controls);
  210. my_list_destroy(core->filters);
  211. my_list_destroy(core->sources);
  212. my_list_destroy(core->targets);
  213. my_list_destroy(core->wirings);
  214. my_list_destroy(core_priv->watched_fd_list);
  215. my_list_purge(core_priv->alarm_list, MY_LIST_PURGE_FLAG_FREE_DATA);
  216. my_list_destroy(core_priv->alarm_list);
  217. my_mem_free(core);
  218. }
  219. int my_core_init(my_core_t *core, my_conf_t *conf)
  220. {
  221. if (my_control_create_all(core, conf) != 0) {
  222. goto _MY_ERR_create_controls;
  223. }
  224. if (my_filter_create_all(core, conf) != 0) {
  225. goto _MY_ERR_create_filters;
  226. }
  227. if (my_source_create_all(core, conf) != 0) {
  228. goto _MY_ERR_create_sources;
  229. }
  230. if (my_target_create_all(core, conf) != 0) {
  231. goto _MY_ERR_create_targets;
  232. }
  233. if (my_wiring_create_all(core, conf) != 0) {
  234. goto _MY_ERR_create_wirings;
  235. }
  236. if (my_control_open_all(core) != 0) {
  237. goto _MY_ERR_open_controls;
  238. }
  239. if (my_source_open_all(core) != 0) {
  240. goto _MY_ERR_open_sources;
  241. }
  242. if (my_target_open_all(core) != 0) {
  243. goto _MY_ERR_open_targets;
  244. }
  245. return 0;
  246. my_target_close_all(core);
  247. _MY_ERR_open_targets:
  248. my_source_close_all(core);
  249. _MY_ERR_open_sources:
  250. my_control_close_all(core);
  251. _MY_ERR_open_controls:
  252. my_wiring_destroy_all(core);
  253. _MY_ERR_create_wirings:
  254. my_target_destroy_all(core);
  255. _MY_ERR_create_targets:
  256. my_source_destroy_all(core);
  257. _MY_ERR_create_sources:
  258. my_filter_destroy_all(core);
  259. _MY_ERR_create_filters:
  260. my_control_destroy_all(core);
  261. _MY_ERR_create_controls:
  262. return -1;
  263. }
  264. static void my_core_alarm_add_at_pos(my_core_t *core, struct alarm_entry *alarm_entry)
  265. {
  266. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  267. struct alarm_entry *alarm_entry_tmp;
  268. my_node_t *node;
  269. my_list_for_each(core_priv->alarm_list, node, alarm_entry_tmp) {
  270. if ((int64_t)(alarm_entry_tmp->alarm_time - alarm_entry->alarm_time) > 0)
  271. continue;
  272. my_list_insert_before(core_priv->alarm_list, node, alarm_entry);
  273. return;
  274. }
  275. my_list_enqueue(core_priv->alarm_list, alarm_entry);
  276. }
  277. static void my_core_alarm_list_add_reoccurring(my_core_t *core, struct alarm_entry *alarm_entry)
  278. {
  279. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  280. alarm_entry->alarm_time = core_priv->curr_time + alarm_entry->reoccurring;
  281. my_core_alarm_add_at_pos(core, alarm_entry);
  282. }
  283. int my_core_alarm_add(my_core_t *core, unsigned int timeout,
  284. int reoccurring, my_alarm_handler_t handler, void *p)
  285. {
  286. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  287. struct alarm_entry *alarm_entry;
  288. alarm_entry = my_mem_alloc(sizeof(struct alarm_entry));
  289. if (!alarm_entry) {
  290. my_log(MY_LOG_ERROR, "core: error creating alarm entry (%s)" , strerror(errno));
  291. goto err;
  292. }
  293. alarm_entry->alarm_time = my_core_get_time_msec(core) + timeout;
  294. alarm_entry->callback_fn = handler;
  295. alarm_entry->data = p;
  296. if (reoccurring)
  297. alarm_entry->reoccurring = timeout;
  298. else
  299. alarm_entry->reoccurring = 0;
  300. my_core_alarm_add_at_pos(core, alarm_entry);
  301. out:
  302. return 0;
  303. err:
  304. return -1;
  305. }
  306. static void my_core_alarm_list_maintain(my_core_t *core)
  307. {
  308. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  309. struct alarm_entry *alarm_entry;
  310. my_node_t *node;
  311. my_list_for_each(core_priv->alarm_list, node, alarm_entry) {
  312. if ((int)(alarm_entry->alarm_time - core_priv->curr_time) > 0)
  313. break;
  314. (alarm_entry->callback_fn)(alarm_entry->data);
  315. my_list_remove(core_priv->alarm_list, node);
  316. if (alarm_entry->reoccurring)
  317. my_core_alarm_list_add_reoccurring(core, alarm_entry);
  318. else
  319. my_mem_free(alarm_entry);
  320. }
  321. }
  322. void my_core_loop(my_core_t *core)
  323. {
  324. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  325. my_node_t *node;
  326. struct watch_entry *watch_entry;
  327. fd_set tmp_watched_fds;
  328. struct timeval tv;
  329. int ret;
  330. core_priv->running = 1;
  331. tv.tv_sec = 0;
  332. tv.tv_usec = EVENT_LIST_RESOLUTION;
  333. while (core_priv->running) {
  334. memcpy(&tmp_watched_fds, &core_priv->watching_fds, sizeof(fd_set));
  335. ret = select(core_priv->max_sock + 1, &tmp_watched_fds, NULL, NULL, &tv);
  336. core_priv->curr_time = my_core_get_time_msec(core);
  337. my_core_alarm_list_maintain(core);
  338. if (ret < 0) {
  339. if (errno != EINTR)
  340. my_log(MY_LOG_ERROR, "core: select error '%s'", strerror(errno));
  341. }
  342. if (ret <= 0)
  343. goto reset_sleep;
  344. my_list_for_each(core_priv->watched_fd_list, node, watch_entry) {
  345. if (!FD_ISSET(watch_entry->fd, &tmp_watched_fds))
  346. continue;
  347. (watch_entry->callback_fn)(watch_entry->fd, watch_entry->data);
  348. }
  349. continue;
  350. reset_sleep:
  351. tv.tv_sec = 0;
  352. tv.tv_usec = EVENT_LIST_RESOLUTION;
  353. }
  354. }
  355. void my_core_stop(my_core_t *core)
  356. {
  357. MY_CORE_PRIV(core)->running = 0;
  358. }
  359. int my_core_event_handler_add(my_core_t *core, int fd, my_event_handler_t handler, void *p)
  360. {
  361. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  362. struct watch_entry *watch_entry;
  363. my_node_t *node;
  364. my_list_for_each(core_priv->watched_fd_list, node, watch_entry) {
  365. if (watch_entry->fd != fd)
  366. continue;
  367. my_log(MY_LOG_ERROR, "core: trying to register an already watched fd: '%i'", fd);
  368. goto err;
  369. }
  370. watch_entry = my_mem_alloc(sizeof(struct watch_entry));
  371. if (!watch_entry) {
  372. my_log(MY_LOG_ERROR, "core: error creating event handler (%s)" , strerror(errno));
  373. goto err;
  374. }
  375. watch_entry->fd = fd;
  376. watch_entry->callback_fn = handler;
  377. watch_entry->data = p;
  378. my_list_enqueue(core_priv->watched_fd_list, watch_entry);
  379. my_core_watched_fds_update(core);
  380. return 0;
  381. err:
  382. return -1;
  383. }
  384. int my_core_event_handler_del(my_core_t *core, int fd)
  385. {
  386. my_core_priv_t *core_priv = MY_CORE_PRIV(core);
  387. struct watch_entry *watch_entry;
  388. my_node_t *node;
  389. my_list_for_each(core_priv->watched_fd_list, node, watch_entry) {
  390. if (watch_entry->fd == fd)
  391. break;
  392. watch_entry = NULL;
  393. }
  394. if (!watch_entry) {
  395. my_log(MY_LOG_ERROR, "core: error unregistering fd: unknown fd (%i)" , fd);
  396. goto err;
  397. }
  398. my_list_remove(core_priv->watched_fd_list, node);
  399. my_mem_free(watch_entry);
  400. my_core_watched_fds_update(core);
  401. return 0;
  402. err:
  403. return -1;
  404. }
  405. static char my_proto[] = "UMMD/" VERSION;
  406. int my_core_handle_command(my_core_t *core, void *buf, int len)
  407. {
  408. char *p = buf;
  409. int n;
  410. n = strlen(my_proto);
  411. if (strncmp(p, my_proto, n) != 0) {
  412. my_log(MY_LOG_NOTICE, "core: unknown command signature");
  413. }
  414. p += n + 1;
  415. if (strcmp(p, "QUIT") == 0) {
  416. MY_DEBUG("core: received '%s' command", p);
  417. my_core_exit(core);
  418. } else {
  419. my_log(MY_LOG_ERROR, "core: unknown command '%s'", p);
  420. }
  421. }
  422. #ifdef MY_DEBUGGING
  423. void my_core_dump(my_core_t *core)
  424. {
  425. my_control_dump_all();
  426. my_filter_dump_all();
  427. my_source_dump_all();
  428. my_target_dump_all();
  429. }
  430. #endif /* MY_DEBUGGING */