static void sighup(int sig) { signal(sig, sighup); DPRINTF(E_WARN, L_GENERAL, "received signal %d, re-read\n", sig); reload_ifaces(1); }
/* process HTTP or SSDP requests */ int main(int argc, char **argv) { int ret, i; int shttpl = -1; int smonitor = -1; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lastnotifytime = {0, 0}; time_t lastupdatetime = 0; int max_fd = -1; int last_changecnt = 0; pid_t scanner_pid = 0; pthread_t inotify_thread = 0; #ifdef TIVO_SUPPORT uint8_t beacon_interval = 5; int sbeacon = -1; struct sockaddr_in tivo_bcast; struct timeval lastbeacontime = {0, 0}; #endif for (i = 0; i < L_MAX; i++) log_level[i] = E_WARN; init_nls(); ret = init(argc, argv); if (ret != 0) return 1; #if (!defined(RTN66U) && !defined(RTN56U)) init_icon(PATH_ICON_PNG_SM); init_icon(PATH_ICON_PNG_LRG); init_icon(PATH_ICON_JPEG_SM); init_icon(PATH_ICON_JPEG_LRG); #endif DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n"); if (sqlite3_libversion_number() < 3005001) { DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n"); } LIST_INIT(&upnphttphead); ret = open_db(NULL); if (ret == 0) { updateID = sql_get_int_field(db, "SELECT VALUE from SETTINGS where KEY = 'UPDATE_ID'"); if (updateID == -1) ret = -1; } check_db(db, ret, &scanner_pid); #ifdef HAVE_INOTIFY if( GETFLAG(INOTIFY_MASK) ) { if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 3005001) DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " "Inotify will be disabled.\n"); else if (pthread_create(&inotify_thread, NULL, start_inotify, NULL) != 0) DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify. EXITING\n"); } #endif smonitor = OpenAndConfMonitorSocket(); sssdp = OpenAndConfSSDPReceiveSocket(); if (sssdp < 0) { DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n"); if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING"); } /* open socket for HTTP connections. */ shttpl = OpenAndConfHTTPSocket(runtime_vars.port); if (shttpl < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n"); DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port); #ifdef TIVO_SUPPORT if (GETFLAG(TIVO_MASK)) { DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); /* Add TiVo-specific randomize function to sqlite */ ret = sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL); if (ret != SQLITE_OK) DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n"); /* open socket for sending Tivo notifications */ sbeacon = OpenAndConfTivoBeaconSocket(); if(sbeacon < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify " "messages. EXITING\n"); tivo_bcast.sin_family = AF_INET; tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress()); tivo_bcast.sin_port = htons(2190); } #endif reload_ifaces(0); lastnotifytime.tv_sec = time(NULL) + runtime_vars.notify_interval; /* main loop */ while (!quitting) { /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if (gettimeofday(&timeofday, 0) < 0) { DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno)); timeout.tv_sec = runtime_vars.notify_interval; timeout.tv_usec = 0; } else { /* the comparison is not very precise but who cares ? */ if (timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) { DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n"); for (i = 0; i < n_lan_addr; i++) { SendSSDPNotifies(lan_addr[i].snotify, lan_addr[i].str, runtime_vars.port, runtime_vars.notify_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 + lastnotifytime.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, sizeof(struct sockaddr_in), 1); memcpy(&lastbeacontime, &timeofday, sizeof(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 (scanning) { if (!scanner_pid || kill(scanner_pid, 0) != 0) { scanning = 0; updateID++; } } /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); if (sssdp >= 0) { FD_SET(sssdp, &readset); max_fd = MAX(max_fd, sssdp); } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX(max_fd, shttpl); } #ifdef TIVO_SUPPORT if (sbeacon >= 0) { FD_SET(sbeacon, &readset); 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 */ for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if ((e->socket >= 0) && (e->state <= 2)) { FD_SET(e->socket, &readset); max_fd = MAX(max_fd, e->socket); i++; } } FD_ZERO(&writeset); 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 sockets. 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.port); } #ifdef TIVO_SUPPORT if (sbeacon >= 0 && FD_ISSET(sbeacon, &readset)) { /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ ProcessTiVoBeacon(sbeacon); } #endif if (smonitor >= 0 && FD_ISSET(smonitor, &readset)) { ProcessMonitorEvent(smonitor); } /* increment SystemUpdateID if the content database has changed, * and if there is an active HTTP connection, at most once every 2 seconds */ if (i && (timeofday.tv_sec >= (lastupdatetime + 2))) { if (scanning || sqlite3_total_changes(db) != last_changecnt) { updateID++; last_changecnt = sqlite3_total_changes(db); upnp_event_var_change_notify(EContentDirectory); 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->socket, &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, &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); } } } /* delete finished HTTP connections */ for (e = upnphttphead.lh_first; e != NULL; e = next) { next = e->entries.le_next; if(e->state >= 100) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } } } shutdown: /* kill the scanner */ if (scanning && scanner_pid) kill(scanner_pid, SIGKILL); /* kill other child processes */ process_reap_children(); free(children); /* close out open sockets */ while (upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sssdp >= 0) close(sssdp); if (shttpl >= 0) close(shttpl); #ifdef TIVO_SUPPORT if (sbeacon >= 0) close(sbeacon); #endif for (i = 0; i < n_lan_addr; i++) { SendSSDPGoodbyes(lan_addr[i].snotify); close(lan_addr[i].snotify); } if (inotify_thread) pthread_join(inotify_thread, NULL); sql_exec(db, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID); sqlite3_close(db); upnpevents_removeSubscribers(); if (pidfilename && unlink(pidfilename) < 0) DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); log_close(); freeoptions(); exit(EXIT_SUCCESS); }
/* init phase : * 1) read configuration file * 2) read command line arguments * 3) daemonize * 4) check and write pid file * 5) set startup time stamp * 6) compute presentation URL * 7) set signal handlers */ static int init(int argc, char **argv) { int i; int pid; int debug_flag = 0; int verbose_flag = 0; int options_flag = 0; struct sigaction sa; const char * presurl = NULL; const char * optionsfile = "/etc/minidlna.conf"; char mac_str[13]; char *string, *word; char *path; char buf[PATH_MAX]; char log_str[75] = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn"; char *log_level = NULL; struct media_dir_s *media_dir; int ifaces = 0; media_types types; uid_t uid = -1; /* first check if "-f" option is used */ for (i=2; i<argc; i++) { if (strcmp(argv[i-1], "-f") == 0) { optionsfile = argv[i]; options_flag = 1; break; } } /* set up uuid based on mac address */ if (getsyshwaddr(mac_str, sizeof(mac_str)) < 0) { DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n"); strcpy(mac_str, "554e4b4e4f57"); } strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-"); strncat(uuidvalue, mac_str, 12); getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN); runtime_vars.port = 8200; runtime_vars.notify_interval = 895; /* seconds between SSDP announces */ runtime_vars.root_container = NULL; runtime_vars.ifaces[0] = NULL; /* read options file first since * command line arguments have final say */ if (readoptionsfile(optionsfile) < 0) { /* only error if file exists or using -f */ if(access(optionsfile, F_OK) == 0 || options_flag) DPRINTF(E_FATAL, L_GENERAL, "Error reading configuration file %s\n", optionsfile); } for (i=0; i<num_options; i++) { switch (ary_options[i].id) { case UPNPIFNAME: for (string = ary_options[i].value; (word = strtok(string, ",")); string = NULL) { if (ifaces >= MAX_LAN_ADDR) { DPRINTF(E_ERROR, L_GENERAL, "Too many interfaces (max: %d), ignoring %s\n", MAX_LAN_ADDR, word); break; } runtime_vars.ifaces[ifaces++] = word; } break; case UPNPPORT: runtime_vars.port = atoi(ary_options[i].value); break; case UPNPPRESENTATIONURL: presurl = ary_options[i].value; break; case UPNPNOTIFY_INTERVAL: runtime_vars.notify_interval = atoi(ary_options[i].value); break; case UPNPSERIAL: strncpyt(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); break; case UPNPMODEL_NAME: strncpyt(modelname, ary_options[i].value, MODELNAME_MAX_LEN); break; case UPNPMODEL_NUMBER: strncpyt(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN); break; case UPNPFRIENDLYNAME: strncpyt(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN); break; case UPNPMEDIADIR: types = ALL_MEDIA; path = ary_options[i].value; word = strchr(path, ','); if (word && (access(path, F_OK) != 0)) { types = 0; while (*path) { if (*path == ',') { path++; break; } else if (*path == 'A' || *path == 'a') types |= TYPE_AUDIO; else if (*path == 'V' || *path == 'v') types |= TYPE_VIDEO; else if (*path == 'P' || *path == 'p') types |= TYPE_IMAGES; else DPRINTF(E_FATAL, L_GENERAL, "Media directory entry not understood [%s]\n", ary_options[i].value); path++; } } path = realpath(path, buf); if (!path || access(path, F_OK) != 0) { DPRINTF(E_ERROR, L_GENERAL, "Media directory \"%s\" not accessible [%s]\n", ary_options[i].value, strerror(errno)); break; } media_dir = calloc(1, sizeof(struct media_dir_s)); media_dir->path = strdup(path); media_dir->types = types; if (media_dirs) { struct media_dir_s *all_dirs = media_dirs; while( all_dirs->next ) all_dirs = all_dirs->next; all_dirs->next = media_dir; } else media_dirs = media_dir; break; case UPNPALBUMART_NAMES: for (string = ary_options[i].value; (word = strtok(string, "/")); string = NULL) { struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s)); int len = strlen(word); if (word[len-1] == '*') { word[len-1] = '\0'; this_name->wildcard = 1; } this_name->name = strdup(word); if (album_art_names) { struct album_art_name_s * all_names = album_art_names; while( all_names->next ) all_names = all_names->next; all_names->next = this_name; } else album_art_names = this_name; } break; case UPNPDBDIR: path = realpath(ary_options[i].value, buf); if (!path) path = (ary_options[i].value); make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (access(path, F_OK) != 0) DPRINTF(E_FATAL, L_GENERAL, "Database path not accessible! [%s]\n", path); strncpyt(db_path, path, PATH_MAX); break; case UPNPLOGDIR: path = realpath(ary_options[i].value, buf); if (!path) path = (ary_options[i].value); make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); if (access(path, F_OK) != 0) DPRINTF(E_FATAL, L_GENERAL, "Log path not accessible! [%s]\n", path); strncpyt(log_path, path, PATH_MAX); break; case UPNPLOGLEVEL: log_level = ary_options[i].value; break; case UPNPINOTIFY: if ((strcmp(ary_options[i].value, "yes") != 0) && !atoi(ary_options[i].value)) CLEARFLAG(INOTIFY_MASK); break; case ENABLE_TIVO: if ((strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value)) SETFLAG(TIVO_MASK); break; case ENABLE_DLNA_STRICT: if ((strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value)) SETFLAG(DLNA_STRICT_MASK); break; case ROOT_CONTAINER: switch (ary_options[i].value[0]) { case '.': runtime_vars.root_container = NULL; break; case 'B': case 'b': runtime_vars.root_container = BROWSEDIR_ID; break; case 'M': case 'm': runtime_vars.root_container = MUSIC_ID; break; case 'V': case 'v': runtime_vars.root_container = VIDEO_ID; break; case 'P': case 'p': runtime_vars.root_container = IMAGE_ID; break; default: DPRINTF(E_ERROR, L_GENERAL, "Invalid root container! [%s]\n", ary_options[i].value); break; } break; case UPNPMINISSDPDSOCKET: minissdpdsocketpath = ary_options[i].value; break; case UPNPUUID: strcpy(uuidvalue+5, ary_options[i].value); break; case USER_ACCOUNT: uid = strtol(ary_options[i].value, &string, 0); if (*string) { /* Symbolic username given, not UID. */ struct passwd *entry = getpwnam(ary_options[i].value); if (!entry) DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); uid = entry->pw_uid; } break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n", optionsfile); } } if (log_path[0] == '\0') { if (db_path[0] == '\0') strncpyt(log_path, DEFAULT_LOG_PATH, PATH_MAX); 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 */ for (i=1; i<argc; i++) { if (argv[i][0] != '-') { DPRINTF(E_FATAL, L_GENERAL, "Unknown option: %s\n", argv[i]); } else if (strcmp(argv[i], "--help") == 0) { runtime_vars.port = 0; break; } else switch(argv[i][1]) { case 't': if (i+1 < argc) runtime_vars.notify_interval = atoi(argv[++i]); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 's': if (i+1 < argc) strncpyt(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'm': if (i+1 < argc) strncpyt(modelnumber, argv[++i], MODELNUMBER_MAX_LEN); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'p': if (i+1 < argc) runtime_vars.port = atoi(argv[++i]); else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'P': if (i+1 < argc) { if (argv[++i][0] != '/') DPRINTF(E_FATAL, L_GENERAL, "Option -%c requires an absolute filename.\n", argv[i-1][1]); else pidfilename = argv[i]; } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'd': debug_flag = 1; case 'v': verbose_flag = 1; break; case 'L': SETFLAG(NO_PLAYLIST_MASK); break; case 'w': if (i+1 < argc) presurl = argv[++i]; else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'i': if (i+1 < argc) { i++; if (ifaces >= MAX_LAN_ADDR) { DPRINTF(E_ERROR, L_GENERAL, "Too many interfaces (max: %d), ignoring %s\n", MAX_LAN_ADDR, argv[i]); break; } runtime_vars.ifaces[ifaces++] = argv[i]; } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'f': i++; /* discarding, the config file is already read */ break; case 'h': runtime_vars.port = 0; // triggers help display break; case 'R': snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cache", db_path, db_path); if (system(buf) != 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to clean old file cache. EXITING\n"); break; case 'u': if (i+1 != argc) { i++; uid = strtol(argv[i], &string, 0); if (*string) { /* Symbolic username given, not UID. */ struct passwd *entry = getpwnam(argv[i]); if (!entry) DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); uid = entry->pw_uid; } } else DPRINTF(E_FATAL, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; break; #ifdef __linux__ case 'S': SETFLAG(SYSTEMD_MASK); break; #endif case 'V': printf("Version " MINIDLNA_VERSION "\n"); exit(0); break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[i]); } } if (runtime_vars.port <= 0) { printf("Usage:\n\t" "%s [-d] [-v] [-f config_file] [-p port]\n" "\t\t[-i network_interface] [-u uid_to_run_as]\n" "\t\t[-t notify_interval] [-P pid_filename]\n" "\t\t[-s serial] [-m model_number]\n" #ifdef __linux__ "\t\t[-w url] [-R] [-L] [-S] [-V] [-h]\n" #else "\t\t[-w url] [-R] [-L] [-V] [-h]\n" #endif "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n" "\tDefault pid file is %s.\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-v enables verbose output\n" "\t-h displays this text\n" "\t-R forces a full rescan\n" "\t-L do not create playlists\n" #ifdef __linux__ "\t-S changes behaviour for systemd\n" #endif "\t-V print the version number\n", argv[0], pidfilename); return 1; } if (verbose_flag) { strcpy(log_str+65, "debug"); log_level = log_str; } else if (!log_level) log_level = log_str; /* Set the default log file path to NULL (stdout) */ path = NULL; if (debug_flag) { pid = getpid(); strcpy(log_str+65, "maxdebug"); log_level = log_str; } else if (GETFLAG(SYSTEMD_MASK)) { pid = getpid(); } else { pid = daemonize(); #ifdef READYNAS unlink("/ramfs/.upnp-av_scan"); path = "/var/log/upnp-av.log"; #else if (access(db_path, F_OK) != 0) 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 (checkforrunning(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n"); return 1; } set_startup_time(); reload_ifaces(0); /* presentation url */ if (presurl) strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); else strcpy(presentationurl, "/"); /* set signal handlers */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm; if (sigaction(SIGTERM, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGTERM"); if (sigaction(SIGINT, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGINT"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGPIPE"); if (signal(SIGHUP, &sighup) == SIG_ERR) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGHUP"); if (writepidfile(pidfilename, pid, uid) != 0) pidfilename = NULL; if (uid != -1 && setuid(uid) == -1) DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to uid '%d'. [%s] EXITING.\n", uid, strerror(errno)); return 0; }