+}
+
+int main(int argc, char **argv) {
+ int n, logsyslog = !isatty(2);
+ struct sockaddr_un addr;
+ static const int one = 1;
+ struct speaker_message sm;
+ const char *d;
+ char *dir;
+ struct rlimit rl[1];
+
+ set_progname(argv);
+ if(!setlocale(LC_CTYPE, "")) disorder_fatal(errno, "error calling setlocale");
+ while((n = getopt_long(argc, argv, "hVc:dDSs", options, 0)) >= 0) {
+ switch(n) {
+ case 'h': help();
+ case 'V': version("disorder-speaker");
+ case 'c': configfile = optarg; break;
+ case 'd': debugging = 1; break;
+ case 'D': debugging = 0; break;
+ case 'S': logsyslog = 0; break;
+ case 's': logsyslog = 1; break;
+ default: disorder_fatal(0, "invalid option");
+ }
+ }
+ if((d = getenv("DISORDER_DEBUG_SPEAKER"))) debugging = atoi(d);
+ if(logsyslog) {
+ openlog(progname, LOG_PID, LOG_DAEMON);
+ log_default = &log_syslog;
+ }
+ config_uaudio_apis = uaudio_apis;
+ config_per_user = 0;
+ if(config_read(1, NULL)) disorder_fatal(0, "cannot read configuration");
+ /* ignore SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+ /* set nice value */
+ xnice(config->nice_speaker);
+ /* change user */
+ become_mortal();
+ /* make sure we're not root, whatever the config says */
+ if(getuid() == 0 || geteuid() == 0)
+ disorder_fatal(0, "do not run as root");
+ /* Make sure we can't have more than NFDS files open (it would bust our
+ * poll() array) */
+ if(getrlimit(RLIMIT_NOFILE, rl) < 0)
+ disorder_fatal(errno, "getrlimit RLIMIT_NOFILE");
+ if(rl->rlim_cur > NFDS) {
+ rl->rlim_cur = NFDS;
+ if(setrlimit(RLIMIT_NOFILE, rl) < 0)
+ disorder_fatal(errno, "setrlimit to reduce RLIMIT_NOFILE to %lu",
+ (unsigned long)rl->rlim_cur);
+ disorder_info("set RLIM_NOFILE to %lu", (unsigned long)rl->rlim_cur);
+ } else
+ disorder_info("RLIM_NOFILE is %lu", (unsigned long)rl->rlim_cur);
+ /* gcrypt initialization */
+ if(!gcry_check_version(NULL))
+ disorder_fatal(0, "gcry_check_version failed");
+ gcry_control(GCRYCTL_INIT_SECMEM, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ /* create a pipe between the backend callback and the poll() loop */
+ xpipe(sigpipe);
+ nonblock(sigpipe[0]);
+ /* set up audio backend */
+ uaudio_set_format(config->sample_format.rate,
+ config->sample_format.channels,
+ config->sample_format.bits,
+ config->sample_format.bits != 8);
+ early_finish = uaudio_sample_size * uaudio_channels * uaudio_rate;
+ /* TODO other parameters! */
+ backend = uaudio_find(config->api);
+ /* backend-specific initialization */
+ if(backend->configure)
+ backend->configure();
+ uaudio_set("application", "disorder-speaker");
+ backend->start(speaker_callback, NULL);
+ /* create the private socket directory */
+ byte_xasprintf(&dir, "%s/private", config->home);
+ unlink(dir); /* might be a leftover socket */
+ if(mkdir(dir, 0700) < 0 && errno != EEXIST)
+ disorder_fatal(errno, "error creating %s", dir);
+ /* set up the listen socket */
+ listenfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
+ memset(&addr, 0, sizeof addr);
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof addr.sun_path, "%s/private/speaker",
+ config->home);
+ if(unlink(addr.sun_path) < 0 && errno != ENOENT)
+ disorder_error(errno, "removing %s", addr.sun_path);
+ xsetsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+ if(bind(listenfd, (const struct sockaddr *)&addr, sizeof addr) < 0)
+ disorder_fatal(errno, "error binding socket to %s", addr.sun_path);
+ xlisten(listenfd, 128);
+ nonblock(listenfd);
+ disorder_info("version "VERSION" process ID %lu",
+ (unsigned long)getpid());
+ disorder_info("listening on %s", addr.sun_path);
+ memset(&sm, 0, sizeof sm);
+ sm.type = SM_READY;
+ speaker_send(1, &sm);
+ mainloop();
+ disorder_info("stopped (parent terminated)");