diff --git a/Makefile b/Makefile index 7a5c3051..42e44128 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ endif PREFIX ?= /usr/local -SRCS := shairport.c daemon.c rtsp.c mdns.c common.c rtp.c player.c alac.c audio.c audio_dummy.c audio_pipe.c +SRCS := shairport.c daemon.c rtsp.c mdns.c mdns_external.c common.c rtp.c player.c alac.c audio.c audio_dummy.c audio_pipe.c ifdef CONFIG_SNDIO SRCS += audio_sndio.c @@ -28,7 +28,7 @@ SRCS += audio_alsa.c endif ifdef CONFIG_AVAHI -SRCS += avahi.c +SRCS += mdns_avahi.c endif ifndef CONFIG_HAVE_GETOPT_H diff --git a/avahi.h b/avahi.h deleted file mode 100644 index 0ad53ff0..00000000 --- a/avahi.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _AVAHI_H -#define _AVAHI_H - -int avahi_register(char *srvname); -void avahi_unregister(void); - -#endif // _AVAHI_H diff --git a/mdns.c b/mdns.c index 66d88363..9720a74b 100644 --- a/mdns.c +++ b/mdns.c @@ -25,28 +25,29 @@ */ -#include #include #include -#include #include "config.h" #include "common.h" #include "mdns.h" + #ifdef CONFIG_AVAHI -#include "avahi.h" +extern mdns_backend mdns_avahi; #endif -int mdns_pid = 0; +extern mdns_backend mdns_external_avahi; +extern mdns_backend mdns_external_dns_sd; -void mdns_unregister(void) { +static mdns_backend *mdns_backends[] = { #ifdef CONFIG_AVAHI - avahi_unregister(); + &mdns_avahi, #endif + &mdns_external_avahi, + &mdns_external_dns_sd, + NULL +}; - if (mdns_pid) - kill(mdns_pid, SIGTERM); - mdns_pid = 0; -} +static mdns_backend *backend = NULL; void mdns_register(void) { char *mdns_apname = malloc(strlen(config.apname) + 14); @@ -59,31 +60,26 @@ void mdns_register(void) { *p++ = '@'; strcpy(p, config.apname); -#ifdef CONFIG_AVAHI - if (avahi_register(mdns_apname)) - return; - warn("avahi_register failed, falling back to external programs"); -#endif + backend = NULL; + mdns_backend **s = NULL; + for (s = mdns_backends; *s; s++) + { + int error = (*s)->mdns_register(mdns_apname, config.port); + if (error == 0) + { + backend = *s; + break; + } + } - if ((mdns_pid = fork())) - return; - - char mdns_port[6]; - sprintf(mdns_port, "%d", config.port); - - char *argv[] = { - NULL, mdns_apname, "_raop._tcp", mdns_port, MDNS_RECORD, NULL - }; - - argv[0] = "avahi-publish-service"; - execvp(argv[0], argv); - - argv[0] = "mDNSPublish"; - execvp(argv[0], argv); - - char *mac_argv[] = {"dns-sd", "-R", mdns_apname, "_raop._tcp", ".", - mdns_port, MDNS_RECORD, NULL}; - execvp(mac_argv[0], mac_argv); - - die("Could not establish mDNS advertisement!"); + if (backend == NULL) + die("Could not establish mDNS advertisement!"); } + +void mdns_unregister(void) { + if (backend) { + backend->mdns_unregister(); + } +} + + diff --git a/mdns.h b/mdns.h index 9fc5792f..4b5f839d 100644 --- a/mdns.h +++ b/mdns.h @@ -6,6 +6,11 @@ extern int mdns_pid; void mdns_unregister(void); void mdns_register(void); +typedef struct { + int (*mdns_register)(char *apname, int port); + void (*mdns_unregister)(void); +} mdns_backend; + #define MDNS_RECORD "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", \ "ss=16", "sr=44100", "vn=3", "txtvers=1", \ config.password ? "pw=true" : "pw=false" diff --git a/avahi.c b/mdns_avahi.c similarity index 92% rename from avahi.c rename to mdns_avahi.c index f38c4129..d927d51c 100644 --- a/avahi.c +++ b/mdns_avahi.c @@ -38,6 +38,7 @@ static AvahiEntryGroup *group = NULL; static AvahiThreadedPoll *tpoll = NULL; static char *name = NULL; +static int port = 0; static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, @@ -67,7 +68,7 @@ static void register_service(AvahiClient *c) { "_raop._tcp", NULL, NULL, - config.port, + port, MDNS_RECORD, NULL); if (ret < 0) @@ -100,14 +101,15 @@ static void client_callback(AvahiClient *c, } } -int avahi_register(char *srvname) { +static int avahi_register(char *srvname, int srvport) { debug(1, "avahi: avahi_register\n"); name = strdup(srvname); + port = srvport; int err; if (!(tpoll = avahi_threaded_poll_new())) { warn("couldn't create avahi threaded tpoll!"); - return 0; + return -1; } if (!(client = avahi_client_new(avahi_threaded_poll_get(tpoll), 0, @@ -115,18 +117,18 @@ int avahi_register(char *srvname) { NULL, &err))) { warn("couldn't create avahi client: %s!", avahi_strerror(err)); - return 0; + return -1; } if (avahi_threaded_poll_start(tpoll) < 0) { warn("couldn't start avahi tpoll thread"); - return 0; + return -1; } - return 1; + return 0; } -void avahi_unregister(void) { +static void avahi_unregister(void) { debug(1, "avahi: avahi_unregister\n"); if (tpoll) avahi_threaded_poll_stop(tpoll); @@ -137,4 +139,9 @@ void avahi_unregister(void) { name = NULL; } +mdns_backend mdns_avahi = +{ + .mdns_register = avahi_register, + .mdns_unregister = avahi_unregister +}; diff --git a/mdns_external.c b/mdns_external.c new file mode 100644 index 00000000..e86d2fb7 --- /dev/null +++ b/mdns_external.c @@ -0,0 +1,144 @@ +/* + * mDNS registration handler. This file is part of Shairport. + * Copyright (c) Paul Lietar 2013 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "common.h" +#include "mdns.h" + +int mdns_pid = 0; + +/* + * Do a fork followed by a execvp, handling execvp errors correctly. + * Return the pid of the new process upon success, or -1 if something failed. + * Check errno for error details. + */ +static int fork_execvp(const char *file, char *const argv[]) { + int execpipe[2]; + int pid = 0; + if (pipe(execpipe) < 0) { + return -1; + } + + if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { + close(execpipe[0]); + close(execpipe[1]); + return -1; + } + + pid = fork(); + if (pid < 0) { + close(execpipe[0]); + close(execpipe[1]); + return -1; + } + else if(pid == 0) { // Child + close(execpipe[0]); // Close the read end + execvp(file, argv); + + // If we reach this point then execve has failed. + // Write erno's value into the pipe and exit. + write(execpipe[1], &errno, sizeof(errno)); + + _exit(-1); + return 0; // Just to make the compiler happy. + } + else { // Parent + close(execpipe[1]); // Close the write end + + int childErrno; + // Block until child closes the pipe or sends errno. + if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno)) { // We received errno + errno = childErrno; + return -1; + } + else { // Child closed the pipe. execvp was successful. + return pid; + } + } +} + +static int mdns_external_avahi_register(char *apname, int port) { + char mdns_port[6]; + sprintf(mdns_port, "%d", config.port); + + char *argv[] = { + NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD, NULL + }; + + argv[0] = "avahi-publish-service"; + mdns_pid = fork_execvp(argv[0], argv); + if (mdns_pid > 0) + return 0; + else + warn("Calling %s failed !", argv[0]); + + argv[0] = "mDNSPublish"; + mdns_pid = fork_execvp(argv[0], argv); + if (mdns_pid > 0) + return 0; + else + warn("Calling %s failed !", argv[0]); + + // If we reach here, both execvp calls failed. + return -1; +} + +static int mdns_external_dns_sd_register(char *apname, int port) { + char mdns_port[6]; + sprintf(mdns_port, "%d", config.port); + + char *argv[] = {"dns-sd", "-R", apname, "_raop._tcp", ".", + mdns_port, MDNS_RECORD, NULL}; + + mdns_pid = fork_execvp(argv[0], argv); + if (mdns_pid > 0) + return 0; + else + warn("Calling %s failed !", argv[0]); + + return -1; +} + +static void kill_mdns_child(void) { + if (mdns_pid) + kill(mdns_pid, SIGTERM); + mdns_pid = 0; +} + +mdns_backend mdns_external_avahi = { + .mdns_register = mdns_external_avahi_register, + .mdns_unregister = kill_mdns_child +}; + +mdns_backend mdns_external_dns_sd = { + .mdns_register = mdns_external_dns_sd_register, + .mdns_unregister = kill_mdns_child +}; + diff --git a/shairport.c b/shairport.c index b30f4948..8140acd4 100644 --- a/shairport.c +++ b/shairport.c @@ -187,7 +187,7 @@ void signal_setup(void) { // setting this to SIG_IGN would prevent signalling any threads. struct sigaction sa; memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_SIGINFO; + sa.sa_flags = SA_SIGINFO | SA_RESTART; sa.sa_sigaction = &sig_ignore; sigaction(SIGUSR1, &sa, NULL); @@ -198,7 +198,6 @@ void signal_setup(void) { sa.sa_sigaction = &sig_logrotate; sigaction(SIGHUP, &sa, NULL); - sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = &sig_child; sigaction(SIGCHLD, &sa, NULL); }