summaryrefslogtreecommitdiff
path: root/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'common.c')
-rw-r--r--common.c327
1 files changed, 238 insertions, 89 deletions
diff --git a/common.c b/common.c
index ff643ef..b48adad 100644
--- a/common.c
+++ b/common.c
@@ -29,6 +29,8 @@
#include "common.h"
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
#include <memory.h>
#include <poll.h>
#include <popt.h>
@@ -87,7 +89,12 @@
void set_alsa_out_dev(char *);
#endif
-// always lock use this when accessing the fp_time_at_last_debug_message
+config_t config_file_stuff;
+int emergency_exit;
+pthread_t main_thread_id;
+uint64_t ns_time_at_startup, ns_time_at_last_debug_message;
+
+// always lock use this when accessing the ns_time_at_last_debug_message
static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t the_conn_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -96,8 +103,8 @@ const char *sps_format_description_string_array[] = {
"unknown", "S8", "U8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE",
"S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE", "S32_BE", "auto", "invalid"};
-const char *sps_format_description_string(enum sps_format_t format) {
- if ((format >= SPS_FORMAT_UNKNOWN) && (format <= SPS_FORMAT_AUTO))
+const char *sps_format_description_string(sps_format_t format) {
+ if (format <= SPS_FORMAT_AUTO)
return sps_format_description_string_array[format];
else
return sps_format_description_string_array[SPS_FORMAT_INVALID];
@@ -116,7 +123,7 @@ static void (*sps_log)(int prio, const char *t, ...) = daemon_log;
static void (*sps_log)(int prio, const char *t, ...) = syslog;
#endif
-void do_sps_log(__attribute__((unused)) int prio, const char *t, ...) {
+void do_sps_log_to_stderr(__attribute__((unused)) int prio, const char *t, ...) {
char s[1024];
va_list args;
va_start(args, t);
@@ -125,7 +132,91 @@ void do_sps_log(__attribute__((unused)) int prio, const char *t, ...) {
fprintf(stderr, "%s\n", s);
}
-void log_to_stderr() { sps_log = do_sps_log; }
+void do_sps_log_to_stdout(__attribute__((unused)) int prio, const char *t, ...) {
+ char s[1024];
+ va_list args;
+ va_start(args, t);
+ vsnprintf(s, sizeof(s), t, args);
+ va_end(args);
+ fprintf(stdout, "%s\n", s);
+}
+
+int create_log_file(const char *path) {
+ int fd = -1;
+ if (path != NULL) {
+ char *dirc = strdup(path);
+ if (dirc) {
+ char *dname = dirname(dirc);
+ // create the directory, if necessary
+ int result = 0;
+ if (dname) {
+ char *pdir = realpath(dname, NULL); // will return a NULL if the directory doesn't exist
+ if (pdir == NULL) {
+ mode_t oldumask = umask(000);
+ result = mkpath(dname, 0777);
+ umask(oldumask);
+ } else {
+ free(pdir);
+ }
+ if ((result == 0) || (result == -EEXIST)) {
+ // now open the file
+ fd = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if ((fd == -1) && (errno == EEXIST))
+ fd = open(path, O_WRONLY | O_APPEND | O_NONBLOCK);
+
+ if (fd >= 0) {
+ // now we switch to blocking mode
+ int flags = fcntl(fd, F_GETFL);
+ if (flags == -1) {
+ // strerror_r(errno, (char
+ //*)errorstring, sizeof(errorstring));
+ // debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".",
+ // errno,
+ // (char *)errorstring, pathname);
+ } else {
+ flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ // if (flags == -1) {
+ // strerror_r(errno,
+ //(char *)errorstring, sizeof(errorstring));
+ // debug(1, "create_log_file -- error %d
+ //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno,
+ //(char *)errorstring, pathname);
+ }
+ }
+ }
+ }
+ free(dirc);
+ }
+ }
+ return fd;
+}
+
+void do_sps_log_to_fd(__attribute__((unused)) int prio, const char *t, ...) {
+ char s[1024];
+ va_list args;
+ va_start(args, t);
+ vsnprintf(s, sizeof(s), t, args);
+ va_end(args);
+ if (config.log_fd == -1)
+ config.log_fd = create_log_file(config.log_file_path);
+ if (config.log_fd >= 0) {
+ dprintf(config.log_fd, "%s\n", s);
+ } else if (errno != ENXIO) { // maybe there is a pipe there but not hooked up
+ fprintf(stderr, "%s\n", s);
+ }
+}
+
+void log_to_stderr() { sps_log = do_sps_log_to_stderr; }
+void log_to_stdout() { sps_log = do_sps_log_to_stdout; }
+void log_to_file() { sps_log = do_sps_log_to_fd; }
+void log_to_syslog() {
+#ifdef CONFIG_LIBDAEMON
+ sps_log = daemon_log;
+#else
+ sps_log = syslog;
+#endif
+}
shairport_cfg config;
@@ -198,10 +289,10 @@ char *generate_preliminary_string(char *buffer, size_t buffer_length, double tss
insertion_point = insertion_point + strlen(insertion_point);
space_remaining = space_remaining - strlen(insertion_point);
}
-
if (prefix) {
snprintf(insertion_point, space_remaining, "%s", prefix);
insertion_point = insertion_point + strlen(insertion_point);
+ space_remaining = space_remaining - strlen(insertion_point);
}
return insertion_point;
}
@@ -214,17 +305,17 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
char *s;
if (debuglev) {
pthread_mutex_lock(&debug_timing_lock);
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
+ uint64_t time_now = get_absolute_time_in_ns();
+ uint64_t time_since_start = time_now - ns_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
+ ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
- uint64_t divisor = (uint64_t)1 << 32;
- s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
- 1.0 * time_since_last_debug_message / divisor, filename,
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
+ 1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *fatal error: ");
} else {
- s = b;
+ strncpy(b, "fatal error: ", sizeof(b));
+ s = b + strlen(b);
}
va_list args;
va_start(args, format);
@@ -232,7 +323,8 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
va_end(args);
sps_log(LOG_ERR, "%s", b);
pthread_setcancelstate(oldState, NULL);
- abort(); // exit() doesn't always work, by heaven.
+ emergency_exit = 1;
+ exit(EXIT_FAILURE);
}
void _warn(const char *filename, const int linenumber, const char *format, ...) {
@@ -243,17 +335,17 @@ void _warn(const char *filename, const int linenumber, const char *format, ...)
char *s;
if (debuglev) {
pthread_mutex_lock(&debug_timing_lock);
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
+ uint64_t time_now = get_absolute_time_in_ns();
+ uint64_t time_since_start = time_now - ns_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
+ ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
- uint64_t divisor = (uint64_t)1 << 32;
- s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
- 1.0 * time_since_last_debug_message / divisor, filename,
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
+ 1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *warning: ");
} else {
- s = b;
+ strncpy(b, "warning: ", sizeof(b));
+ s = b + strlen(b);
}
va_list args;
va_start(args, format);
@@ -271,14 +363,13 @@ void _debug(const char *filename, const int linenumber, int level, const char *f
char b[1024];
b[0] = 0;
pthread_mutex_lock(&debug_timing_lock);
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
+ uint64_t time_now = get_absolute_time_in_ns();
+ uint64_t time_since_start = time_now - ns_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
+ ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
- uint64_t divisor = (uint64_t)1 << 32;
- char *s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
- 1.0 * time_since_last_debug_message / divisor, filename,
+ char *s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
+ 1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " ");
va_list args;
va_start(args, format);
@@ -296,14 +387,13 @@ void _inform(const char *filename, const int linenumber, const char *format, ...
char *s;
if (debuglev) {
pthread_mutex_lock(&debug_timing_lock);
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
+ uint64_t time_now = get_absolute_time_in_ns();
+ uint64_t time_since_start = time_now - ns_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
+ ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
- uint64_t divisor = (uint64_t)1 << 32;
- s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
- 1.0 * time_since_last_debug_message / divisor, filename,
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
+ 1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " ");
} else {
s = b;
@@ -1009,43 +1099,77 @@ uint64_t get_absolute_time_in_fp() {
return time_now_fp;
}
-ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, int timeout) {
- // timeout is in milliseconds
- void *ibuf = (void *)buf;
- size_t bytes_remaining = count;
- int rc = 1;
- struct pollfd ufds[1];
- while ((bytes_remaining > 0) && (rc > 0)) {
- // check that we can do some writing
- ufds[0].fd = fd;
- ufds[0].events = POLLOUT;
- rc = poll(ufds, 1, timeout);
- if (rc < 0) {
- // debug(1, "non-blocking write error waiting for pipe to become ready for writing...");
- } else if (rc == 0) {
- // warn("non-blocking write timeout waiting for pipe to become ready for writing");
- rc = -1;
- errno = -ETIMEDOUT;
- } else { // rc > 0, implying it might be ready
- ssize_t bytes_written = write(fd, ibuf, bytes_remaining);
- if (bytes_written == -1) {
- // debug(1,"Error %d in non_blocking_write: \"%s\".",errno,strerror(errno));
- rc = bytes_written; // to imitate the return from write()
- } else {
- ibuf += bytes_written;
- bytes_remaining -= bytes_written;
- }
- }
+uint64_t get_absolute_time_in_ns() {
+ uint64_t time_now_ns;
+
+#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
+ struct timespec tn;
+ // can't use CLOCK_MONOTONIC_RAW as it's not implemented in OpenWrt
+ clock_gettime(CLOCK_MONOTONIC, &tn);
+ uint64_t tnnsec = tn.tv_sec;
+ tnnsec = tnnsec * 1000000000;
+ uint64_t tnjnsec = tn.tv_nsec;
+ time_now_ns = tnnsec + tnjnsec;
+#endif
+
+#ifdef COMPILE_FOR_OSX
+ uint64_t time_now_mach;
+ uint64_t elapsedNano;
+ static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
+
+ time_now_mach = mach_absolute_time();
+
+ // If this is the first time we've run, get the timebase.
+ // We can use denom == 0 to indicate that sTimebaseInfo is
+ // uninitialised because it makes no sense to have a zero
+ // denominator in a fraction.
+
+ if (sTimebaseInfo.denom == 0) {
+ debug(1, "Mac initialise timebase info.");
+ (void)mach_timebase_info(&sTimebaseInfo);
}
- if (rc > 0)
- return count - bytes_remaining; // this is just to mimic a normal write/3.
- else
- return rc;
- // return write(fd,buf,count);
+
+ // Do the maths. We hope that the multiplication doesn't
+ // overflow; the price you pay for working in fixed point.
+
+ // this gives us nanoseconds
+ time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
+#endif
+
+ return time_now_ns;
}
-ssize_t non_blocking_write(int fd, const void *buf, size_t count) {
- return non_blocking_write_with_timeout(fd, buf, count, 5000); // default is 5 seconds.
+int try_to_open_pipe_for_writing(const char *pathname) {
+ // tries to open the pipe in non-blocking mode first.
+ // if it succeeds, it sets it to blocking.
+ // if not, it returns -1.
+
+ int fdis = open(pathname, O_WRONLY | O_NONBLOCK); // open it in non blocking mode first
+
+ // we check that it's not a "real" error. From the "man 2 open" page:
+ // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
+ // open for reading." Which is okay.
+ // This is checked by the caller.
+
+ if (fdis >= 0) {
+ // now we switch to blocking mode
+ int flags = fcntl(fdis, F_GETFL);
+ if (flags == -1) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "try_to_open_pipe -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno,
+ (char *)errorstring, pathname);
+ } else {
+ flags = fcntl(fdis, F_SETFL, flags & ~O_NONBLOCK);
+ if (flags == -1) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "try_to_open_pipe -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno,
+ (char *)errorstring, pathname);
+ }
+ }
+ }
+ return fdis;
}
/* from
@@ -1089,7 +1213,9 @@ char *str_replace(const char *string, const char *substr, const char *replacemen
/* from http://burtleburtle.net/bob/rand/smallprng.html */
-// this is not thread-safe, so we need a mutex on it to use it properly// always lock use this when accessing the fp_time_at_last_debug_message
+// this is not thread-safe, so we need a mutex on it to use it properly.
+// always lock use this when accessing the fp_time_at_last_debug_message
+
pthread_mutex_t r64_mutex = PTHREAD_MUTEX_INITIALIZER;
// typedef uint64_t u8;
@@ -1197,18 +1323,17 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
timeoutTime.tv_sec = time_then;
timeoutTime.tv_nsec = time_then_nsec;
- int64_t start_time = get_absolute_time_in_fp();
+ uint64_t start_time = get_absolute_time_in_ns();
int r = pthread_mutex_timedlock(mutex, &timeoutTime);
- int64_t et = get_absolute_time_in_fp() - start_time;
+ uint64_t et = get_absolute_time_in_ns() - start_time;
if ((debuglevel != 0) && (r != 0) && (debugmessage != NULL)) {
- et = (et * 1000000) >> 32; // microseconds
char errstr[1000];
if (r == ETIMEDOUT)
debug(debuglevel,
- "timed out waiting for a mutex, having waiting %f seconds, with a maximum "
+ "timed out waiting for a mutex, having waited %f microseconds, with a maximum "
"waiting time of %d microseconds. \"%s\".",
- (1.0 * et) / 1000000, dally_time, debugmessage);
+ (1.0E6 * et) / 1000000000, dally_time, debugmessage);
else
debug(debuglevel, "error %d: \"%s\" waiting for a mutex: \"%s\".", r,
strerror_r(r, errstr, sizeof(errstr)), debugmessage);
@@ -1257,7 +1382,7 @@ int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char
return pthread_mutex_lock(mutex);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
- uint64_t time_at_start = get_absolute_time_in_fp();
+ uint64_t time_at_start = get_absolute_time_in_ns();
char dstring[1000];
memset(dstring, 0, sizeof(dstring));
snprintf(dstring, sizeof(dstring), "%s:%d", filename, line);
@@ -1266,12 +1391,10 @@ int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char
int result = sps_pthread_mutex_timedlock(mutex, dally_time, dstring, debuglevel);
if (result == ETIMEDOUT) {
result = pthread_mutex_lock(mutex);
- uint64_t time_delay = get_absolute_time_in_fp() - time_at_start;
- uint64_t divisor = (uint64_t)1 << 32;
- double delay = 1.0 * time_delay / divisor;
+ uint64_t time_delay = get_absolute_time_in_ns() - time_at_start;
debug(debuglevel,
- "mutex_lock \"%s\" at \"%s\" expected max wait: %0.9f, actual wait: %0.9f sec.",
- mutexname, dstring, (1.0 * dally_time) / 1000000, delay);
+ "mutex_lock \"%s\" at \"%s\" expected max wait: %0.9f, actual wait: %0.9f microseconds.",
+ mutexname, dstring, (1.0 * dally_time), 0.001 * time_delay);
}
pthread_setcancelstate(oldState, NULL);
return result;
@@ -1309,6 +1432,9 @@ char *get_version_string() {
if (version_string) {
strcpy(version_string, PACKAGE_VERSION);
+#ifdef CONFIG_APPLE_ALAC
+ strcat(version_string, "-alac");
+#endif
#ifdef CONFIG_LIBDAEMON
strcat(version_string, "-libdaemon");
#endif
@@ -1339,6 +1465,9 @@ char *get_version_string() {
#ifdef CONFIG_SNDIO
strcat(version_string, "-sndio");
#endif
+#ifdef CONFIG_JACK
+ strcat(version_string, "-jack");
+#endif
#ifdef CONFIG_AO
strcat(version_string, "-ao");
#endif
@@ -1381,7 +1510,7 @@ char *get_version_string() {
return version_string;
}
-int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_format_t format,
+int64_t generate_zero_frames(char *outp, size_t number_of_frames, sps_format_t format,
int with_dither, int64_t random_number_in) {
// return the last random number used
// assuming the buffer has been assigned
@@ -1539,9 +1668,11 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
return previous_random_number;
}
-// This will check the incoming string "s" of length "len" with the existing NUL-terminated string "str" and update "flag" accordingly.
+// This will check the incoming string "s" of length "len" with the existing NUL-terminated string
+// "str" and update "flag" accordingly.
-// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings are stored.
+// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings
+// are stored.
// If the strings are different, the str is free'd and replaced by a pointer
// to a newly strdup'd string and the flag is set
@@ -1550,9 +1681,13 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
int string_update_with_size(char **str, int *flag, char *s, size_t len) {
if (*str) {
if ((s) && (len)) {
- if (strncmp(*str, s, len) != 0) {
+ if ((len != strlen(*str)) || (strncmp(*str, s, len) != 0)) {
free(*str);
- *str = strndup(s, len);
+ //*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
+ char *p = malloc(len + 1);
+ memcpy(p, s, len);
+ p[len] = '\0';
+ *str = p;
*flag = 1;
} else {
*flag = 0;
@@ -1565,7 +1700,11 @@ int string_update_with_size(char **str, int *flag, char *s, size_t len) {
}
} else { // old string is NULL
if ((s) && (len)) {
- *str = strndup(s, len);
+ //*str = strndup(s, len); // it seems that OpenWrt 12 doesn't have this
+ char *p = malloc(len + 1);
+ memcpy(p, s, len);
+ p[len] = '\0';
+ *str = p;
*flag = 1;
} else {
// old string is NULL and new string is NULL or length 0
@@ -1574,3 +1713,13 @@ int string_update_with_size(char **str, int *flag, char *s, size_t len) {
}
return *flag;
}
+
+// from https://stackoverflow.com/questions/13663617/memdup-function-in-c, with thanks
+void *memdup(const void *mem, size_t size) {
+ void *out = malloc(size);
+
+ if (out != NULL)
+ memcpy(out, mem, size);
+
+ return out;
+}