/* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; int sudp = -1, shttpl = -1; int snotify[MAX_LAN_ADDR]; 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; struct media_dir_s *media_path, *last_path; struct album_art_name_s *art_names, *last_name; #ifdef TIVO_SUPPORT unsigned short int 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; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, "en_US.utf8"); DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR"))); textdomain("minidlna"); #endif if (init(argc, argv) != 0) return 1; #ifdef READYNAS DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n"); unlink("/ramfs/.upnp-av_scan"); #else DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION " [SQLite %s].\n", sqlite3_libversion()); if( !sqlite3_threadsafe() ) { DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " "Scanning must be finished before file serving can begin, " "and inotify will be disabled.\n"); } if( sqlite3_libversion_number() < 3005001 ) { DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n"); } #endif LIST_INIT(&upnphttphead); if( open_db() == 0 ) { updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS"); } i = db_upgrade(db); if( i != 0 ) { if( i < 0 ) { DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n"); } else { DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n"); } sqlite3_close(db); char *cmd; i = asprintf(&cmd, "rm -rf %s/files.db %s/art_cache", db_path, db_path); if( i > 0 ) i = system(cmd); else cmd = NULL; if( i != 0 ) { DPRINTF(E_WARN, L_GENERAL, "Failed to clean old file cache.\n"); } free(cmd); open_db(); if( CreateDatabase() != 0 ) { DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n"); } #if USE_FORK scanning = 1; sqlite3_close(db); scanner_pid = fork(); open_db(); if( !scanner_pid ) // child (scanner) process { start_scanner(); sqlite3_close(db); media_path = media_dirs; art_names = album_art_names; while( media_path ) { free(media_path->path); last_path = media_path; media_path = media_path->next; free(last_path); } while( art_names ) { free(art_names->name); last_name = art_names; art_names = art_names->next; free(last_name); } freeoptions(); exit(EXIT_SUCCESS); } #else start_scanner(); #endif } signal(SIGCHLD, SIG_IGN); if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 && GETFLAG(INOTIFY_MASK) && pthread_create(&inotify_thread, NULL, start_inotify, NULL) ) { DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n"); } sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); if(sudp < 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"); return 1; } } /* open socket for HTTP connections. Listen on the 1st LAN address */ 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); /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) { DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify " "messages. EXITING\n"); } #ifdef TIVO_SUPPORT if( GETFLAG(TIVO_MASK) ) { DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); /* Add TiVo-specific randomize function to sqlite */ if( sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL) != 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); } else { sbeacon = -1; } #endif SendSSDPGoodbye(snotify, n_lan_addr); /* 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 comparaison is not very precise but who cares ? */ if(timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) { SendSSDPNotifies2(snotify, (unsigned short)runtime_vars.port, (runtime_vars.notify_interval << 1)+10); 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( GETFLAG(TIVO_MASK) ) { 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) ) { scanning = 0; updateID++; } } /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); if (sudp >= 0) { FD_SET(sudp, &readset); max_fd = MAX(max_fd, sudp); } 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 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++; } } #ifdef DEBUG /* for debug */ if(i > 1) { DPRINTF(E_DEBUG, L_GENERAL, "%d active incoming HTTP connections\n", i); } #endif FD_ZERO(&writeset); upnpevents_selectfds(&readset, &writeset, &max_fd); if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; 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(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ ProcessSSDPRequest(sudp, (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 /* 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; ) { next = e->entries.le_next; if(e->state >= 100) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } shutdown: /* kill the scanner */ if( scanning && scanner_pid ) { kill(scanner_pid, 9); } /* close out open sockets */ while(upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sudp >= 0) close(sudp); if (shttpl >= 0) close(shttpl); #ifdef TIVO_SUPPORT if (sbeacon >= 0) close(sbeacon); #endif if(SendSSDPGoodbye(snotify, n_lan_addr) < 0) { DPRINTF(E_ERROR, L_GENERAL, "Failed to broadcast good-bye notifications\n"); } for(i=0; i<n_lan_addr; i++) close(snotify[i]); if( inotify_thread ) pthread_join(inotify_thread, NULL); sql_exec(db, "UPDATE SETTINGS set UPDATE_ID = %u", updateID); sqlite3_close(db); upnpevents_removeSubscribers(); media_path = media_dirs; art_names = album_art_names; while( media_path ) { free(media_path->path); last_path = media_path; media_path = media_path->next; free(last_path); } while( art_names ) { free(art_names->name); last_name = art_names; art_names = art_names->next; free(last_name); } if(unlink(pidfilename) < 0) { DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); } freeoptions(); exit(EXIT_SUCCESS); }
/* 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); }