3 * http://sourceforge.net/projects/minidlna/
5 * MiniDLNA media server
6 * Copyright (C) 2008-2012 Justin Maggard
8 * This file is part of MiniDLNA.
10 * MiniDLNA is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * MiniDLNA is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
22 * Portions of the code from the MiniUPnP project:
24 * Copyright (c) 2006-2007, Thomas Bernard
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 * * Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * * Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * * The name of the author may not be used to endorse or promote products
35 * derived from this software without specific prior written permission.
37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
38 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
41 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47 * POSSIBILITY OF SUCH DAMAGE.
54 #include <sys/types.h>
55 #include <sys/socket.h>
59 #include <sys/param.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
79 #include "upnpglobalvars.h"
82 #include "upnpdescgen.h"
83 #include "minidlnapath.h"
84 #include "getifaddr.h"
89 #include "minidlnatypes.h"
91 #include "upnpevents.h"
95 #include "tivo_beacon.h"
96 #include "tivo_utils.h"
98 #if SQLITE_VERSION_NUMBER < 3005001
99 # warning "Your SQLite3 library appears to be too old! Please use 3.5.1 or newer."
100 # define sqlite3_threadsafe() 0
103 /* OpenAndConfHTTPSocket() :
104 * setup the socket used to handle incoming HTTP connections. */
106 OpenAndConfHTTPSocket(unsigned short port
)
110 struct sockaddr_in listenname
;
112 /* Initialize client type cache */
113 memset(&clients
, 0, sizeof(struct client_cache_s
));
115 s
= socket(PF_INET
, SOCK_STREAM
, 0);
118 DPRINTF(E_ERROR
, L_GENERAL
, "socket(http): %s\n", strerror(errno
));
122 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
123 DPRINTF(E_WARN
, L_GENERAL
, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno
));
125 memset(&listenname
, 0, sizeof(struct sockaddr_in
));
126 listenname
.sin_family
= AF_INET
;
127 listenname
.sin_port
= htons(port
);
128 listenname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
130 if (bind(s
, (struct sockaddr
*)&listenname
, sizeof(struct sockaddr_in
)) < 0)
132 DPRINTF(E_ERROR
, L_GENERAL
, "bind(http): %s\n", strerror(errno
));
137 if (listen(s
, 6) < 0)
139 DPRINTF(E_ERROR
, L_GENERAL
, "listen(http): %s\n", strerror(errno
));
147 /* Handler for the SIGTERM signal (kill)
148 * SIGINT is also handled */
152 signal(sig
, SIG_IGN
); /* Ignore this signal while we are quitting */
154 DPRINTF(E_WARN
, L_GENERAL
, "received signal %d, good-bye\n", sig
);
162 signal(sig
, sigusr1
);
163 DPRINTF(E_WARN
, L_GENERAL
, "received signal %d, clear cache\n", sig
);
165 memset(&clients
, '\0', sizeof(clients
));
172 DPRINTF(E_WARN
, L_GENERAL
, "received signal %d, re-read\n", sig
);
177 /* record the startup time */
179 set_startup_time(void)
181 startup_time
= time(NULL
);
185 getfriendlyname(char *buf
, int len
)
191 if (gethostname(hn
, sizeof(hn
)) == 0)
193 strncpyt(buf
, hn
, len
);
194 p
= strchr(buf
, '.');
199 strcpy(buf
, "Unknown");
202 off
+= snprintf(buf
+off
, len
-off
, ": ");
205 char ibuf
[64], *key
, *val
;
206 snprintf(buf
+off
, len
-off
, "ReadyNAS");
207 info
= fopen("/proc/sys/dev/boot/info", "r");
210 while ((val
= fgets(ibuf
, 64, info
)) != NULL
)
212 key
= strsep(&val
, ": \t");
214 if (strcmp(key
, "model") == 0)
216 snprintf(buf
+off
, len
-off
, "%s", val
);
217 key
= strchr(val
, ' ');
220 strncpyt(modelnumber
, key
+1, MODELNUMBER_MAX_LEN
);
223 snprintf(modelname
, MODELNAME_MAX_LEN
,
224 "Windows Media Connect compatible (%s)", val
);
226 else if (strcmp(key
, "serial") == 0)
228 strncpyt(serialnumber
, val
, SERIALNUMBER_MAX_LEN
);
229 if (serialnumber
[0] == '\0')
232 if (getsyshwaddr(mac_str
, sizeof(mac_str
)) == 0)
233 strcpy(serialnumber
, mac_str
);
235 strcpy(serialnumber
, "0");
242 memcpy(pnpx_hwid
+4, "01F2", 4);
243 if (strcmp(modelnumber
, "NVX") == 0)
244 memcpy(pnpx_hwid
+17, "0101", 4);
245 else if (strcmp(modelnumber
, "Pro") == 0 ||
246 strcmp(modelnumber
, "Pro 6") == 0 ||
247 strncmp(modelnumber
, "Ultra 6", 7) == 0)
248 memcpy(pnpx_hwid
+17, "0102", 4);
249 else if (strcmp(modelnumber
, "Pro 2") == 0 ||
250 strncmp(modelnumber
, "Ultra 2", 7) == 0)
251 memcpy(pnpx_hwid
+17, "0103", 4);
252 else if (strcmp(modelnumber
, "Pro 4") == 0 ||
253 strncmp(modelnumber
, "Ultra 4", 7) == 0)
254 memcpy(pnpx_hwid
+17, "0104", 4);
255 else if (strcmp(modelnumber
+1, "100") == 0)
256 memcpy(pnpx_hwid
+17, "0105", 4);
257 else if (strcmp(modelnumber
+1, "200") == 0)
258 memcpy(pnpx_hwid
+17, "0106", 4);
260 else if (strcmp(modelnumber
, "Duo v2") == 0)
261 memcpy(pnpx_hwid
+17, "0108", 4);
262 else if (strcmp(modelnumber
, "NV+ v2") == 0)
263 memcpy(pnpx_hwid
+17, "0109", 4);
267 logname
= getenv("LOGNAME");
268 #ifndef STATIC // Disable for static linking
271 struct passwd
* pwent
;
272 pwent
= getpwuid(getuid());
274 logname
= pwent
->pw_name
;
277 snprintf(buf
+off
, len
-off
, "%s", logname
?logname
:"Unknown");
282 open_db(sqlite3
**sq3
)
287 snprintf(path
, sizeof(path
), "%s/files.db", db_path
);
288 if (access(path
, F_OK
) != 0)
291 make_dir(db_path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
293 if (sqlite3_open(path
, &db
) != SQLITE_OK
)
294 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: Failed to open sqlite database! Exiting...\n");
297 sqlite3_busy_timeout(db
, 5000);
298 sql_exec(db
, "pragma page_size = 4096");
299 sql_exec(db
, "pragma journal_mode = OFF");
300 sql_exec(db
, "pragma synchronous = OFF;");
301 // this sets the sqlite database cache size
302 // original code had 8192 = 32MB - reduce it to 4MB
303 sql_exec(db
, "pragma default_cache_size = 1024;");
309 check_db(sqlite3
*db
, int new_db
, pid_t
*scanner_pid
)
311 struct media_dir_s
*media_path
= NULL
;
312 char cmd
[PATH_MAX
*2];
319 /* Check if any new media dirs appeared */
320 media_path
= media_dirs
;
323 ret
= sql_get_int_field(db
, "SELECT TIMESTAMP from DETAILS where PATH = %Q", media_path
->path
);
324 if (ret
!= media_path
->types
)
329 media_path
= media_path
->next
;
331 /* Check if any media dirs disappeared */
332 sql_get_table(db
, "SELECT VALUE from SETTINGS where KEY = 'media_dir'", &result
, &rows
, NULL
);
333 for (i
=1; i
<= rows
; i
++)
335 media_path
= media_dirs
;
338 if (strcmp(result
[i
], media_path
->path
) == 0)
340 media_path
= media_path
->next
;
345 sqlite3_free_table(result
);
349 sqlite3_free_table(result
);
352 ret
= db_upgrade(db
);
357 DPRINTF(E_WARN
, L_GENERAL
, "Creating new database at %s/files.db\n", db_path
);
359 DPRINTF(E_WARN
, L_GENERAL
, "New media_dir detected; rescanning...\n");
361 DPRINTF(E_WARN
, L_GENERAL
, "Removed media_dir detected; rescanning...\n");
363 DPRINTF(E_WARN
, L_GENERAL
, "Database version mismatch (%d=>%d); need to recreate...\n",
367 snprintf(cmd
, sizeof(cmd
), "rm -rf %s/files.db %s/art_cache", db_path
, db_path
);
368 if (system(cmd
) != 0)
369 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to clean old file cache! Exiting...\n");
372 if (CreateDatabase() != 0)
373 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: Failed to create sqlite database! Exiting...\n");
377 *scanner_pid
= fork();
379 if (*scanner_pid
== 0) /* child (scanner) process */
388 else if (*scanner_pid
< 0)
399 writepidfile(const char *fname
, int pid
, uid_t uid
)
403 char path
[PATH_MAX
], *dir
;
406 if(!fname
|| *fname
== '\0')
409 /* Create parent directory if it doesn't already exist */
410 strncpyt(path
, fname
, sizeof(path
));
412 if (stat(dir
, &st
) == 0)
414 if (!S_ISDIR(st
.st_mode
))
416 DPRINTF(E_ERROR
, L_GENERAL
, "Pidfile path is not a directory: %s\n",
423 if (make_dir(dir
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) != 0)
425 DPRINTF(E_ERROR
, L_GENERAL
, "Unable to create pidfile directory: %s\n",
431 if (chown(dir
, uid
, -1) != 0)
432 DPRINTF(E_WARN
, L_GENERAL
, "Unable to change pidfile %s ownership: %s\n",
433 dir
, strerror(errno
));
437 pidfile
= fopen(fname
, "w");
440 DPRINTF(E_ERROR
, L_GENERAL
, "Unable to open pidfile for writing %s: %s\n",
441 fname
, strerror(errno
));
445 if (fprintf(pidfile
, "%d\n", pid
) <= 0)
447 DPRINTF(E_ERROR
, L_GENERAL
,
448 "Unable to write to pidfile %s: %s\n", fname
, strerror(errno
));
453 if (fchown(fileno(pidfile
), uid
, -1) != 0)
454 DPRINTF(E_WARN
, L_GENERAL
, "Unable to change pidfile %s ownership: %s\n",
455 fname
, strerror(errno
));
463 static int strtobool(const char *str
)
465 return ((strcasecmp(str
, "yes") == 0) ||
466 (strcasecmp(str
, "true") == 0) ||
470 static void init_nls(void)
473 setlocale(LC_MESSAGES
, "");
474 setlocale(LC_CTYPE
, "en_US.utf8");
475 DPRINTF(E_DEBUG
, L_GENERAL
, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR")));
476 textdomain("minidlna");
481 * 1) read configuration file
482 * 2) read command line arguments
484 * 4) check and write pid file
485 * 5) set startup time stamp
486 * 6) compute presentation URL
487 * 7) set signal handlers */
489 init(int argc
, char **argv
)
494 int verbose_flag
= 0;
495 int options_flag
= 0;
497 const char * presurl
= NULL
;
498 const char * optionsfile
= "/etc/minidlna.conf";
503 char log_str
[75] = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn";
504 char *log_level
= NULL
;
505 struct media_dir_s
*media_dir
;
510 /* first check if "-f" option is used */
511 for (i
=2; i
<argc
; i
++)
513 if (strcmp(argv
[i
-1], "-f") == 0)
515 optionsfile
= argv
[i
];
521 /* set up uuid based on mac address */
522 if (getsyshwaddr(mac_str
, sizeof(mac_str
)) < 0)
524 DPRINTF(E_OFF
, L_GENERAL
, "No MAC address found. Falling back to generic UUID.\n");
525 strcpy(mac_str
, "554e4b4e4f57");
527 strcpy(uuidvalue
+5, "4d696e69-444c-164e-9d41-");
528 strncat(uuidvalue
, mac_str
, 12);
530 getfriendlyname(friendly_name
, FRIENDLYNAME_MAX_LEN
);
532 runtime_vars
.port
= 8200;
533 runtime_vars
.notify_interval
= 895; /* seconds between SSDP announces */
534 runtime_vars
.max_connections
= 50;
535 runtime_vars
.root_container
= NULL
;
536 runtime_vars
.ifaces
[0] = NULL
;
538 /* read options file first since
539 * command line arguments have final say */
540 if (readoptionsfile(optionsfile
) < 0)
542 /* only error if file exists or using -f */
543 if(access(optionsfile
, F_OK
) == 0 || options_flag
)
544 DPRINTF(E_FATAL
, L_GENERAL
, "Error reading configuration file %s\n", optionsfile
);
547 for (i
=0; i
<num_options
; i
++)
549 switch (ary_options
[i
].id
)
552 for (string
= ary_options
[i
].value
; (word
= strtok(string
, ",")); string
= NULL
)
554 if (ifaces
>= MAX_LAN_ADDR
)
556 DPRINTF(E_ERROR
, L_GENERAL
, "Too many interfaces (max: %d), ignoring %s\n",
560 while (isspace(*word
))
562 runtime_vars
.ifaces
[ifaces
++] = word
;
566 runtime_vars
.port
= atoi(ary_options
[i
].value
);
568 case UPNPPRESENTATIONURL
:
569 presurl
= ary_options
[i
].value
;
571 case UPNPNOTIFY_INTERVAL
:
572 runtime_vars
.notify_interval
= atoi(ary_options
[i
].value
);
575 strncpyt(serialnumber
, ary_options
[i
].value
, SERIALNUMBER_MAX_LEN
);
578 strncpyt(modelname
, ary_options
[i
].value
, MODELNAME_MAX_LEN
);
580 case UPNPMODEL_NUMBER
:
581 strncpyt(modelnumber
, ary_options
[i
].value
, MODELNUMBER_MAX_LEN
);
583 case UPNPFRIENDLYNAME
:
584 strncpyt(friendly_name
, ary_options
[i
].value
, FRIENDLYNAME_MAX_LEN
);
588 path
= ary_options
[i
].value
;
589 word
= strchr(path
, ',');
590 if (word
&& (access(path
, F_OK
) != 0))
600 else if (*path
== 'A' || *path
== 'a')
602 else if (*path
== 'V' || *path
== 'v')
604 else if (*path
== 'P' || *path
== 'p')
605 types
|= TYPE_IMAGES
;
607 DPRINTF(E_FATAL
, L_GENERAL
, "Media directory entry not understood [%s]\n",
608 ary_options
[i
].value
);
612 path
= realpath(path
, buf
);
613 if (!path
|| access(path
, F_OK
) != 0)
615 DPRINTF(E_ERROR
, L_GENERAL
, "Media directory \"%s\" not accessible [%s]\n",
616 ary_options
[i
].value
, strerror(errno
));
619 media_dir
= calloc(1, sizeof(struct media_dir_s
));
620 media_dir
->path
= strdup(path
);
621 media_dir
->types
= types
;
624 struct media_dir_s
*all_dirs
= media_dirs
;
625 while( all_dirs
->next
)
626 all_dirs
= all_dirs
->next
;
627 all_dirs
->next
= media_dir
;
630 media_dirs
= media_dir
;
632 case UPNPALBUMART_NAMES
:
633 for (string
= ary_options
[i
].value
; (word
= strtok(string
, "/")); string
= NULL
)
635 struct album_art_name_s
* this_name
= calloc(1, sizeof(struct album_art_name_s
));
636 int len
= strlen(word
);
637 if (word
[len
-1] == '*')
640 this_name
->wildcard
= 1;
642 this_name
->name
= strdup(word
);
645 struct album_art_name_s
* all_names
= album_art_names
;
646 while( all_names
->next
)
647 all_names
= all_names
->next
;
648 all_names
->next
= this_name
;
651 album_art_names
= this_name
;
655 path
= realpath(ary_options
[i
].value
, buf
);
657 path
= (ary_options
[i
].value
);
658 make_dir(path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
659 if (access(path
, F_OK
) != 0)
660 DPRINTF(E_FATAL
, L_GENERAL
, "Database path not accessible! [%s]\n", path
);
661 strncpyt(db_path
, path
, PATH_MAX
);
664 path
= realpath(ary_options
[i
].value
, buf
);
666 path
= (ary_options
[i
].value
);
667 make_dir(path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
668 if (access(path
, F_OK
) != 0)
669 DPRINTF(E_FATAL
, L_GENERAL
, "Log path not accessible! [%s]\n", path
);
670 strncpyt(log_path
, path
, PATH_MAX
);
673 log_level
= ary_options
[i
].value
;
676 if (!strtobool(ary_options
[i
].value
))
677 CLEARFLAG(INOTIFY_MASK
);
680 if (strtobool(ary_options
[i
].value
))
683 case ENABLE_DLNA_STRICT
:
684 if (strtobool(ary_options
[i
].value
))
685 SETFLAG(DLNA_STRICT_MASK
);
688 switch (ary_options
[i
].value
[0]) {
690 runtime_vars
.root_container
= NULL
;
694 runtime_vars
.root_container
= BROWSEDIR_ID
;
698 runtime_vars
.root_container
= MUSIC_ID
;
702 runtime_vars
.root_container
= VIDEO_ID
;
706 runtime_vars
.root_container
= IMAGE_ID
;
709 runtime_vars
.root_container
= ary_options
[i
].value
;
710 DPRINTF(E_WARN
, L_GENERAL
, "Using arbitrary root container [%s]\n",
711 ary_options
[i
].value
);
715 case UPNPMINISSDPDSOCKET
:
716 minissdpdsocketpath
= ary_options
[i
].value
;
719 strcpy(uuidvalue
+5, ary_options
[i
].value
);
722 uid
= strtoul(ary_options
[i
].value
, &string
, 0);
725 /* Symbolic username given, not UID. */
726 struct passwd
*entry
= getpwnam(ary_options
[i
].value
);
728 DPRINTF(E_FATAL
, L_GENERAL
, "Bad user '%s'.\n",
729 ary_options
[i
].value
);
733 case FORCE_SORT_CRITERIA
:
734 force_sort_criteria
= ary_options
[i
].value
;
736 case MAX_CONNECTIONS
:
737 runtime_vars
.max_connections
= atoi(ary_options
[i
].value
);
739 case MERGE_MEDIA_DIRS
:
740 if (strtobool(ary_options
[i
].value
))
741 SETFLAG(MERGE_MEDIA_DIRS_MASK
);
744 DPRINTF(E_ERROR
, L_GENERAL
, "Unknown option in file %s\n",
748 if (log_path
[0] == '\0')
750 if (db_path
[0] == '\0')
751 strncpyt(log_path
, DEFAULT_LOG_PATH
, PATH_MAX
);
753 strncpyt(log_path
, db_path
, PATH_MAX
);
755 if (db_path
[0] == '\0')
756 strncpyt(db_path
, DEFAULT_DB_PATH
, PATH_MAX
);
758 /* command line arguments processing */
759 for (i
=1; i
<argc
; i
++)
761 if (argv
[i
][0] != '-')
763 DPRINTF(E_FATAL
, L_GENERAL
, "Unknown option: %s\n", argv
[i
]);
765 else if (strcmp(argv
[i
], "--help") == 0)
767 runtime_vars
.port
= -1;
770 else switch(argv
[i
][1])
774 runtime_vars
.notify_interval
= atoi(argv
[++i
]);
776 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
780 strncpyt(serialnumber
, argv
[++i
], SERIALNUMBER_MAX_LEN
);
782 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
786 strncpyt(modelnumber
, argv
[++i
], MODELNUMBER_MAX_LEN
);
788 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
792 runtime_vars
.port
= atoi(argv
[++i
]);
794 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
799 if (argv
[++i
][0] != '/')
800 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c requires an absolute filename.\n", argv
[i
-1][1]);
802 pidfilename
= argv
[i
];
805 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
813 SETFLAG(NO_PLAYLIST_MASK
);
819 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
825 if (ifaces
>= MAX_LAN_ADDR
)
827 DPRINTF(E_ERROR
, L_GENERAL
, "Too many interfaces (max: %d), ignoring %s\n",
828 MAX_LAN_ADDR
, argv
[i
]);
831 runtime_vars
.ifaces
[ifaces
++] = argv
[i
];
834 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
837 i
++; /* discarding, the config file is already read */
840 runtime_vars
.port
= -1; // triggers help display
843 snprintf(buf
, sizeof(buf
), "rm -rf %s/files.db %s/art_cache", db_path
, db_path
);
844 if (system(buf
) != 0)
845 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to clean old file cache. EXITING\n");
851 uid
= strtoul(argv
[i
], &string
, 0);
854 /* Symbolic username given, not UID. */
855 struct passwd
*entry
= getpwnam(argv
[i
]);
857 DPRINTF(E_FATAL
, L_GENERAL
, "Bad user '%s'.\n", argv
[i
]);
862 DPRINTF(E_FATAL
, L_GENERAL
, "Option -%c takes one argument.\n", argv
[i
][1]);
867 SETFLAG(SYSTEMD_MASK
);
871 printf("Version " MINIDLNA_VERSION
"\n");
875 DPRINTF(E_ERROR
, L_GENERAL
, "Unknown option: %s\n", argv
[i
]);
876 runtime_vars
.port
= -1; // triggers help display
880 if (runtime_vars
.port
< 0)
883 "%s [-d] [-v] [-f config_file] [-p port]\n"
884 "\t\t[-i network_interface] [-u uid_to_run_as]\n"
885 "\t\t[-t notify_interval] [-P pid_filename]\n"
886 "\t\t[-s serial] [-m model_number]\n"
888 "\t\t[-w url] [-R] [-L] [-S] [-V] [-h]\n"
890 "\t\t[-w url] [-R] [-L] [-V] [-h]\n"
892 "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n"
893 "\tDefault pid file is %s.\n"
894 "\tWith -d minidlna will run in debug mode (not daemonize).\n"
895 "\t-w sets the presentation url. Default is http address on port 80\n"
896 "\t-v enables verbose output\n"
897 "\t-h displays this text\n"
898 "\t-R forces a full rescan\n"
899 "\t-L do not create playlists\n"
901 "\t-S changes behaviour for systemd\n"
903 "\t-V print the version number\n",
904 argv
[0], pidfilename
);
910 strcpy(log_str
+65, "debug");
916 /* Set the default log file path to NULL (stdout) */
921 strcpy(log_str
+65, "maxdebug");
924 else if (GETFLAG(SYSTEMD_MASK
))
930 pid
= process_daemonize();
932 unlink("/ramfs/.upnp-av_scan");
933 path
= "/var/log/upnp-av.log";
935 if (access(db_path
, F_OK
) != 0)
936 make_dir(db_path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
937 snprintf(buf
, sizeof(buf
), "%s/minidlna.log", log_path
);
938 unlink("/var/notice/dlna");
942 log_init(path
, log_level
);
944 if (process_check_if_running(pidfilename
) < 0)
946 DPRINTF(E_ERROR
, L_GENERAL
, SERVER_NAME
" is already running. EXITING.\n");
952 /* presentation url */
954 strncpyt(presentationurl
, presurl
, PRESENTATIONURL_MAX_LEN
);
956 strcpy(presentationurl
, "/");
958 /* set signal handlers */
959 memset(&sa
, 0, sizeof(struct sigaction
));
960 sa
.sa_handler
= sigterm
;
961 if (sigaction(SIGTERM
, &sa
, NULL
))
962 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set %s handler. EXITING.\n", "SIGTERM");
963 if (sigaction(SIGINT
, &sa
, NULL
))
964 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set %s handler. EXITING.\n", "SIGINT");
965 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
)
966 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set %s handler. EXITING.\n", "SIGPIPE");
967 if (signal(SIGHUP
, &sighup
) == SIG_ERR
)
968 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set %s handler. EXITING.\n", "SIGHUP");
969 signal(SIGUSR1
, &sigusr1
);
970 sa
.sa_handler
= process_handle_child_termination
;
971 if (sigaction(SIGCHLD
, &sa
, NULL
))
972 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set %s handler. EXITING.\n", "SIGCHLD");
974 if (writepidfile(pidfilename
, pid
, uid
) != 0)
980 if (stat(db_path
, &st
) == 0 && st
.st_uid
!= uid
&& chown(db_path
, uid
, -1) != 0)
981 DPRINTF(E_ERROR
, L_GENERAL
, "Unable to set db_path [%s] ownership to %d: %s\n",
982 db_path
, uid
, strerror(errno
));
985 if (uid
> 0 && setuid(uid
) == -1)
986 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to switch to uid '%d'. [%s] EXITING.\n",
987 uid
, strerror(errno
));
989 children
= calloc(runtime_vars
.max_connections
, sizeof(struct child
));
992 DPRINTF(E_ERROR
, L_GENERAL
, "Allocation failed\n");
1000 /* process HTTP or SSDP requests */
1002 main(int argc
, char **argv
)
1007 LIST_HEAD(httplisthead
, upnphttp
) upnphttphead
;
1008 struct upnphttp
* e
= 0;
1009 struct upnphttp
* next
;
1010 fd_set readset
; /* for select() */
1012 struct timeval timeout
, timeofday
, lastnotifytime
= {0, 0};
1013 time_t lastupdatetime
= 0;
1015 int last_changecnt
= 0;
1016 pid_t scanner_pid
= 0;
1017 pthread_t inotify_thread
= 0;
1019 uint8_t beacon_interval
= 5;
1021 struct sockaddr_in tivo_bcast
;
1022 struct timeval lastbeacontime
= {0, 0};
1025 for (i
= 0; i
< L_MAX
; i
++)
1026 log_level
[i
] = E_WARN
;
1029 ret
= init(argc
, argv
);
1033 DPRINTF(E_WARN
, L_GENERAL
, "Starting " SERVER_NAME
" version " MINIDLNA_VERSION
".\n");
1034 if (sqlite3_libversion_number() < 3005001)
1036 DPRINTF(E_WARN
, L_GENERAL
, "SQLite library is old. Please use version 3.5.1 or newer.\n");
1039 LIST_INIT(&upnphttphead
);
1041 ret
= open_db(NULL
);
1044 updateID
= sql_get_int_field(db
, "SELECT VALUE from SETTINGS where KEY = 'UPDATE_ID'");
1048 check_db(db
, ret
, &scanner_pid
);
1050 if( GETFLAG(INOTIFY_MASK
) )
1052 if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 3005001)
1053 DPRINTF(E_ERROR
, L_GENERAL
, "SQLite library is not threadsafe! "
1054 "Inotify will be disabled.\n");
1055 else if (pthread_create(&inotify_thread
, NULL
, start_inotify
, NULL
) != 0)
1056 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: pthread_create() failed for start_inotify. EXITING\n");
1059 smonitor
= OpenAndConfMonitorSocket();
1061 sssdp
= OpenAndConfSSDPReceiveSocket();
1064 DPRINTF(E_INFO
, L_GENERAL
, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n");
1065 reload_ifaces(0); /* populate lan_addr[0].str */
1066 if (SubmitServicesToMiniSSDPD(lan_addr
[0].str
, runtime_vars
.port
) < 0)
1067 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to connect to MiniSSDPd. EXITING");
1069 /* open socket for HTTP connections. */
1070 shttpl
= OpenAndConfHTTPSocket((runtime_vars
.port
> 0) ? runtime_vars
.port
: 0);
1072 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open socket for HTTP. EXITING\n");
1074 if(runtime_vars
.port
<= 0)
1076 struct sockaddr_in sockinfo
;
1077 socklen_t len
= sizeof(struct sockaddr_in
);
1078 if (getsockname(shttpl
, (struct sockaddr
*)&sockinfo
, &len
) < 0)
1080 DPRINTF(E_FATAL
, L_GENERAL
, "getsockname(): %s. EXITING\n", strerror(errno
));
1082 runtime_vars
.port
= ntohs(sockinfo
.sin_port
);
1085 DPRINTF(E_WARN
, L_GENERAL
, "HTTP listening on port %d\n", runtime_vars
.port
);
1088 if (GETFLAG(TIVO_MASK
))
1090 DPRINTF(E_WARN
, L_GENERAL
, "TiVo support is enabled.\n");
1091 /* Add TiVo-specific randomize function to sqlite */
1092 ret
= sqlite3_create_function(db
, "tivorandom", 1, SQLITE_UTF8
, NULL
, &TiVoRandomSeedFunc
, NULL
, NULL
);
1093 if (ret
!= SQLITE_OK
)
1094 DPRINTF(E_ERROR
, L_TIVO
, "ERROR: Failed to add sqlite randomize function for TiVo!\n");
1095 /* open socket for sending Tivo notifications */
1096 sbeacon
= OpenAndConfTivoBeaconSocket();
1098 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open sockets for sending Tivo beacon notify "
1099 "messages. EXITING\n");
1100 tivo_bcast
.sin_family
= AF_INET
;
1101 tivo_bcast
.sin_addr
.s_addr
= htonl(getBcastAddress());
1102 tivo_bcast
.sin_port
= htons(2190);
1107 lastnotifytime
.tv_sec
= time(NULL
) + runtime_vars
.notify_interval
;
1112 /* Check if we need to send SSDP NOTIFY messages and do it if
1114 if (gettimeofday(&timeofday
, 0) < 0)
1116 DPRINTF(E_ERROR
, L_GENERAL
, "gettimeofday(): %s\n", strerror(errno
));
1117 timeout
.tv_sec
= runtime_vars
.notify_interval
;
1118 timeout
.tv_usec
= 0;
1122 /* the comparison is not very precise but who cares ? */
1123 if (timeofday
.tv_sec
>= (lastnotifytime
.tv_sec
+ runtime_vars
.notify_interval
))
1125 DPRINTF(E_DEBUG
, L_SSDP
, "Sending SSDP notifies\n");
1126 for (i
= 0; i
< n_lan_addr
; i
++)
1128 SendSSDPNotifies(lan_addr
[i
].snotify
, lan_addr
[i
].str
,
1129 runtime_vars
.port
, runtime_vars
.notify_interval
);
1131 memcpy(&lastnotifytime
, &timeofday
, sizeof(struct timeval
));
1132 timeout
.tv_sec
= runtime_vars
.notify_interval
;
1133 timeout
.tv_usec
= 0;
1137 timeout
.tv_sec
= lastnotifytime
.tv_sec
+ runtime_vars
.notify_interval
1139 if (timeofday
.tv_usec
> lastnotifytime
.tv_usec
)
1141 timeout
.tv_usec
= 1000000 + lastnotifytime
.tv_usec
1142 - timeofday
.tv_usec
;
1146 timeout
.tv_usec
= lastnotifytime
.tv_usec
- timeofday
.tv_usec
;
1151 if (timeofday
.tv_sec
>= (lastbeacontime
.tv_sec
+ beacon_interval
))
1153 sendBeaconMessage(sbeacon
, &tivo_bcast
, sizeof(struct sockaddr_in
), 1);
1154 memcpy(&lastbeacontime
, &timeofday
, sizeof(struct timeval
));
1155 if (timeout
.tv_sec
> beacon_interval
)
1157 timeout
.tv_sec
= beacon_interval
;
1158 timeout
.tv_usec
= 0;
1160 /* Beacons should be sent every 5 seconds or so for the first minute,
1161 * then every minute or so thereafter. */
1162 if (beacon_interval
== 5 && (timeofday
.tv_sec
- startup_time
) > 60)
1163 beacon_interval
= 60;
1165 else if (timeout
.tv_sec
> (lastbeacontime
.tv_sec
+ beacon_interval
+ 1 - timeofday
.tv_sec
))
1166 timeout
.tv_sec
= lastbeacontime
.tv_sec
+ beacon_interval
- timeofday
.tv_sec
;
1173 if (!scanner_pid
|| kill(scanner_pid
, 0) != 0)
1180 /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
1185 FD_SET(sssdp
, &readset
);
1186 max_fd
= MAX(max_fd
, sssdp
);
1191 FD_SET(shttpl
, &readset
);
1192 max_fd
= MAX(max_fd
, shttpl
);
1197 FD_SET(sbeacon
, &readset
);
1198 max_fd
= MAX(max_fd
, sbeacon
);
1203 FD_SET(smonitor
, &readset
);
1204 max_fd
= MAX(max_fd
, smonitor
);
1207 i
= 0; /* active HTTP connections count */
1208 for (e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1210 if ((e
->socket
>= 0) && (e
->state
<= 2))
1212 FD_SET(e
->socket
, &readset
);
1213 max_fd
= MAX(max_fd
, e
->socket
);
1218 upnpevents_selectfds(&readset
, &writeset
, &max_fd
);
1220 ret
= select(max_fd
+1, &readset
, &writeset
, 0, &timeout
);
1223 if(quitting
) goto shutdown
;
1224 if(errno
== EINTR
) continue;
1225 DPRINTF(E_ERROR
, L_GENERAL
, "select(all): %s\n", strerror(errno
));
1226 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to select open sockets. EXITING\n");
1228 upnpevents_processfds(&readset
, &writeset
);
1229 /* process SSDP packets */
1230 if (sssdp
>= 0 && FD_ISSET(sssdp
, &readset
))
1232 /*DPRINTF(E_DEBUG, L_GENERAL, "Received SSDP Packet\n");*/
1233 ProcessSSDPRequest(sssdp
, (unsigned short)runtime_vars
.port
);
1236 if (sbeacon
>= 0 && FD_ISSET(sbeacon
, &readset
))
1238 /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
1239 ProcessTiVoBeacon(sbeacon
);
1242 if (smonitor
>= 0 && FD_ISSET(smonitor
, &readset
))
1244 ProcessMonitorEvent(smonitor
);
1246 /* increment SystemUpdateID if the content database has changed,
1247 * and if there is an active HTTP connection, at most once every 2 seconds */
1248 if (i
&& (timeofday
.tv_sec
>= (lastupdatetime
+ 2)))
1250 if (scanning
|| sqlite3_total_changes(db
) != last_changecnt
)
1253 last_changecnt
= sqlite3_total_changes(db
);
1254 upnp_event_var_change_notify(EContentDirectory
);
1255 lastupdatetime
= timeofday
.tv_sec
;
1258 /* process active HTTP connections */
1259 for (e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1261 if ((e
->socket
>= 0) && (e
->state
<= 2) && (FD_ISSET(e
->socket
, &readset
)))
1262 Process_upnphttp(e
);
1264 /* process incoming HTTP connections */
1265 if (shttpl
>= 0 && FD_ISSET(shttpl
, &readset
))
1268 socklen_t clientnamelen
;
1269 struct sockaddr_in clientname
;
1270 clientnamelen
= sizeof(struct sockaddr_in
);
1271 shttp
= accept(shttpl
, (struct sockaddr
*)&clientname
, &clientnamelen
);
1274 DPRINTF(E_ERROR
, L_GENERAL
, "accept(http): %s\n", strerror(errno
));
1278 struct upnphttp
* tmp
= 0;
1279 DPRINTF(E_DEBUG
, L_GENERAL
, "HTTP connection from %s:%d\n",
1280 inet_ntoa(clientname
.sin_addr
),
1281 ntohs(clientname
.sin_port
) );
1282 /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
1283 DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n");
1285 /* Create a new upnphttp object and add it to
1286 * the active upnphttp object list */
1287 tmp
= New_upnphttp(shttp
);
1290 tmp
->clientaddr
= clientname
.sin_addr
;
1291 LIST_INSERT_HEAD(&upnphttphead
, tmp
, entries
);
1295 DPRINTF(E_ERROR
, L_GENERAL
, "New_upnphttp() failed\n");
1300 /* delete finished HTTP connections */
1301 for (e
= upnphttphead
.lh_first
; e
!= NULL
; e
= next
)
1303 next
= e
->entries
.le_next
;
1306 LIST_REMOVE(e
, entries
);
1313 /* kill the scanner */
1314 if (scanning
&& scanner_pid
)
1315 kill(scanner_pid
, SIGKILL
);
1317 /* kill other child processes */
1318 process_reap_children();
1321 /* close out open sockets */
1322 while (upnphttphead
.lh_first
!= NULL
)
1324 e
= upnphttphead
.lh_first
;
1325 LIST_REMOVE(e
, entries
);
1339 for (i
= 0; i
< n_lan_addr
; i
++)
1341 SendSSDPGoodbyes(lan_addr
[i
].snotify
);
1342 close(lan_addr
[i
].snotify
);
1346 pthread_join(inotify_thread
, NULL
);
1348 sql_exec(db
, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID
);
1351 upnpevents_removeSubscribers();
1353 if (pidfilename
&& unlink(pidfilename
) < 0)
1354 DPRINTF(E_ERROR
, L_GENERAL
, "Failed to remove pidfile %s: %s\n", pidfilename
, strerror(errno
));