jack1 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.

305 lines
6.8KB

  1. /*
  2. Copyright (C) 2001 Paul Davis
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  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. $Id$
  15. */
  16. #include <stdio.h>
  17. #include <signal.h>
  18. #include <getopt.h>
  19. #include <sys/types.h>
  20. #include <dirent.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <wait.h>
  24. #include <jack/engine.h>
  25. #include <jack/internal.h>
  26. #include <jack/driver.h>
  27. static sigset_t signals;
  28. static jack_engine_t *engine;
  29. static int jackd_pid;
  30. static char *alsa_pcm_name = "default";
  31. static nframes_t frames_per_interrupt = 64;
  32. static nframes_t srate = 48000;
  33. static int realtime = 0;
  34. static int realtime_priority = 10;
  35. static int with_fork = 0;
  36. static void
  37. cleanup ()
  38. {
  39. DIR *dir;
  40. struct dirent *dirent;
  41. /* its important that we remove all files that jackd creates
  42. because otherwise subsequent attempts to start jackd will
  43. believe that an instance is already running.
  44. */
  45. if ((dir = opendir ("/tmp")) == NULL) {
  46. fprintf (stderr, "jackd(%li): cleanup - cannot open scratch directory (%s)\n", (long)getpid(), strerror (errno));
  47. return;
  48. }
  49. while ((dirent = readdir (dir)) != NULL) {
  50. if (strncmp (dirent->d_name, "jack-", 5) == 0) {
  51. unlink (dirent->d_name);
  52. }
  53. }
  54. closedir (dir);
  55. }
  56. static void
  57. signal_handler (int sig)
  58. {
  59. fprintf (stderr, "killing jackd at %d\n", jackd_pid);
  60. kill (jackd_pid, SIGTERM);
  61. cleanup ();
  62. exit (1);
  63. }
  64. static void
  65. catch_signals (void)
  66. {
  67. /* what's this for?
  68. this just makes sure that if we are using the fork
  69. approach to cleanup (see main()), the waiting
  70. process will catch common "interrupt" signals
  71. and terminate the real server appropriately.
  72. */
  73. signal (SIGHUP, signal_handler);
  74. signal (SIGINT, signal_handler);
  75. signal (SIGQUIT, signal_handler);
  76. signal (SIGTERM, signal_handler);
  77. }
  78. static void *
  79. signal_thread (void *arg)
  80. {
  81. int sig;
  82. int err;
  83. pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  84. err = sigwait (&signals, &sig);
  85. fprintf (stderr, "exiting due to signal %d\n", sig);
  86. jack_engine_delete (engine);
  87. if (!with_fork) {
  88. /* parent cleans up if we forked */
  89. cleanup ();
  90. }
  91. exit (err);
  92. /*NOTREACHED*/
  93. return 0;
  94. }
  95. static int
  96. posix_me_harder (void)
  97. {
  98. pthread_t thread_id;
  99. /* what's this for?
  100. POSIX says that signals are delivered like this:
  101. * if a thread has blocked that signal, it is not
  102. a candidate to receive the signal.
  103. * of all threads not blocking the signal, pick
  104. one at random, and deliver the signal.
  105. this means that a simple-minded multi-threaded
  106. program can expect to get POSIX signals delivered
  107. to any of its threads.
  108. here, we block all signals that we think we
  109. might receive and want to catch. all later
  110. threads will inherit this setting. then we
  111. create a thread that calls sigwait() on the
  112. same set of signals, implicitly unblocking
  113. all those signals. any of those signals that
  114. are delivered to the process will be delivered
  115. to that thread, and that thread alone. this
  116. makes cleanup for a signal-driven exit much
  117. easier, since we know which thread is doing
  118. it and more importantly, we are free to
  119. call async-unsafe functions, because the
  120. code is executing in normal thread context
  121. after a return from sigwait().
  122. */
  123. sigemptyset (&signals);
  124. sigaddset(&signals, SIGHUP);
  125. sigaddset(&signals, SIGINT);
  126. sigaddset(&signals, SIGQUIT);
  127. sigaddset(&signals, SIGILL);
  128. sigaddset(&signals, SIGTRAP);
  129. sigaddset(&signals, SIGABRT);
  130. sigaddset(&signals, SIGIOT);
  131. sigaddset(&signals, SIGFPE);
  132. sigaddset(&signals, SIGPIPE);
  133. sigaddset(&signals, SIGTERM);
  134. /* this can make debugging a pain, but it also makes
  135. segv-exits cleanup after themselves rather than
  136. leaving the audio thread active. i still
  137. find it truly wierd that _exit() or whatever is done
  138. by the default SIGSEGV handler does not
  139. cancel all threads in a process, but what
  140. else can we do?
  141. */
  142. sigaddset(&signals, SIGSEGV);
  143. /* all child threads will inherit this mask */
  144. pthread_sigmask (SIG_BLOCK, &signals, 0);
  145. /* start a thread to wait for signals */
  146. if (pthread_create (&thread_id, 0, signal_thread, 0)) {
  147. fprintf (stderr, "cannot create signal catching thread");
  148. return -1;
  149. }
  150. pthread_detach (thread_id);
  151. return 0;
  152. }
  153. static void
  154. jack_main ()
  155. {
  156. jack_driver_t *driver;
  157. posix_me_harder ();
  158. if ((engine = jack_engine_new (realtime, realtime_priority)) == 0) {
  159. fprintf (stderr, "cannot create engine\n");
  160. return;
  161. }
  162. if ((driver = jack_driver_load (ADDON_DIR "/jack_alsa.so", alsa_pcm_name, frames_per_interrupt, srate)) == 0) {
  163. fprintf (stderr, "cannot load ALSA driver module\n");
  164. return;
  165. }
  166. jack_use_driver (engine, driver);
  167. jack_run (engine);
  168. jack_wait (engine);
  169. }
  170. static void usage ()
  171. {
  172. fprintf (stderr,
  173. "usage: jackd [ --device OR -d ALSA-PCM-device ]
  174. [ --srate OR -r sample-rate ]
  175. [ --frames-per-interrupt OR -p frames_per_interrupt ]
  176. [ --realtime OR -R [ --realtime-priority OR -P priority ] ]
  177. [ --spoon OR -F ] (don't fork)
  178. ");
  179. }
  180. int
  181. main (int argc, char *argv[])
  182. {
  183. const char *options = "hd:r:p:RP:F";
  184. struct option long_options[] =
  185. {
  186. { "device", 1, 0, 'd' },
  187. { "srate", 1, 0, 'r' },
  188. { "frames-per-interrupt", 1, 0, 'p' },
  189. { "help", 0, 0, 'h' },
  190. { "realtime", 0, 0, 'R' },
  191. { "realtime-priority", 1, 0, 'P' },
  192. { "spoon", 0, 0, 'F' },
  193. { 0, 0, 0, 0 }
  194. };
  195. int option_index;
  196. int opt;
  197. opterr = 0;
  198. while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) {
  199. switch (opt) {
  200. case 'd':
  201. alsa_pcm_name = optarg;
  202. break;
  203. case 'r':
  204. srate = atoi (optarg);
  205. break;
  206. case 'p':
  207. frames_per_interrupt = atoi (optarg);
  208. break;
  209. case 'F':
  210. with_fork = 0;
  211. break;
  212. case 'P':
  213. realtime_priority = atoi (optarg);
  214. break;
  215. case 'R':
  216. realtime = 1;
  217. break;
  218. case 'h':
  219. default:
  220. fprintf (stderr, "unknown option character %c\n", opt);
  221. usage();
  222. return -1;
  223. }
  224. }
  225. if (with_fork) {
  226. jack_main ();
  227. cleanup ();
  228. } else {
  229. int pid = fork ();
  230. if (pid < 0) {
  231. fprintf (stderr, "could not fork jack server (%s)", strerror (errno));
  232. exit (1);
  233. } else if (pid == 0) {
  234. jack_main ();
  235. } else {
  236. jackd_pid = pid;
  237. catch_signals ();
  238. waitpid (pid, NULL, 0);
  239. cleanup ();
  240. }
  241. }
  242. return 0;
  243. }