From bb3a78ccc2c62b413a5c7a46cdb503095515d0a7 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 22 Sep 2017 10:11:45 +0100 Subject: [PATCH 1/6] 3.1.2 Remove the "rc0" tag, duh. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d4d04f0c..b511167e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.1.2rc0], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.1.2], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 5d5802db92eb2265785305d51253b78c49b61f6b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 23 Sep 2017 05:25:48 -0700 Subject: [PATCH 2/6] Create the directory for the PID file, if necessary --- common.h | 1 + shairport.c | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/common.h b/common.h index 40bf594d..bad4ef70 100644 --- a/common.h +++ b/common.h @@ -118,6 +118,7 @@ typedef struct { int daemonise; int daemonise_store_pid; // don't try to save a PID file char *piddir; + char *computed_piddir; // the actual pid directory to create, if any int logOutputLevel; // log output level int statistics_requested, use_negotiated_latencies; enum playback_mode_type playback_mode; diff --git a/shairport.c b/shairport.c index 6249d636..a2645f7a 100644 --- a/shairport.c +++ b/shairport.c @@ -893,6 +893,18 @@ int parse_options(int argc, char **argv) { free(i2); free(i3); free(vs); + + // now, check and calculate the pid directory +#ifdef USE_CUSTOM_PID_DIR + char *use_this_pid_dir = PIDDIR; +#else + char *use_this_pid_dir = "/var/run/shairport-sync"; +#endif + // debug(1,"config.piddir \"%s\".",config.piddir); + if (config.piddir) + use_this_pid_dir = config.piddir; + if (use_this_pid_dir) + config.computed_piddir = strdup(use_this_pid_dir); return optind + 1; } @@ -942,16 +954,9 @@ void shairport_startup_complete(void) { } const char *pid_file_proc(void) { -#ifdef USE_CUSTOM_PID_DIR - char *use_this_pid_dir = PIDDIR; -#else - char *use_this_pid_dir = "/var/run/shairport-sync"; -#endif - // debug(1,"config.piddir \"%s\".",config.piddir); - if (config.piddir) - use_this_pid_dir = config.piddir; + char fn[8192]; - snprintf(fn, sizeof(fn), "%s/%s.pid", use_this_pid_dir, + snprintf(fn, sizeof(fn), "%s/%s.pid", config.computed_piddir, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); // debug(1,"fn \"%s\".",fn); return strdup(fn); @@ -1192,6 +1197,8 @@ int main(int argc, char **argv) { /* Create the PID file if required */ if (config.daemonise_store_pid) { + /* Create the PID directory if required -- we don't really care about the result */ + mkdir(config.computed_piddir, 0700); if (daemon_pid_file_create() < 0) { daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); daemon_retval_send(2); From a9b6c85ada626b3ce2b067f95c383e02b7081635 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 23 Sep 2017 05:26:37 -0700 Subject: [PATCH 3/6] Create the directory for the PID file, if necessary --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b511167e..f9783b31 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.1.2], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.1.3rc0], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 825b418b949c3913213690128e05993df1426739 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 24 Sep 2017 15:52:26 +0100 Subject: [PATCH 4/6] Make PID directory path creation recursive and tidy up some code formatting. --- common.h | 2 +- mdns_dns_sd.c | 2 +- mdns_external.c | 2 +- shairport.c | 81 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/common.h b/common.h index bad4ef70..4c2584e9 100644 --- a/common.h +++ b/common.h @@ -119,7 +119,7 @@ typedef struct { int daemonise_store_pid; // don't try to save a PID file char *piddir; char *computed_piddir; // the actual pid directory to create, if any - int logOutputLevel; // log output level + int logOutputLevel; // log output level int statistics_requested, use_negotiated_latencies; enum playback_mode_type playback_mode; char *cmd_start, *cmd_stop, *cmd_set_volume; diff --git a/mdns_dns_sd.c b/mdns_dns_sd.c index b4b7e4ac..5785d103 100644 --- a/mdns_dns_sd.c +++ b/mdns_dns_sd.c @@ -24,8 +24,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "mdns.h" #include "common.h" +#include "mdns.h" #include #include #include diff --git a/mdns_external.c b/mdns_external.c index fcd3a323..b8698b8c 100644 --- a/mdns_external.c +++ b/mdns_external.c @@ -24,8 +24,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "mdns.h" #include "common.h" +#include "mdns.h" #include #include #include diff --git a/shairport.c b/shairport.c index a2645f7a..130700fe 100644 --- a/shairport.c +++ b/shairport.c @@ -114,6 +114,54 @@ static void sig_connect_audio_output(int foo, siginfo_t *bar, void *baz) { set_requested_connection_state_to_output(1); } +// The following two functions are adapted slightly and with thanks from Jonathan Leffler's sample +// code at +// https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux + +int do_mkdir(const char *path, mode_t mode) { + struct stat st; + int status = 0; + + if (stat(path, &st) != 0) { + /* Directory does not exist. EEXIST for race condition */ + if (mkdir(path, mode) != 0 && errno != EEXIST) + status = -1; + } else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + status = -1; + } + + return (status); +} + +// mkpath - ensure all directories in path exist +// Algorithm takes the pessimistic view and works top-down to ensure +// each directory in path exists, rather than optimistically creating +// the last element and working backwards. + +int mkpath(const char *path, mode_t mode) { + char *pp; + char *sp; + int status; + char *copypath = strdup(path); + + status = 0; + pp = copypath; + while (status == 0 && (sp = strchr(pp, '/')) != 0) { + if (sp != pp) { + /* Neither root nor double slash in path */ + *sp = '\0'; + status = do_mkdir(copypath, mode); + *sp = '/'; + } + pp = sp + 1; + } + if (status == 0) + status = do_mkdir(path, mode); + free(copypath); + return (status); +} + char *get_version_string() { char *version_string = malloc(200); if (version_string) { @@ -893,8 +941,8 @@ int parse_options(int argc, char **argv) { free(i2); free(i3); free(vs); - - // now, check and calculate the pid directory + +// now, check and calculate the pid directory #ifdef USE_CUSTOM_PID_DIR char *use_this_pid_dir = PIDDIR; #else @@ -954,7 +1002,7 @@ void shairport_startup_complete(void) { } const char *pid_file_proc(void) { - + char fn[8192]; snprintf(fn, sizeof(fn), "%s/%s.pid", config.computed_piddir, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown"); @@ -1180,10 +1228,23 @@ int main(int argc, char **argv) { return 255; } - if (ret != 0) - daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret); + switch (ret) { + case 0: + break; + case 1: + daemon_log(LOG_ERR, + "daemon failed to launch: could not close open file descriptors after forking."); + break; + case 2: + daemon_log(LOG_ERR, "daemon failed to launch: could not create PID file."); + break; + case 3: + daemon_log(LOG_ERR, "daemon failed to launch: could not create or access PID directory."); + break; + default: + daemon_log(LOG_ERR, "daemon failed to launch, error %i.", ret); + } return ret; - } else { /* The daemon */ /* Close FDs */ @@ -1198,7 +1259,13 @@ int main(int argc, char **argv) { /* Create the PID file if required */ if (config.daemonise_store_pid) { /* Create the PID directory if required -- we don't really care about the result */ - mkdir(config.computed_piddir, 0700); + printf("PID directory is \"%s\".", config.computed_piddir); + int result = mkpath(config.computed_piddir, 0700); + if ((result != 0) && (result != -EEXIST)) { + // error creating or accessing the PID file directory + daemon_retval_send(3); + goto finish; + } if (daemon_pid_file_create() < 0) { daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); daemon_retval_send(2); From 60ead5a3cd09749fc8bcfa2206318b21e4def3b2 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 25 Sep 2017 17:09:04 +0100 Subject: [PATCH 5/6] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7a5b3aa9..86659846 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Shairport Sync ============= -Shairport Sync is an AirPlay audio player – it plays audio streamed from iTunes, iOS and macOS devices and AirPlay sources such as Quicktime Player and [ForkedDaapd](http://ejurgensen.github.io/forked-daapd/), among others. +Shairport Sync is an AirPlay audio player – it plays audio streamed from iTunes, iOS, Apple TV and macOS devices and AirPlay sources such as Quicktime Player and [ForkedDaapd](http://ejurgensen.github.io/forked-daapd/), among others. Audio played by a Shairport Sync-powered device stays synchronised with the source and hence with similar devices playing the same source. In this way, synchronised multi-room audio is possible for suitable players. (Hence the name Shairport Sync, BTW.) @@ -50,7 +50,7 @@ Shairport Sync works on a wide variety of Linux devices and FreeBSD. It works on Shairport Sync will work with PulseAudio, which is installed in many desktop Linuxes. PulseAudio normally runs in the *user mode* but can be configured to run in *system mode*, though this is not recommended. Shairport Sync can work with it in either mode. -Shairport Sync runs well on the Raspberry Pi on USB and I2S cards. It can drive the built-in sound card – see the note below on configuring the Raspberry Pi to make best use of it. +Shairport Sync runs well on the Raspberry Pi on USB and I2S cards. It can drive the built-in sound card – see the note below on configuring the Raspberry Pi to make best use of it. It runs well on the Raspberry Pi Zero W with a suitable USB or I2S card. Shairport Sync runs natively on FreeBSD using the `sndio` sound system. @@ -85,7 +85,7 @@ Note that the installation uses the libao library and so synchronisation is not Sincere thanks to all package contributors! -If you wish to build and install the latest version of Shairport Sync on Debian, Ubuntu, Fedora or Arch platforms, please follow the General Build Instructions. When the program has been installed, refer to the section on Configuring Shairport Sync that follows. To build Shairport Sync from sources on FreeBSD please refer to [FREEBSD.md](https://github.com/mikebrady/shairport-sync/blob/master/FREEBSD.md). +If you wish to build and install the latest version of Shairport Sync on Debian, Ubuntu, Fedora or Arch platforms, please continue to follow these instructions. When the program has been compiled and installed, refer to the section on Configuring Shairport Sync that follows. To build Shairport Sync from sources on FreeBSD please refer to [FREEBSD.md](https://github.com/mikebrady/shairport-sync/blob/master/FREEBSD.md). **Remove Old Versions Of Shairport Sync** @@ -171,7 +171,7 @@ $ autoreconf -i -f **Choose the appropriate `--with-*` options:** -(Don't worry -- there's a standard set of configuration options recommended further down.) +(Don't worry -- there's a recommended set of configuration options further down.) - `--with-alsa` include the ALSA backend module to audio to be output through the Advanced Linux Sound Architecture (ALSA) system directly. This is recommended for highest quality. - `--with-pa` include the PulseAudio audio back end. This is recommended if your Linux installation already has PulseAudio installed. Although ALSA would be better, it requires direct and exclusive access to to a real (hardware) soundcard, and this is often impractical if PulseAudio is installed. @@ -193,7 +193,7 @@ $ autoreconf -i -f If you wish to have Shairport Sync start automatically when your system boots, you need to figure out what so-called "init system" your system is using. (If you are using Shairport Sync with PulseAudio, as installed in many desktop systems, this section doesn't apply.) -There are a number of init systems in use: `systemd`, `upstart` and "System V" among others, and it's actually difficult to be certain which one your system is using. Fortunately, for Shairport Sync, all you have to do is figure out if it's a `systemd` init system or not. If it is not a `systemd` init system, you can assume that it is either a System V init system or else it is compatible with a System V init system. Recent systems tend to use `systemd`, whereas older systems use `upstart` or the earleir System V init system. +There are a number of init systems in use: `systemd`, `upstart` and "System V" among others, and it's actually difficult to be certain which one your system is using. Fortunately, for Shairport Sync, all you have to do is figure out if it's a `systemd` init system or not. If it is not a `systemd` init system, you can assume that it is either a System V init system or else it is compatible with a System V init system. Recent systems tend to use `systemd`, whereas older systems use `upstart` or the earlier System V init system. The easiest way to look at the first few lines of the `init` manual. Enter the command: From e2d37dbe34d1c711389e5a2e6144165be38d9d3e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 25 Sep 2017 17:10:46 +0100 Subject: [PATCH 6/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86659846..6720e264 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ For more about the motivation behind Shairport Sync, please see the wiki at http Synchronisation, Latency, "Stuffing" --------- -The AirPlay protocol uses an agreed *latency* – the time difference between the time represented by a sound sample's `timestamp` and the time it is actually played by the audio output device, typically a Digital to Audio Converter (DAC). The latency to be used is specified by the audio source when it negotiates with Shairport Sync. Most sources set a latency of two seconds. Recent versions of iTunes and forkedDaapd use a latency of just over 2.25 seconds. A latency of this length allows AirPlay players to correct for network delays, processing time variations and so on. +The AirPlay protocol uses an agreed *latency* – the time difference, or delay, between the time represented by a sound sample's `timestamp` and the time it is actually played by the audio output device, typically a Digital to Audio Converter (DAC). The latency to be used is specified by the audio source when it negotiates with Shairport Sync. Most sources set a latency of two seconds. Recent versions of iTunes and forkedDaapd use a latency of just over 2.25 seconds. A latency of this length allows AirPlay players to correct for network delays, processing time variations and so on. As mentioned previously, Shairport Sync implements full audio synchronisation when used with `alsa`, `sndio` or PulseAudio systems. This is done by monitoring the timestamps present in data coming from the audio source and the timing information from the audio system, e.g. `alsa`. To maintain the latency required for exact synchronisation, if the output device is running slow relative to the source, Shairport Sync will delete frames of audio to allow the device to keep up. If the output device is running fast, Shairport Sync will insert frames to keep time. The number of frames inserted or deleted is so small as to be almost inaudible on normal audio material. Frames are inserted or deleted as necessary at pseudorandom intervals. Alternatively, with `libsoxr` support, Shairport Sync can resample the audio feed to ensure the output device can keep up. This is less obtrusive than insertion and deletion but requires a good deal of processing power — most embedded devices probably can't support it. The process of insertion/deletion or resampling is rather inelegantly called “stuffing”.