"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "minidlna.c" between
minidlna-1.2.1.tar.gz and minidlna-1.3.0.tar.gz

About: ReadyMedia (formerly known as MiniDLNA) is a simple media server software, with the aim of being fully compliant with DLNA/UPnP-AV clients.

minidlna.c  (minidlna-1.2.1):minidlna.c  (minidlna-1.3.0)
skipping to change at line 71 skipping to change at line 71
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
#include <pthread.h> #include <pthread.h>
#include <limits.h> #include <limits.h>
#include <libgen.h> #include <libgen.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h>
#include "config.h" #include "config.h"
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
#include <locale.h> #include <locale.h>
#include <libintl.h> #include <libintl.h>
#endif #endif
#include "event.h"
#include "upnpglobalvars.h" #include "upnpglobalvars.h"
#include "sql.h" #include "sql.h"
#include "upnphttp.h" #include "upnphttp.h"
#include "upnpdescgen.h" #include "upnpdescgen.h"
#include "minidlnapath.h" #include "minidlnapath.h"
#include "getifaddr.h" #include "getifaddr.h"
#include "upnpsoap.h" #include "upnpsoap.h"
#include "options.h" #include "options.h"
#include "utils.h" #include "utils.h"
#include "minissdp.h" #include "minissdp.h"
#include "minidlnatypes.h" #include "minidlnatypes.h"
#include "process.h" #include "process.h"
#include "upnpevents.h" #include "upnpevents.h"
#include "scanner.h" #include "scanner.h"
#include "monitor.h" #include "monitor.h"
#include "libav.h"
#include "log.h" #include "log.h"
#include "tivo_beacon.h" #include "tivo_beacon.h"
#include "tivo_utils.h" #include "tivo_utils.h"
#include "avahi.h" #include "avahi.h"
#if SQLITE_VERSION_NUMBER < 3005001 #if SQLITE_VERSION_NUMBER < 3005001
# warning "Your SQLite3 library appears to be too old! Please use 3.5.1 or newe r." # warning "Your SQLite3 library appears to be too old! Please use 3.5.1 or newe r."
# define sqlite3_threadsafe() 0 # define sqlite3_threadsafe() 0
#endif #endif
static LIST_HEAD(httplisthead, upnphttp) upnphttphead;
/* OpenAndConfHTTPSocket() : /* OpenAndConfHTTPSocket() :
* setup the socket used to handle incoming HTTP connections. */ * setup the socket used to handle incoming HTTP connections. */
static int static int
OpenAndConfHTTPSocket(unsigned short port) OpenAndConfHTTPSocket(unsigned short port)
{ {
int s; int s;
int i = 1; int i = 1;
struct sockaddr_in listenname; struct sockaddr_in listenname;
/* Initialize client type cache */ /* Initialize client type cache */
skipping to change at line 148 skipping to change at line 153
if (listen(s, 16) < 0) if (listen(s, 16) < 0)
{ {
DPRINTF(E_ERROR, L_GENERAL, "listen(http): %s\n", strerror(errno) ); DPRINTF(E_ERROR, L_GENERAL, "listen(http): %s\n", strerror(errno) );
close(s); close(s);
return -1; return -1;
} }
return s; return s;
} }
/* ProcessListen() :
* accept incoming HTTP connection. */
static void
ProcessListen(struct event *ev)
{
int shttp;
socklen_t clientnamelen;
struct sockaddr_in clientname;
clientnamelen = sizeof(struct sockaddr_in);
shttp = accept(ev->fd, (struct sockaddr *)&clientname, &clientnamelen);
if (shttp<0)
{
DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno)
);
}
else
{
struct upnphttp * tmp = 0;
DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n",
inet_ntoa(clientname.sin_addr),
ntohs(clientname.sin_port) );
/*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n"
);
}*/
/* Create a new upnphttp object and add it to
* the active upnphttp object list */
tmp = New_upnphttp(shttp);
if (tmp)
{
tmp->clientaddr = clientname.sin_addr;
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
}
else
{
DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n");
close(shttp);
}
}
}
/* Handler for the SIGTERM signal (kill) /* Handler for the SIGTERM signal (kill)
* SIGINT is also handled */ * SIGINT is also handled */
static void static void
sigterm(int sig) sigterm(int sig)
{ {
signal(sig, SIG_IGN); /* Ignore this signal while we are quitting */ signal(sig, SIG_IGN); /* Ignore this signal while we are quitting */
DPRINTF(E_WARN, L_GENERAL, "received signal %d, good-bye\n", sig); DPRINTF(E_WARN, L_GENERAL, "received signal %d, good-bye\n", sig);
quitting = 1; quitting = 1;
skipping to change at line 173 skipping to change at line 218
signal(sig, sigusr1); signal(sig, sigusr1);
DPRINTF(E_WARN, L_GENERAL, "received signal %d, clear cache\n", sig); DPRINTF(E_WARN, L_GENERAL, "received signal %d, clear cache\n", sig);
memset(&clients, '\0', sizeof(clients)); memset(&clients, '\0', sizeof(clients));
} }
static void static void
sighup(int sig) sighup(int sig)
{ {
signal(sig, sighup); signal(sig, sighup);
DPRINTF(E_WARN, L_GENERAL, "received signal %d, re-read\n", sig); DPRINTF(E_WARN, L_GENERAL, "received signal %d, reloading\n", sig);
reload_ifaces(1); reload_ifaces(1);
log_reopen();
} }
/* record the startup time */ /* record the startup time */
static void static void
set_startup_time(void) set_startup_time(void)
{ {
startup_time = time(NULL); startup_time = time(NULL);
} }
static void static void
getfriendlyname(char *buf, int len) getfriendlyname(char *buf, int len)
{ {
char *p = NULL; char *p = NULL;
char hn[256]; char hn[63];
int off; int off;
if (gethostname(hn, sizeof(hn)) == 0) if (gethostname(hn, sizeof(hn)) == 0)
{ {
strncpyt(buf, hn, len); strncpyt(buf, hn, len);
p = strchr(buf, '.'); p = strchr(buf, '.');
if (p) if (p)
*p = '\0'; *p = '\0';
} }
else else
skipping to change at line 244 skipping to change at line 290
strcpy(serialnumber, mac_str); strcpy(serialnumber, mac_str);
else else
strcpy(serialnumber, "0"); strcpy(serialnumber, "0");
} }
break; break;
} }
} }
fclose(info); fclose(info);
#else #else
char * logname; char * logname;
logname = getenv("LOGNAME"); logname = getenv("USER");
#ifndef STATIC // Disable for static linking
if (!logname) if (!logname)
{ {
struct passwd * pwent; logname = getenv("LOGNAME");
pwent = getpwuid(getuid()); #ifndef STATIC // Disable for static linking
if (pwent) if (!logname)
logname = pwent->pw_name; {
} struct passwd *pwent = getpwuid(geteuid());
if (pwent)
logname = pwent->pw_name;
}
#endif #endif
}
snprintf(buf+off, len-off, "%s", logname?logname:"Unknown"); snprintf(buf+off, len-off, "%s", logname?logname:"Unknown");
#endif #endif
} }
static time_t static time_t
_get_dbtime(void) _get_dbtime(void)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
struct stat st; struct stat st;
skipping to change at line 367 skipping to change at line 416
if (system(cmd) != 0) if (system(cmd) != 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cac he! Exiting...\n"); DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cac he! Exiting...\n");
open_db(&db); open_db(&db);
if (CreateDatabase() != 0) if (CreateDatabase() != 0)
DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqli te database! Exiting...\n"); DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqli te database! Exiting...\n");
} }
if (ret || GETFLAG(RESCAN_MASK)) if (ret || GETFLAG(RESCAN_MASK))
{ {
#if USE_FORK #if USE_FORK
SETFLAG(SCANNING_MASK);
sqlite3_close(db); sqlite3_close(db);
*scanner_pid = fork(); *scanner_pid = fork();
open_db(&db); open_db(&db);
if (*scanner_pid == 0) /* child (scanner) process */ if (*scanner_pid == 0) /* child (scanner) process */
{ {
start_scanner(); start_scanner();
sqlite3_close(db); sqlite3_close(db);
log_close(); log_close();
freeoptions(); freeoptions();
free(children); free(children);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
else if (*scanner_pid < 0) else if (*scanner_pid < 0)
{ {
start_scanner(); start_scanner();
} }
else
SETFLAG(SCANNING_MASK);
#else #else
start_scanner(); start_scanner();
#endif #endif
} }
} }
static int static int
writepidfile(const char *fname, int pid, uid_t uid) writepidfile(const char *fname, int pid, uid_t uid)
{ {
FILE *pidfile; FILE *pidfile;
skipping to change at line 513 skipping to change at line 563
char mac_str[13]; char mac_str[13];
char *string, *word; char *string, *word;
char *path; char *path;
char buf[PATH_MAX]; char buf[PATH_MAX];
char log_str[75] = "general,artwork,database,inotify,scanner,metadata,htt p,ssdp,tivo=warn"; char log_str[75] = "general,artwork,database,inotify,scanner,metadata,htt p,ssdp,tivo=warn";
char *log_level = NULL; char *log_level = NULL;
struct media_dir_s *media_dir; struct media_dir_s *media_dir;
int ifaces = 0; int ifaces = 0;
media_types types; media_types types;
uid_t uid = 0; uid_t uid = 0;
gid_t gid = 0;
int error;
/* first check if "-f" option is used */ /* first check if "-f" option is used */
for (i=2; i<argc; i++) for (i=2; i<argc; i++)
{ {
if (strcmp(argv[i-1], "-f") == 0) if (strcmp(argv[i-1], "-f") == 0)
{ {
optionsfile = argv[i]; optionsfile = argv[i];
options_flag = 1; options_flag = 1;
break; break;
} }
} }
/* set up uuid based on mac address */ /* set up uuid based on mac address */
if (getsyshwaddr(mac_str, sizeof(mac_str)) < 0) if (getsyshwaddr(mac_str, sizeof(mac_str)) < 0)
{ {
DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n"); DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n");
strcpy(mac_str, "554e4b4e4f57"); strcpy(mac_str, "554e4b4e4f57");
} }
strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-"); snprintf(uuidvalue+5, UUIDVALUE_MAX_LEN-5, "4d696e69-444c-164e-9d41-%s",
strncat(uuidvalue, mac_str, 12); mac_str);
getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN); getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
runtime_vars.port = 8200; runtime_vars.port = 8200;
runtime_vars.notify_interval = 895; /* seconds between SSDP announces */ runtime_vars.notify_interval = 895; /* seconds between SSDP announces */
runtime_vars.max_connections = 50; runtime_vars.max_connections = 50;
runtime_vars.root_container = NULL; runtime_vars.root_container = NULL;
runtime_vars.ifaces[0] = NULL; runtime_vars.ifaces[0] = NULL;
/* read options file first since /* read options file first since
skipping to change at line 665 skipping to change at line 716
album_art_names = this_name; album_art_names = this_name;
} }
break; break;
case UPNPDBDIR: case UPNPDBDIR:
path = realpath(ary_options[i].value, buf); path = realpath(ary_options[i].value, buf);
if (!path) if (!path)
path = (ary_options[i].value); path = (ary_options[i].value);
make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
if (access(path, F_OK) != 0) if (access(path, F_OK) != 0)
DPRINTF(E_FATAL, L_GENERAL, "Database path not ac cessible! [%s]\n", path); DPRINTF(E_FATAL, L_GENERAL, "Database path not ac cessible! [%s]\n", path);
strncpyt(db_path, path, PATH_MAX); strncpyt(db_path, path, sizeof(db_path));
break; break;
case UPNPLOGDIR: case UPNPLOGDIR:
path = realpath(ary_options[i].value, buf); path = realpath(ary_options[i].value, buf);
if (!path) if (!path)
path = (ary_options[i].value); path = ary_options[i].value;
make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (snprintf(log_path, sizeof(log_path), "%s", path) > si
if (access(path, F_OK) != 0) zeof(log_path))
DPRINTF(E_FATAL, L_GENERAL, "Log path not accessi DPRINTF(E_FATAL, L_GENERAL, "Log path too long! [
ble! [%s]\n", path); %s]\n", path);
strncpyt(log_path, path, PATH_MAX); make_dir(log_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
break; break;
case UPNPLOGLEVEL: case UPNPLOGLEVEL:
log_level = ary_options[i].value; log_level = ary_options[i].value;
break; break;
case UPNPINOTIFY: case UPNPINOTIFY:
if (!strtobool(ary_options[i].value)) if (!strtobool(ary_options[i].value))
CLEARFLAG(INOTIFY_MASK); CLEARFLAG(INOTIFY_MASK);
break; break;
case ENABLE_TIVO: case ENABLE_TIVO:
if (strtobool(ary_options[i].value)) if (strtobool(ary_options[i].value))
skipping to change at line 735 skipping to change at line 785
case USER_ACCOUNT: case USER_ACCOUNT:
uid = strtoul(ary_options[i].value, &string, 0); uid = strtoul(ary_options[i].value, &string, 0);
if (*string) if (*string)
{ {
/* Symbolic username given, not UID. */ /* Symbolic username given, not UID. */
struct passwd *entry = getpwnam(ary_options[i].va lue); struct passwd *entry = getpwnam(ary_options[i].va lue);
if (!entry) if (!entry)
DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s '.\n", DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s '.\n",
ary_options[i].value); ary_options[i].value);
uid = entry->pw_uid; uid = entry->pw_uid;
if (!gid)
gid = entry->pw_gid;
} }
break; break;
case FORCE_SORT_CRITERIA: case FORCE_SORT_CRITERIA:
force_sort_criteria = ary_options[i].value; force_sort_criteria = ary_options[i].value;
if (force_sort_criteria[0] == '!')
{
SETFLAG(FORCE_ALPHASORT_MASK);
force_sort_criteria++;
}
break; break;
case MAX_CONNECTIONS: case MAX_CONNECTIONS:
runtime_vars.max_connections = atoi(ary_options[i].value) ; runtime_vars.max_connections = atoi(ary_options[i].value) ;
break; break;
case MERGE_MEDIA_DIRS: case MERGE_MEDIA_DIRS:
if (strtobool(ary_options[i].value)) if (strtobool(ary_options[i].value))
SETFLAG(MERGE_MEDIA_DIRS_MASK); SETFLAG(MERGE_MEDIA_DIRS_MASK);
break; break;
case WIDE_LINKS: case WIDE_LINKS:
if (strtobool(ary_options[i].value)) if (strtobool(ary_options[i].value))
SETFLAG(WIDE_LINKS_MASK); SETFLAG(WIDE_LINKS_MASK);
break; break;
case TIVO_DISCOVERY: case TIVO_DISCOVERY:
if (strcasecmp(ary_options[i].value, "beacon") == 0) if (strcasecmp(ary_options[i].value, "beacon") == 0)
CLEARFLAG(TIVO_BONJOUR_MASK); CLEARFLAG(TIVO_BONJOUR_MASK);
break; break;
case ENABLE_SUBTITLES:
if (!strtobool(ary_options[i].value))
CLEARFLAG(SUBTITLES_MASK);
break;
default: default:
DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n" , DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n" ,
optionsfile); optionsfile);
} }
} }
if (log_path[0] == '\0') if (!log_path[0])
{ strncpyt(log_path, DEFAULT_LOG_PATH, sizeof(log_path));
if (db_path[0] == '\0') if (!db_path[0])
strncpyt(log_path, DEFAULT_LOG_PATH, PATH_MAX); strncpyt(db_path, DEFAULT_DB_PATH, sizeof(db_path));
else
strncpyt(log_path, db_path, PATH_MAX);
}
if (db_path[0] == '\0')
strncpyt(db_path, DEFAULT_DB_PATH, PATH_MAX);
/* command line arguments processing */ /* command line arguments processing */
for (i=1; i<argc; i++) for (i=1; i<argc; i++)
{ {
if (argv[i][0] != '-') if (argv[i][0] != '-')
{ {
DPRINTF(E_FATAL, L_GENERAL, "Unknown option: %s\n", argv[ i]); DPRINTF(E_FATAL, L_GENERAL, "Unknown option: %s\n", argv[ i]);
} }
else if (strcmp(argv[i], "--help") == 0) else if (strcmp(argv[i], "--help") == 0)
{ {
skipping to change at line 860 skipping to change at line 916
break; break;
case 'h': case 'h':
runtime_vars.port = -1; // triggers help display runtime_vars.port = -1; // triggers help display
break; break;
case 'r': case 'r':
SETFLAG(RESCAN_MASK); SETFLAG(RESCAN_MASK);
break; break;
case 'R': case 'R':
snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cac he", db_path, db_path); snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cac he", db_path, db_path);
if (system(buf) != 0) if (system(buf) != 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cache. EXITING\n"); DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cache %s. EXITING\n", db_path);
break; break;
case 'u': case 'u':
if (i+1 != argc) if (i+1 != argc)
{ {
i++; i++;
uid = strtoul(argv[i], &string, 0); uid = strtoul(argv[i], &string, 0);
if (*string) if (*string)
{ {
/* Symbolic username given, not UID. */ /* Symbolic username given, not UID. */
struct passwd *entry = getpwnam(argv[i]); struct passwd *entry = getpwnam(argv[i]);
if (!entry) if (!entry)
DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]);
uid = entry->pw_uid; uid = entry->pw_uid;
if (!gid)
gid = entry->pw_gid;
} }
} }
else else
DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]);
break; break;
case 'g':
if (i+1 != argc)
{
i++;
gid = strtoul(argv[i], &string, 0);
if (*string)
{
/* Symbolic group given, not GID. */
struct group *grp = getgrnam(argv[i]);
if (!grp)
DPRINTF(E_FATAL, L_GENERAL, "Bad
group '%s'.\n", argv[i]);
gid = grp->gr_gid;
}
}
else
DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one
argument.\n", argv[i][1]);
break; break;
#ifdef __linux__ #if defined(__linux__) || defined(__APPLE__)
case 'S': case 'S':
SETFLAG(SYSTEMD_MASK); SETFLAG(SYSTEMD_MASK);
break; break;
#endif #endif
case 'V': case 'V':
printf("Version " MINIDLNA_VERSION "\n"); printf("Version " MINIDLNA_VERSION "\n");
exit(0); exit(0);
break; break;
default: default:
DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[ i]); DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[ i]);
runtime_vars.port = -1; // triggers help display runtime_vars.port = -1; // triggers help display
} }
} }
if (runtime_vars.port <= 0) if (runtime_vars.port <= 0)
{ {
printf("Usage:\n\t" printf("Usage:\n\t"
"%s [-d] [-v] [-f config_file] [-p port]\n" "%s [-d] [-v] [-f config_file] [-p port]\n"
"\t\t[-i network_interface] [-u uid_to_run_as]\n" "\t\t[-i network_interface] [-u uid_to_run_as] [-g group_ to_run_as]\n"
"\t\t[-t notify_interval] [-P pid_filename]\n" "\t\t[-t notify_interval] [-P pid_filename]\n"
"\t\t[-s serial] [-m model_number]\n" "\t\t[-s serial] [-m model_number]\n"
#ifdef __linux__ #ifdef __linux__
"\t\t[-w url] [-r] [-R] [-L] [-S] [-V] [-h]\n" "\t\t[-w url] [-r] [-R] [-L] [-S] [-V] [-h]\n"
#else #else
"\t\t[-w url] [-r] [-R] [-L] [-V] [-h]\n" "\t\t[-w url] [-r] [-R] [-L] [-V] [-h]\n"
#endif #endif
"\nNotes:\n\tNotify interval is in seconds. Default is 89 5 seconds.\n" "\nNotes:\n\tNotify interval is in seconds. Default is 89 5 seconds.\n"
"\tDefault pid file is %s.\n" "\tDefault pid file is %s.\n"
"\tWith -d minidlna will run in debug mode (not daemonize ).\n" "\tWith -d minidlna will run in debug mode (not daemonize ).\n"
"\t-w sets the presentation url. Default is http address on port 80\n" "\t-w sets the presentation url. Default is http address on port 80\n"
"\t-v enables verbose output\n" "\t-v enables verbose output\n"
"\t-h displays this text\n" "\t-h displays this text\n"
"\t-r forces a rescan\n" "\t-r forces a rescan\n"
"\t-R forces a rebuild\n" "\t-R forces a rebuild\n"
"\t-L do not create playlists\n" "\t-L do not create playlists\n"
#ifdef __linux__ #if defined(__linux__) || defined(__APPLE__)
"\t-S changes behaviour for systemd\n" "\t-S changes behaviour for systemd/launchd\n"
#endif #endif
"\t-V print the version number\n", "\t-V print the version number\n",
argv[0], pidfilename); argv[0], pidfilename);
return 1; return 1;
} }
if (verbose_flag) if (verbose_flag)
{ {
strcpy(log_str+65, "debug"); strcpy(log_str+65, "debug");
log_level = log_str; log_level = log_str;
} }
else if (!log_level) else if (!log_level)
log_level = log_str; log_level = log_str;
/* Set the default log file path to NULL (stdout) */ /* Set the default log to stdout */
path = NULL;
if (debug_flag) if (debug_flag)
{ {
pid = getpid(); pid = getpid();
strcpy(log_str+65, "maxdebug"); strcpy(log_str+65, "maxdebug");
log_level = log_str; log_level = log_str;
log_path[0] = '\0';
} }
else if (GETFLAG(SYSTEMD_MASK)) else if (GETFLAG(SYSTEMD_MASK))
{ {
pid = getpid(); pid = getpid();
log_path[0] = '\0';
} }
else else
{ {
pid = process_daemonize(); pid = process_daemonize();
#ifdef READYNAS
unlink("/ramfs/.upnp-av_scan");
path = "/var/log/upnp-av.log";
#else
if (access(db_path, F_OK) != 0) if (access(db_path, F_OK) != 0)
make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
snprintf(buf, sizeof(buf), "%s/minidlna.log", log_path);
path = buf;
#endif
} }
log_init(path, log_level); if (log_init(log_level) < 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to open log file '%s/" LOGFIL
E_NAME "': %s\n",
log_path, strerror(errno));
if (process_check_if_running(pidfilename) < 0) if (process_check_if_running(pidfilename) < 0)
{ DPRINTF(E_FATAL, L_GENERAL, SERVER_NAME " is already running. EXI
DPRINTF(E_ERROR, L_GENERAL, SERVER_NAME " is already running. EXI TING.\n");
TING.\n");
return 1;
}
set_startup_time(); set_startup_time();
/* presentation url */ /* presentation url */
if (presurl) if (presurl)
strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN);
else else
strcpy(presentationurl, "/"); strcpy(presentationurl, "/");
/* set signal handlers */ /* set signal handlers */
memset(&sa, 0, sizeof(struct sigaction)); memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sigterm; sa.sa_handler = sigterm;
if (sigaction(SIGTERM, &sa, NULL)) if (sigaction(SIGTERM, &sa, NULL))
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGTERM"); DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGTERM");
if (sigaction(SIGINT, &sa, NULL)) if (sigaction(SIGINT, &sa, NULL))
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGINT"); DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGINT");
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGPIPE"); DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGPIPE");
if (signal(SIGHUP, &sighup) == SIG_ERR) if (signal(SIGHUP, &sighup) == SIG_ERR)
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGHUP"); DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGHUP");
if (signal(SIGUSR2, SIG_IGN) == SIG_ERR)
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n
", "SIGUSR2");
signal(SIGUSR1, &sigusr1); signal(SIGUSR1, &sigusr1);
sa.sa_handler = process_handle_child_termination; sa.sa_handler = process_handle_child_termination;
if (sigaction(SIGCHLD, &sa, NULL)) if (sigaction(SIGCHLD, &sa, NULL))
DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGCHLD"); DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n ", "SIGCHLD");
if (writepidfile(pidfilename, pid, uid) != 0) if (writepidfile(pidfilename, pid, uid) != 0)
pidfilename = NULL; pidfilename = NULL;
if (uid > 0) if (uid > 0)
{ {
struct stat st; struct stat st;
if (stat(db_path, &st) == 0 && st.st_uid != uid && chown(db_path, uid, -1) != 0) if (stat(db_path, &st) == 0 && st.st_uid != uid && chown(db_path, uid, -1) != 0)
DPRINTF(E_ERROR, L_GENERAL, "Unable to set db_path [%s] o wnership to %d: %s\n", DPRINTF(E_ERROR, L_GENERAL, "Unable to set db_path [%s] o wnership to %d: %s\n",
db_path, uid, strerror(errno)); db_path, uid, strerror(errno));
} }
if (gid > 0 && setgid(gid) == -1)
DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to gid '%d'. [%s] E
XITING.\n",
gid, strerror(errno));
if (uid > 0 && setuid(uid) == -1) if (uid > 0 && setuid(uid) == -1)
DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to uid '%d'. [%s] E XITING.\n", DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to uid '%d'. [%s] E XITING.\n",
uid, strerror(errno)); uid, strerror(errno));
children = calloc(runtime_vars.max_connections, sizeof(struct child)); children = calloc(runtime_vars.max_connections, sizeof(struct child));
if (!children) if (!children)
{ {
DPRINTF(E_ERROR, L_GENERAL, "Allocation failed\n"); DPRINTF(E_ERROR, L_GENERAL, "Allocation failed\n");
return 1; return 1;
} }
if ((error = event_module.init()) != 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to init event module. "
"[%s] EXITING.\n", strerror(error));
return 0; return 0;
} }
/* === main === */ /* === main === */
/* process HTTP or SSDP requests */ /* process HTTP or SSDP requests */
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int ret, i; int ret, i;
int shttpl = -1; int shttpl = -1;
int smonitor = -1; int smonitor = -1;
LIST_HEAD(httplisthead, upnphttp) upnphttphead;
struct upnphttp * e = 0; struct upnphttp * e = 0;
struct upnphttp * next; struct upnphttp * next;
fd_set readset; /* for select() */ struct timeval tv, timeofday, lastnotifytime = {0, 0};
fd_set writeset;
struct timeval timeout, timeofday, lastnotifytime = {0, 0};
time_t lastupdatetime = 0, lastdbtime = 0; time_t lastupdatetime = 0, lastdbtime = 0;
int max_fd = -1; u_long timeout; /* in milliseconds */
int last_changecnt = 0; int last_changecnt = 0;
pid_t scanner_pid = 0; pid_t scanner_pid = 0;
pthread_t inotify_thread = 0; pthread_t inotify_thread = 0;
struct event ssdpev, httpev, monev;
#ifdef TIVO_SUPPORT #ifdef TIVO_SUPPORT
uint8_t beacon_interval = 5; uint8_t beacon_interval = 5;
int sbeacon = -1; int sbeacon = -1;
struct sockaddr_in tivo_bcast; struct sockaddr_in tivo_bcast;
struct timeval lastbeacontime = {0, 0}; struct timeval lastbeacontime = {0, 0};
struct event beaconev;
#endif #endif
for (i = 0; i < L_MAX; i++) for (i = 0; i < L_MAX; i++)
log_level[i] = E_WARN; log_level[i] = E_WARN;
ret = init(argc, argv); ret = init(argc, argv);
if (ret != 0) if (ret != 0)
return 1; return 1;
init_nls(); init_nls();
skipping to change at line 1074 skipping to change at line 1150
lastdbtime = _get_dbtime(); lastdbtime = _get_dbtime();
#ifdef HAVE_INOTIFY #ifdef HAVE_INOTIFY
if( GETFLAG(INOTIFY_MASK) ) if( GETFLAG(INOTIFY_MASK) )
{ {
if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 300500 1) if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 300500 1)
DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not thread safe! " DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not thread safe! "
"Inotify will be disabled.\n" ); "Inotify will be disabled.\n" );
else if (pthread_create(&inotify_thread, NULL, start_inotify, NUL L) != 0) else if (pthread_create(&inotify_thread, NULL, start_inotify, NUL L) != 0)
DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() fail ed for start_inotify. EXITING\n"); DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() fail ed for start_inotify. EXITING\n");
} }
#endif #endif /* HAVE_INOTIFY */
#ifdef HAVE_KQUEUE
if (!GETFLAG(SCANNING_MASK)) {
lav_register_all();
kqueue_monitor_start();
}
#endif /* HAVE_KQUEUE */
smonitor = OpenAndConfMonitorSocket(); smonitor = OpenAndConfMonitorSocket();
if (smonitor > 0)
{
monev = (struct event ){ .fd = smonitor, .rdwr = EVENT_READ, .pro
cess = ProcessMonitorEvent };
event_module.add(&monev);
}
sssdp = OpenAndConfSSDPReceiveSocket(); sssdp = OpenAndConfSSDPReceiveSocket();
if (sssdp < 0) if (sssdp < 0)
{ {
DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving S SDP. Trying to use MiniSSDPd\n"); DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving S SDP. Trying to use MiniSSDPd\n");
reload_ifaces(0); /* populate lan_addr[0].str */ reload_ifaces(0); /* populate lan_addr[0].str */
if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSD Pd. EXITING"); DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSD Pd. EXITING");
} }
else
{
ssdpev = (struct event ){ .fd = sssdp, .rdwr = EVENT_READ, .proce
ss = ProcessSSDPRequest };
event_module.add(&ssdpev);
}
/* open socket for HTTP connections. */ /* open socket for HTTP connections. */
shttpl = OpenAndConfHTTPSocket(runtime_vars.port); shttpl = OpenAndConfHTTPSocket(runtime_vars.port);
if (shttpl < 0) if (shttpl < 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXIT ING\n"); DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXIT ING\n");
DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.po rt); DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.po rt);
httpev = (struct event ){ .fd = shttpl, .rdwr = EVENT_READ, .process = Pr
ocessListen };
event_module.add(&httpev);
#ifdef TIVO_SUPPORT #ifdef TIVO_SUPPORT
if (GETFLAG(TIVO_MASK)) if (GETFLAG(TIVO_MASK))
{ {
DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n");
/* Add TiVo-specific randomize function to sqlite */ /* Add TiVo-specific randomize function to sqlite */
ret = sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, N ULL, &TiVoRandomSeedFunc, NULL, NULL); ret = sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, N ULL, &TiVoRandomSeedFunc, NULL, NULL);
if (ret != SQLITE_OK) if (ret != SQLITE_OK)
DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite ran domize function for TiVo!\n"); DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite ran domize function for TiVo!\n");
if (GETFLAG(TIVO_BONJOUR_MASK)) if (GETFLAG(TIVO_BONJOUR_MASK))
{ {
tivo_bonjour_register(); tivo_bonjour_register();
} }
else else
{ {
/* open socket for sending Tivo notifications */ /* open socket for sending Tivo notifications */
sbeacon = OpenAndConfTivoBeaconSocket(); sbeacon = OpenAndConfTivoBeaconSocket();
if(sbeacon < 0) if(sbeacon < 0)
DPRINTF(E_FATAL, L_GENERAL, "Failed to open socke ts for sending Tivo beacon notify " DPRINTF(E_FATAL, L_GENERAL, "Failed to open socke ts for sending Tivo beacon notify "
"messages. EXITING\n"); "messages. EXITING\n");
beaconev = (struct event ){ .fd = sbeacon, .rdwr = EVENT_
READ, .process = ProcessTiVoBeacon };
event_module.add(&beaconev);
tivo_bcast.sin_family = AF_INET; tivo_bcast.sin_family = AF_INET;
tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress()); tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress());
tivo_bcast.sin_port = htons(2190); tivo_bcast.sin_port = htons(2190);
} }
} }
#endif #endif
reload_ifaces(0); reload_ifaces(0);
lastnotifytime.tv_sec = time(NULL) + runtime_vars.notify_interval; lastnotifytime.tv_sec = time(NULL) + runtime_vars.notify_interval;
/* main loop */ /* main loop */
while (!quitting) while (!quitting)
{ {
if (gettimeofday(&timeofday, 0) < 0)
DPRINTF(E_FATAL, L_GENERAL, "gettimeofday(): %s\n", strer
ror(errno));
/* Check if we need to send SSDP NOTIFY messages and do it if /* Check if we need to send SSDP NOTIFY messages and do it if
* needed */ * needed */
if (gettimeofday(&timeofday, 0) < 0) tv = lastnotifytime;
{ tv.tv_sec += runtime_vars.notify_interval;
DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strer if (timevalcmp(&timeofday, &tv, >=))
ror(errno));
timeout.tv_sec = runtime_vars.notify_interval;
timeout.tv_usec = 0;
}
else
{ {
/* the comparison is not very precise but who cares ? */ DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
if (timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_ for (i = 0; i < n_lan_addr; i++)
vars.notify_interval))
{ {
DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n SendSSDPNotifies(lan_addr[i].snotify, lan_addr[i]
"); .str,
for (i = 0; i < n_lan_addr; i++) runtime_vars.port, runtime_vars.notify_in
{ terval);
SendSSDPNotifies(lan_addr[i].snotify, lan
_addr[i].str,
runtime_vars.port, runtime_vars.n
otify_interval);
}
memcpy(&lastnotifytime, &timeofday, sizeof(struct
timeval));
timeout.tv_sec = runtime_vars.notify_interval;
timeout.tv_usec = 0;
}
else
{
timeout.tv_sec = lastnotifytime.tv_sec + runtime_
vars.notify_interval
- timeofday.tv_sec;
if (timeofday.tv_usec > lastnotifytime.tv_usec)
{
timeout.tv_usec = 1000000 + lastnotifytim
e.tv_usec
- timeofday.tv_usec;
timeout.tv_sec--;
}
else
timeout.tv_usec = lastnotifytime.tv_usec
- timeofday.tv_usec;
}
#ifdef TIVO_SUPPORT
if (sbeacon >= 0)
{
if (timeofday.tv_sec >= (lastbeacontime.tv_sec +
beacon_interval))
{
sendBeaconMessage(sbeacon, &tivo_bcast, s
izeof(struct sockaddr_in), 1);
memcpy(&lastbeacontime, &timeofday, sizeo
f(struct timeval));
if (timeout.tv_sec > beacon_interval)
{
timeout.tv_sec = beacon_interval;
timeout.tv_usec = 0;
}
/* Beacons should be sent every 5 seconds
or so for the first minute,
* then every minute or so thereafter. */
if (beacon_interval == 5 && (timeofday.tv
_sec - startup_time) > 60)
beacon_interval = 60;
}
else if (timeout.tv_sec > (lastbeacontime.tv_sec
+ beacon_interval + 1 - timeofday.tv_sec))
timeout.tv_sec = lastbeacontime.tv_sec +
beacon_interval - timeofday.tv_sec;
}
#endif
}
if (GETFLAG(SCANNING_MASK))
{
if (!scanner_pid || kill(scanner_pid, 0) != 0)
{
CLEARFLAG(SCANNING_MASK);
if (_get_dbtime() != lastdbtime)
updateID++;
} }
lastnotifytime = timeofday;
timeout = runtime_vars.notify_interval * 1000;
} }
else
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sock
ets) */
FD_ZERO(&readset);
if (sssdp >= 0)
{
FD_SET(sssdp, &readset);
max_fd = MAX(max_fd, sssdp);
}
if (shttpl >= 0)
{ {
FD_SET(shttpl, &readset); timevalsub(&tv, &timeofday);
max_fd = MAX(max_fd, shttpl); timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
} }
#ifdef TIVO_SUPPORT #ifdef TIVO_SUPPORT
if (sbeacon >= 0) if (sbeacon >= 0)
{ {
FD_SET(sbeacon, &readset); u_long beacontimeout;
max_fd = MAX(max_fd, sbeacon);
}
#endif
if (smonitor >= 0)
{
FD_SET(smonitor, &readset);
max_fd = MAX(max_fd, smonitor);
}
i = 0; /* active HTTP connections count */ tv = lastbeacontime;
for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next tv.tv_sec += beacon_interval;
) if (timevalcmp(&timeofday, &tv, >=))
{ {
if ((e->socket >= 0) && (e->state <= 2)) sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(st
ruct sockaddr_in), 1);
lastbeacontime = timeofday;
beacontimeout = beacon_interval * 1000;
if (timeout > beacon_interval * 1000)
timeout = beacon_interval * 1000;
/* Beacons should be sent every 5 seconds or
* so for the first minute, then every minute
* or so thereafter. */
if (beacon_interval == 5 && (timeofday.tv_sec - s
tartup_time) > 60)
beacon_interval = 60;
}
else
{ {
FD_SET(e->socket, &readset); timevalsub(&tv, &timeofday);
max_fd = MAX(max_fd, e->socket); beacontimeout = tv.tv_sec * 1000 +
i++; tv.tv_usec / 1000;
} }
} if (timeout > beacontimeout)
FD_ZERO(&writeset); timeout = beacontimeout;
upnpevents_selectfds(&readset, &writeset, &max_fd);
ret = select(max_fd+1, &readset, &writeset, 0, &timeout);
if (ret < 0)
{
if(quitting) goto shutdown;
if(errno == EINTR) continue;
DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror
(errno));
DPRINTF(E_FATAL, L_GENERAL, "Failed to select open socket
s. EXITING\n");
}
upnpevents_processfds(&readset, &writeset);
/* process SSDP packets */
if (sssdp >= 0 && FD_ISSET(sssdp, &readset))
{
/*DPRINTF(E_DEBUG, L_GENERAL, "Received SSDP Packet\n");*
/
ProcessSSDPRequest(sssdp, (unsigned short)runtime_vars.po
rt);
}
#ifdef TIVO_SUPPORT
if (sbeacon >= 0 && FD_ISSET(sbeacon, &readset))
{
/*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
ProcessTiVoBeacon(sbeacon);
} }
#endif #endif
if (smonitor >= 0 && FD_ISSET(smonitor, &readset))
{ if (GETFLAG(SCANNING_MASK) && kill(scanner_pid, 0) != 0) {
ProcessMonitorEvent(smonitor); CLEARFLAG(SCANNING_MASK);
if (_get_dbtime() != lastdbtime)
updateID++;
#ifdef HAVE_KQUEUE
lav_register_all();
kqueue_monitor_start();
#endif /* HAVE_KQUEUE */
} }
event_module.process(timeout);
if (quitting)
goto shutdown;
upnpevents_gc();
/* increment SystemUpdateID if the content database has changed, /* increment SystemUpdateID if the content database has changed,
* and if there is an active HTTP connection, at most once every 2 seconds */ * and if there is an active HTTP connection, at most once every 2 seconds */
if (i && (timeofday.tv_sec >= (lastupdatetime + 2))) if (!LIST_EMPTY(&upnphttphead) &&
(timeofday.tv_sec >= (lastupdatetime + 2)))
{ {
if (GETFLAG(SCANNING_MASK)) if (GETFLAG(SCANNING_MASK))
{ {
time_t dbtime = _get_dbtime(); time_t dbtime = _get_dbtime();
if (dbtime != lastdbtime) if (dbtime != lastdbtime)
{ {
lastdbtime = dbtime; lastdbtime = dbtime;
last_changecnt = -1; last_changecnt = -1;
} }
} }
if (sqlite3_total_changes(db) != last_changecnt) if (sqlite3_total_changes(db) != last_changecnt)
{ {
updateID++; updateID++;
last_changecnt = sqlite3_total_changes(db); last_changecnt = sqlite3_total_changes(db);
upnp_event_var_change_notify(EContentDirectory); upnp_event_var_change_notify(EContentDirectory);
lastupdatetime = timeofday.tv_sec; lastupdatetime = timeofday.tv_sec;
} }
} }
/* process active HTTP connections */
for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next
)
{
if ((e->socket >= 0) && (e->state <= 2) && (FD_ISSET(e->s
ocket, &readset)))
Process_upnphttp(e);
}
/* process incoming HTTP connections */
if (shttpl >= 0 && FD_ISSET(shttpl, &readset))
{
int shttp;
socklen_t clientnamelen;
struct sockaddr_in clientname;
clientnamelen = sizeof(struct sockaddr_in);
shttp = accept(shttpl, (struct sockaddr *)&clientname, &c
lientnamelen);
if (shttp<0)
{
DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n",
strerror(errno));
}
else
{
struct upnphttp * tmp = 0;
DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from
%s:%d\n",
inet_ntoa(clientname.sin_addr),
ntohs(clientname.sin_port) );
/*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETF
L, O_NONBLOCK\n");
}*/
/* Create a new upnphttp object and add it to
* the active upnphttp object list */
tmp = New_upnphttp(shttp);
if (tmp)
{
tmp->clientaddr = clientname.sin_addr;
LIST_INSERT_HEAD(&upnphttphead, tmp, entr
ies);
}
else
{
DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp
() failed\n");
close(shttp);
}
}
}
/* delete finished HTTP connections */ /* delete finished HTTP connections */
for (e = upnphttphead.lh_first; e != NULL; e = next) for (e = upnphttphead.lh_first; e != NULL; e = next)
{ {
next = e->entries.le_next; next = e->entries.le_next;
if(e->state >= 100) if(e->state >= 100)
{ {
LIST_REMOVE(e, entries); LIST_REMOVE(e, entries);
Delete_upnphttp(e); Delete_upnphttp(e);
} }
} }
skipping to change at line 1372 skipping to change at line 1364
if (inotify_thread) if (inotify_thread)
{ {
pthread_kill(inotify_thread, SIGCHLD); pthread_kill(inotify_thread, SIGCHLD);
pthread_join(inotify_thread, NULL); pthread_join(inotify_thread, NULL);
} }
/* kill other child processes */ /* kill other child processes */
process_reap_children(); process_reap_children();
free(children); free(children);
event_module.fini();
sql_exec(db, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID); sql_exec(db, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID);
sqlite3_close(db); sqlite3_close(db);
upnpevents_removeSubscribers(); upnpevents_removeSubscribers();
if (pidfilename && unlink(pidfilename) < 0) if (pidfilename && unlink(pidfilename) < 0)
DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno));
log_close(); log_close();
freeoptions(); freeoptions();
 End of changes. 63 change blocks. 
246 lines changed or deleted 228 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)