/* 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 = 0; /* 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.max_connections = 50; 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 (!strtobool(ary_options[i].value)) CLEARFLAG(INOTIFY_MASK); break; case ENABLE_TIVO: if (strtobool(ary_options[i].value)) SETFLAG(TIVO_MASK); break; case ENABLE_DLNA_STRICT: if (strtobool(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: runtime_vars.root_container = ary_options[i].value; DPRINTF(E_WARN, L_GENERAL, "Using arbitrary 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 = strtoul(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; case FORCE_SORT_CRITERIA: force_sort_criteria = ary_options[i].value; break; case MAX_CONNECTIONS: runtime_vars.max_connections = atoi(ary_options[i].value); break; case MERGE_MEDIA_DIRS: if (strtobool(ary_options[i].value)) SETFLAG(MERGE_MEDIA_DIRS_MASK); 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 = -1; 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 = -1; // 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 = strtoul(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]); runtime_vars.port = -1; // triggers help display } } 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 = process_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 (process_check_if_running(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, SERVER_NAME " is already running. EXITING.\n"); return 1; } set_startup_time(); /* 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"); signal(SIGUSR1, &sigusr1); sa.sa_handler = process_handle_child_termination; if (sigaction(SIGCHLD, &sa, NULL)) DPRINTF(E_FATAL, L_GENERAL, "Failed to set %s handler. EXITING.\n", "SIGCHLD"); if (writepidfile(pidfilename, pid, uid) != 0) pidfilename = NULL; if (uid > 0) { struct stat st; 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] ownership to %d: %s\n", db_path, uid, strerror(errno)); } if (uid > 0 && setuid(uid) == -1) DPRINTF(E_FATAL, L_GENERAL, "Failed to switch to uid '%d'. [%s] EXITING.\n", uid, strerror(errno)); children = calloc(runtime_vars.max_connections, sizeof(struct child)); if (!children) { DPRINTF(E_ERROR, L_GENERAL, "Allocation failed\n"); return 1; } // remove working flag remove_scantag(); return 0; }
/* 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; enum media_types type; char * path; char buf[PATH_MAX]; char ip_addr[INET_ADDRSTRLEN + 3] = {'\0'}; char log_str[72] = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn"; char *log_level = NULL; /* first check if "-f" option is used */ for(i=2; i<argc; i++) { if(0 == strcmp(argv[i-1], "-f")) { 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 = -1; runtime_vars.notify_interval = 895; /* seconds between SSDP announces */ runtime_vars.root_container = 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_ERROR, L_GENERAL, "Error reading configuration file %s\n", optionsfile); } else { 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(n_lan_addr < MAX_LAN_ADDR) { if(getifaddr(word, ip_addr, sizeof(ip_addr)) >= 0) { if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 ) if(n_lan_addr < MAX_LAN_ADDR) n_lan_addr++; } } else { DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", MAX_LAN_ADDR, word); } } break; case UPNPLISTENING_IP: if(n_lan_addr < MAX_LAN_ADDR) { if(parselanaddr(&lan_addr[n_lan_addr], ary_options[i].value) == 0) n_lan_addr++; } else { DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", MAX_LAN_ADDR, ary_options[i].value); } 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: type = ALL_MEDIA; char * myval = NULL; switch( ary_options[i].value[0] ) { case 'A': case 'a': if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' ) type = AUDIO_ONLY; case 'V': case 'v': if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' ) type = VIDEO_ONLY; case 'P': case 'p': if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' ) type = IMAGES_ONLY; myval = index(ary_options[i].value, '/'); case '/': path = realpath(myval ? myval:ary_options[i].value, buf); if( !path ) path = (myval ? myval:ary_options[i].value); if( access(path, F_OK) != 0 ) { DPRINTF(E_ERROR, L_GENERAL, "Media directory \"%s\" not accessible! [%s]\n", path, strerror(errno)); break; } struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s)); this_dir->path = strdup(path); this_dir->type = type; if( !media_dirs ) { media_dirs = this_dir; } else { struct media_dir_s * all_dirs = media_dirs; while( all_dirs->next ) all_dirs = all_dirs->next; all_dirs->next = this_dir; } break; default: DPRINTF(E_ERROR, L_GENERAL, "Media directory entry not understood! [%s]\n", ary_options[i].value); break; } 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 ) { album_art_names = this_name; } else { struct album_art_name_s * all_names = album_art_names; while( all_names->next ) all_names = all_names->next; all_names->next = 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); break; } 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); break; } 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; default: DPRINTF(E_DEBUG, 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_ERROR, 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_ERROR, 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_ERROR, 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_ERROR, 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_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'P': if(i+1 < argc) pidfilename = argv[++i]; else DPRINTF(E_ERROR, 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_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'a': if(i+1 < argc) { int address_already_there = 0; int j; i++; for(j=0; j<n_lan_addr; j++) { struct lan_addr_s tmpaddr; parselanaddr(&tmpaddr, argv[i]); if(0 == strcmp(lan_addr[j].str, tmpaddr.str)) address_already_there = 1; } if(address_already_there) break; if(n_lan_addr < MAX_LAN_ADDR) { if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0) n_lan_addr++; } else { DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", MAX_LAN_ADDR, argv[i]); } } else DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); break; case 'i': if(i+1 < argc) { int address_already_there = 0; int j; i++; if( getifaddr(argv[i], ip_addr, sizeof(ip_addr)) < 0 ) { DPRINTF(E_FATAL, L_GENERAL, "Required network interface '%s' not found.\n", argv[i]); } for(j=0; j<n_lan_addr; j++) { struct lan_addr_s tmpaddr; parselanaddr(&tmpaddr, ip_addr); if(0 == strcmp(lan_addr[j].str, tmpaddr.str)) address_already_there = 1; } if(address_already_there) break; if(n_lan_addr < MAX_LAN_ADDR) { if(parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0) n_lan_addr++; } else { DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", MAX_LAN_ADDR, argv[i]); } } else DPRINTF(E_ERROR, 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_WARN, L_GENERAL, "Failed to clean old file cache.\n"); break; case 'V': printf("Version " MINIDLNA_VERSION "\n"); exit(0); break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[i]); } } /* If no IP was specified, try to detect one */ if( n_lan_addr < 1 ) { if( getsysaddrs() <= 0 ) { DPRINTF(E_FATAL, L_GENERAL, "No IP address automatically detected!\n"); } } if( (n_lan_addr==0) || (runtime_vars.port<=0) ) { DPRINTF(E_ERROR, L_GENERAL, "Usage:\n\t" "%s [-d] [-v] [-f config_file]\n" "\t\t[-a listening_ip] [-p port]\n" /*"[-l logfile] " not functionnal */ "\t\t[-s serial] [-m model_number] \n" "\t\t[-t notify_interval] [-P pid_filename]\n" "\t\t[-w url] [-R] [-V] [-h]\n" "\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-h displays this text\n" "\t-R forces a full rescan\n" "\t-L do note create playlists\n" "\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; } if(debug_flag) { pid = getpid(); log_init(NULL, log_level); } else { pid = daemonize(); #ifdef READYNAS log_init("/var/log/upnp-av.log", log_level); #else if( access(db_path, F_OK) != 0 ) make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); sprintf(buf, "%s/minidlna.log", log_path); log_init(buf, log_level); #endif } if(checkforrunning(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n"); return 1; } set_startup_time(); /* presentation url */ if(presurl) strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); else strcpy(presentationurl, "/"); /* set signal handler */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm; if (sigaction(SIGTERM, &sa, NULL)) { DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGTERM handler. EXITING.\n"); } if (sigaction(SIGINT, &sa, NULL)) { DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGINT handler. EXITING.\n"); } if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) { DPRINTF(E_FATAL, L_GENERAL, "Failed to ignore SIGPIPE signals. EXITING.\n"); } writepidfile(pidfilename, pid); // remove working flag remove_scantag(); return 0; }