audev.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #include <assert.h>
  2. #include <inttypes.h>
  3. #include <errno.h>
  4. #include <limits.h>
  5. #include <signal.h>
  6. #include <stdarg.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <unistd.h>
  14. #ifdef USE_CAPSICUM
  15. #include <sys/capsicum.h>
  16. #endif
  17. #include <err.h>
  18. #include <sysexits.h>
  19. #include "au.h"
  20. #include "adev.h"
  21. #include "common.h"
  22. #ifndef NAMEMAX
  23. #define NAMEMAX 14
  24. #endif
  25. #ifndef BSIZE
  26. #define BSIZE 0x1000
  27. #endif
  28. enum {
  29. AU2DEV,
  30. DEV2AU
  31. };
  32. #ifdef SIGINFO
  33. static size_t info_bytes = 0;
  34. #endif
  35. #ifdef SIGINFO
  36. static void siginfo(int);
  37. #endif
  38. int
  39. main(int argc, char *argv[])
  40. {
  41. #ifdef USE_CAPSICUM
  42. cap_rights_t rights;
  43. #endif
  44. int cpipe[2];
  45. pid_t cpid = 0;
  46. void *aux = NULL;
  47. size_t auxs = 0;
  48. struct adev *a;
  49. struct au au = AU_INITIALIZER;
  50. const char *dn = NULL;
  51. char *p;
  52. void *b;
  53. size_t bsize = BSIZE;
  54. double time = -1;
  55. ssize_t i;
  56. ssize_t r;
  57. ssize_t t;
  58. uint32_t sps;
  59. uint32_t enc;
  60. int dmode;
  61. int mode;
  62. int c;
  63. setprogname(argv[0]);
  64. dmode = mode = (strstr(getprogname(), "2au") != NULL ||
  65. strstr(getprogname(), "rec") != NULL) ? DEV2AU : AU2DEV;
  66. while ((c = getopt(argc, argv, "RWb:c:d:e:m:r:s:t:")) != -1)
  67. switch (c) {
  68. case 'R':
  69. mode = AU2DEV;
  70. break;
  71. case 'W':
  72. mode = DEV2AU;
  73. break;
  74. case 'b':
  75. if ((bsize = strtosz(optarg)) == 0)
  76. errx(EX_USAGE, "The buffer size must"
  77. " be non-zero");
  78. break;
  79. case 'c':
  80. if (mode != DEV2AU)
  81. goto usage;
  82. if ((au.au_chan = strtou32(optarg)) == 0)
  83. errx(EX_USAGE, "The channel count must"
  84. " be non-zero");
  85. break;
  86. case 'd':
  87. dn = optarg;
  88. break;
  89. case 'e':
  90. if (mode != DEV2AU)
  91. goto usage;
  92. if ((au.au_enc = au_strenc(optarg))
  93. == UINT32_MAX)
  94. errx(EX_USAGE, "Invalid encoding %s",
  95. optarg);
  96. break;
  97. case 'r':
  98. if (mode != DEV2AU)
  99. goto usage;
  100. if ((au.au_rate = strtou32(optarg)) == 0)
  101. errx(EX_USAGE, "The rate must be "
  102. "non-zero");
  103. break;
  104. case 's':
  105. if (mode != DEV2AU)
  106. goto usage;
  107. if (memacat(&aux, &auxs, optarg,
  108. strlen(optarg) + 1) < 0)
  109. errx(EX_OSERR, "Error reallocating"
  110. " auxiliary information");
  111. break;
  112. case 't':
  113. if (mode != DEV2AU)
  114. goto usage;
  115. errno = 0;
  116. if ((time = strtod(optarg, &p)) < 0 ||
  117. p == optarg || *p != '\0' || errno != 0)
  118. err(EX_USAGE, "Invalid number '%s'",
  119. optarg);
  120. break;
  121. default:
  122. goto usage;
  123. }
  124. if ((b = malloc(bsize)) == NULL)
  125. err(EX_OSERR, "Could not allocate the buffer");
  126. if (dn != NULL && strcmp(dn, "-") == 0)
  127. a = ad_fpopen(mode == AU2DEV ? stdout : stdin,
  128. mode == AU2DEV ? A_WRITE : A_READ);
  129. else {
  130. if ((c = open("/dev/null",
  131. mode == AU2DEV ? O_WRONLY : O_RDONLY)) < 0 ||
  132. dup2(c, mode == AU2DEV ? STDOUT_FILENO :
  133. STDIN_FILENO) < 0)
  134. err(EX_OSERR, "Could not replace the standard "
  135. "%s as /dev/null", mode == AU2DEV ? "output"
  136. : "input");
  137. (void)close(c);
  138. a = ad_open(dn, mode == AU2DEV ? A_WRITE : A_READ);
  139. }
  140. if (a == NULL)
  141. err(EX_OSERR, "Could not open the audio device");
  142. #ifdef SIGINFO
  143. (void)signal(SIGINFO, &siginfo);
  144. #endif
  145. if (mode == DEV2AU)
  146. goto dev2au;
  147. au2dev:
  148. while (nread(STDIN_FILENO, b, AU_SIZE) == AU_SIZE) {
  149. #ifdef SIGINFO
  150. info_bytes += AU_SIZE;
  151. #endif
  152. if (au_gethdr(&au, b) != 0)
  153. errx(EX_DATAERR, "Bad header");
  154. if (ad_set(a, "rate channels", au.au_rate, au.au_chan)
  155. != 2)
  156. err(EX_SOFTWARE,
  157. "Could not set audio parameters");
  158. if (ad_set(a, "encoding", au.au_enc) != 1) {
  159. if (cpid > 0)
  160. abort();
  161. if (ad_get(a, "encoding", &enc) != 1)
  162. err(EX_SOFTWARE,
  163. "Could not get desired encoding");
  164. if (pipe(cpipe) == -1)
  165. err(EX_OSERR, "Could not create pipe");
  166. switch (cpid = fork()) {
  167. case -1:
  168. err(EX_OSERR, "Could not fork process");
  169. case 0:
  170. close(cpipe[0]);
  171. if (dup2(cpipe[1], STDOUT_FILENO) == -1)
  172. err(EX_OSERR, "Could not "
  173. "connect the pipe");
  174. auconvp(&au, enc, b, bsize);
  175. err(EX_SOFTWARE,
  176. "Could not execute auconv");
  177. default:
  178. close(cpipe[1]);
  179. if (dup2(cpipe[0], STDIN_FILENO) == -1)
  180. err(EX_OSERR, "Could not "
  181. "connect the pipe");
  182. break;
  183. }
  184. continue;
  185. }
  186. if (c) {
  187. #ifdef USE_CAPSICUM
  188. if (cap_enter() == -1 && errno != ENOSYS)
  189. err(EX_OSERR,
  190. "Could not enter capability mode");
  191. (void)cap_rights_init(&rights, CAP_READ);
  192. if (cap_rights_limit(STDIN_FILENO, &rights)
  193. != 0 && errno != ENOSYS)
  194. err(EX_OSERR, "Could not limit the "
  195. "standard input");
  196. (void)cap_rights_init(&rights, CAP_WRITE);
  197. if (cap_rights_limit(STDOUT_FILENO, &rights)
  198. != 0 && errno != ENOSYS)
  199. err(EX_OSERR, "Could not limit the "
  200. "standard output");
  201. if (cap_rights_limit(STDERR_FILENO, &rights)
  202. != 0 && errno != ENOSYS)
  203. err(EX_OSERR, "Could not limit the "
  204. "standard error");
  205. #endif /* USE_CAPSICUM */
  206. ad_limit(a);
  207. c = 0;
  208. }
  209. for (i = AU_SIZE; i < au.au_off; i += r) {
  210. if ((r = nread(STDIN_FILENO, b,
  211. MIN(bsize, au.au_off - i))) <= 0)
  212. err(EX_DATAERR, "Unexpected EOF");
  213. #ifdef SIGINFO
  214. info_bytes += r;
  215. #endif
  216. }
  217. while (au.au_size > 0 && (r = read(STDIN_FILENO, b,
  218. MIN(bsize, au.au_size))) > 0) {
  219. for (i = 0; i < r; i += t)
  220. if ((t = ad_write(a,
  221. (char *)b + i, r - i)) < 0)
  222. err(EX_IOERR, "Error while "
  223. "playing audio");
  224. else if (t == 0)
  225. goto read_ret;
  226. if (au.au_size != UINT32_MAX)
  227. au.au_size -= r;
  228. #ifdef SIGINFO
  229. info_bytes += r;
  230. #endif
  231. }
  232. if (r < 0)
  233. err(EX_DATAERR, "Error while reading audio");
  234. if (au.au_size == UINT32_MAX)
  235. break;
  236. }
  237. read_ret:
  238. ad_close(a);
  239. free(b);
  240. return (EX_OK);
  241. dev2au:
  242. #ifdef USE_CAPSICUM
  243. if (cap_enter() == -1 && errno != ENOSYS)
  244. err(EX_OSERR, "Could not enter capibility mode");
  245. (void)cap_rights_init(&rights, CAP_WRITE);
  246. if (cap_rights_limit(STDOUT_FILENO, &rights) == -1 &&
  247. errno != ENOSYS)
  248. err(EX_OSERR, "Could not limit the standard output");
  249. if (cap_rights_limit(STDERR_FILENO, &rights) == -1 &&
  250. errno != ENOSYS)
  251. err(EX_OSERR, "Could not limit the standard error");
  252. #endif
  253. if (ad_limit(a) == -1 && errno != ENOSYS)
  254. err(EX_OSERR, "Could not limit the audio input");
  255. if (ad_set(a, "encoding rate channels", au.au_enc, au.au_rate,
  256. au.au_chan) != 3)
  257. err(EX_SOFTWARE, "Could not set the audio parametes");
  258. au.au_off = AU_SIZE + auxs;
  259. sps = au_ssize(&au) * au.au_rate;
  260. do {
  261. if (time > 0) {
  262. au.au_size = ~sps + 1;
  263. if (time >= au.au_size / sps)
  264. time -= au.au_size / sps;
  265. else {
  266. au.au_size = time * (double)sps;
  267. time = 0;
  268. }
  269. }
  270. (void)au_puthdr(&au, b);
  271. if (nwrite(STDOUT_FILENO, b, AU_SIZE) != AU_SIZE)
  272. err(EX_IOERR, "Could not write audio header");
  273. #ifdef SIGINFO
  274. info_bytes += AU_SIZE;
  275. #endif
  276. if (auxs > 0 && nwrite(STDOUT_FILENO, aux, auxs) <= 0)
  277. err(EX_IOERR, "Error while writing to the "
  278. "standard output");
  279. while (au.au_size > 0 && (r = ad_read(a, b,
  280. MIN(bsize, au.au_size))) > 0) {
  281. if (nwrite(STDOUT_FILENO, b, r) != r)
  282. err(EX_IOERR,
  283. "Error while writing audio");
  284. if (au.au_size != UINT32_MAX)
  285. au.au_size -= r;
  286. #ifdef SIGINFO
  287. info_bytes += r;
  288. #endif
  289. }
  290. if (r < 0)
  291. err(EX_IOERR,
  292. "Error reading from audio device");
  293. if (r == 0)
  294. break;
  295. } while (au.au_size == UINT32_MAX || time != 0);
  296. ad_close(a);
  297. free(b);
  298. return (EX_OK);
  299. usage:
  300. if (mode == DEV2AU) {
  301. (void)fprintf(stderr, "usage: %s %s "
  302. "[-b buffer-size] [-d device]\n", getprogname(),
  303. dmode == DEV2AU ? "[-W]" : "-W");
  304. return (EX_USAGE);
  305. }
  306. (void)fprintf(stderr, "usage: %s %s [-b buffer-size] "
  307. "[-d device] [-e encoding]\n", getprogname(),
  308. dmode == AU2DEV ? "[-R]" : "-R");
  309. for (i = 0; i < strlen(getprogname()); i++)
  310. (void)fputc(' ', stderr);
  311. (void)fprintf(stderr, " [-c channels] [-r rate]"
  312. " [-s string] [-t time]\n");
  313. return (EX_USAGE);
  314. }
  315. #ifdef SIGINFO
  316. void
  317. siginfo(int sig)
  318. {
  319. /* XXXXXXX: XXXXXXiB processed */
  320. char b[NAMEMAX + 2 + 8 + 11];
  321. size_t i;
  322. i = strlen(getprogname());
  323. if (i > NAMEMAX) {
  324. (void)memcpy(b, getprogname(), NAMEMAX - 3);
  325. (void)memcpy(b + NAMEMAX - 3, "...", 3);
  326. i = NAMEMAX;
  327. } else
  328. (void)memcpy(b, getprogname(), i);
  329. (void)memcpy(b + i, ": ", 2);
  330. i += 2;
  331. i += szwrite(b + i, info_bytes, "B", "iB");
  332. (void)memcpy(b + i, " processed\n", 11);
  333. i += 11;
  334. (void)write(STDERR_FILENO, b, i);
  335. }
  336. #endif