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.

335 lines
7.5KB

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