#include <time.h>
#include <unistd.h>
#include <fcntl.h>
+#include <libgen.h>
#ifdef COMPILE_FOR_OSX
#include <CoreServices/CoreServices.h>
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;
char *pidfile;
#endif
+ int log_fd; // file descriptor of the file or pipe to log stuff to.
+ char *log_file_path; // path to file or pipe to log to, if any
int logOutputLevel; // log output level
int debugger_show_elapsed_time; // in the debug message, display the time since startup
int debugger_show_relative_time; // in the debug message, display the time since the last one
void log_to_stderr(); // call this to direct logging to stderr;
void log_to_stdout(); // call this to direct logging to stdout;
void log_to_syslog(); // call this to direct logging to the system log;
+void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
+
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
diagnostics =
{
// disable_resend_requests = "no"; // set this to yes to stop Shairport Sync from requesting the retransmission of missing packets. Default is "no".
-// log_output_to = "syslog"; // set this to "syslog" (default), "STDERR" or "STDOUT" to specify were all logs, statistics and diagnostic messages are written to.
+// log_output_to = "syslog"; // set this to "syslog" (default), "stderr" or "stdout" or a file or pipe path to specify were all logs, statistics and diagnostic messages are written to. If there's anything wrong with the file spec, output will be to "stderr".
// statistics = "no"; // set to "yes" to print statistics in the log
// log_verbosity = 0; // "0" means no debug verbosity, "3" is most verbose.
// log_show_file_and_line = "yes"; // set this to yes if you want the file and line number of the message source in the log file
} else if (strcasecmp(str, "stderr") == 0) {
log_to_stderr();
} else {
- log_to_syslog();
- die("Invalid diagnostics log_output_to setting \"%s\". It should be \"syslog\", \"STDERR\" or \"STDOUT\". It is set to syslog.");
+ config.log_file_path = (char *)str;
+ config.log_fd = -1;
+ log_to_file();
}
}
/* Get the ignore_volume_control setting. */
#ifdef CONFIG_LIBDAEMON
pid = getpid();
#endif
+ config.log_fd = -1;
conns = NULL; // no connections active
memset((void *)&main_thread_id, 0, sizeof(main_thread_id));
memset(&config, 0, sizeof(config)); // also clears all strings, BTW