3 * http://sourceforge.net/projects/minidlna/
5 * MiniDLNA media server
6 * Copyright (C) 2008-2009 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>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
63 #include <sys/param.h>
74 #include "upnpglobalvars.h"
77 #include "upnpdescgen.h"
78 #include "minidlnapath.h"
79 #include "getifaddr.h"
84 #include "minidlnatypes.h"
85 #include "daemonize.h"
86 #include "upnpevents.h"
91 #include "tivo_beacon.h"
92 #include "tivo_utils.h"
95 #if SQLITE_VERSION_NUMBER < 3005001
96 # warning "Your SQLite3 library appears to be too old! Please use 3.5.1 or newer."
97 # define sqlite3_threadsafe() 0
100 /* OpenAndConfHTTPSocket() :
101 * setup the socket used to handle incoming HTTP connections. */
103 OpenAndConfHTTPSocket(unsigned short port
)
107 struct sockaddr_in listenname
;
109 /* Initialize client type cache */
110 memset(&clients
, 0, sizeof(struct client_cache_s
));
112 if( (s
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0)
114 DPRINTF(E_ERROR
, L_GENERAL
, "socket(http): %s\n", strerror(errno
));
118 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
120 DPRINTF(E_WARN
, L_GENERAL
, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno
));
123 memset(&listenname
, 0, sizeof(struct sockaddr_in
));
124 listenname
.sin_family
= AF_INET
;
125 listenname
.sin_port
= htons(port
);
126 listenname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
128 if(bind(s
, (struct sockaddr
*)&listenname
, sizeof(struct sockaddr_in
)) < 0)
130 DPRINTF(E_ERROR
, L_GENERAL
, "bind(http): %s\n", strerror(errno
));
137 DPRINTF(E_ERROR
, L_GENERAL
, "listen(http): %s\n", strerror(errno
));
145 /* Handler for the SIGTERM signal (kill)
146 * SIGINT is also handled */
150 /*int save_errno = errno;*/
151 signal(sig
, SIG_IGN
); /* Ignore this signal while we are quitting */
153 DPRINTF(E_WARN
, L_GENERAL
, "received signal %d, good-bye\n", sig
);
156 /*errno = save_errno;*/
159 /* record the startup time, for returning uptime */
161 set_startup_time(void)
163 startup_time
= time(NULL
);
167 * parse address with mask
173 parselanaddr(struct lan_addr_s
* lan_addr
, const char * str
)
179 while(*p
&& *p
!= '/' && !isspace(*p
))
185 while(*p
&& !isspace(*p
))
190 DPRINTF(E_OFF
, L_GENERAL
, "Error parsing address/mask: %s\n", str
);
193 memcpy(lan_addr
->str
, str
, n
);
194 lan_addr
->str
[n
] = '\0';
195 if(!inet_aton(lan_addr
->str
, &lan_addr
->addr
))
197 DPRINTF(E_OFF
, L_GENERAL
, "Error parsing address/mask: %s\n", str
);
200 lan_addr
->mask
.s_addr
= htonl(nbits
? (0xffffffff << (32 - nbits
)) : 0);
205 getfriendlyname(char * buf
, int len
)
208 char * hn
= calloc(1, 256);
211 if( gethostname(hn
, 256) == 0 )
213 strncpy(buf
, hn
, len
-1);
215 dot
= strchr(buf
, '.');
221 strcpy(buf
, "Unknown");
226 off
+= snprintf(buf
+off
, len
-off
, ": ");
229 char ibuf
[64], *key
, *val
;
230 snprintf(buf
+off
, len
-off
, "ReadyNAS");
231 info
= fopen("/proc/sys/dev/boot/info", "r");
234 while( (val
= fgets(ibuf
, 64, info
)) != NULL
)
236 key
= strsep(&val
, ": \t");
238 if( strcmp(key
, "model") == 0 )
240 snprintf(buf
+off
, len
-off
, "%s", val
);
241 key
= strchr(val
, ' ');
244 strncpy(modelnumber
, key
+1, MODELNUMBER_MAX_LEN
);
245 modelnumber
[MODELNUMBER_MAX_LEN
-1] = '\0';
248 snprintf(modelname
, MODELNAME_MAX_LEN
,
249 "Windows Media Connect compatible (%s)", val
);
251 else if( strcmp(key
, "serial") == 0 )
253 strncpy(serialnumber
, val
, SERIALNUMBER_MAX_LEN
);
254 serialnumber
[SERIALNUMBER_MAX_LEN
-1] = '\0';
255 if( serialnumber
[0] == '\0' )
258 if( getsyshwaddr(mac_str
, sizeof(mac_str
)) == 0 )
259 strcpy(serialnumber
, mac_str
);
261 strcpy(serialnumber
, "0");
267 if( strcmp(modelnumber
, "NVX") == 0 )
268 memcpy(pnpx_hwid
+4, "01F2&DEV_0101", 17);
269 else if( strcmp(modelnumber
, "Pro") == 0 ||
270 strcmp(modelnumber
, "Pro 6") == 0 ||
271 strncmp(modelnumber
, "Ultra 6", 7) == 0 )
272 memcpy(pnpx_hwid
+4, "01F2&DEV_0102", 17);
273 else if( strcmp(modelnumber
, "Pro 2") == 0 ||
274 strncmp(modelnumber
, "Ultra 2", 7) == 0 )
275 memcpy(pnpx_hwid
+4, "01F2&DEV_0103", 17);
276 else if( strcmp(modelnumber
, "Pro 4") == 0 ||
277 strncmp(modelnumber
, "Ultra 4", 7) == 0 )
278 memcpy(pnpx_hwid
+4, "01F2&DEV_0104", 17);
279 else if( strcmp(modelnumber
+1, "100") == 0 )
280 memcpy(pnpx_hwid
+4, "01F2&DEV_0105", 17);
281 else if( strcmp(modelnumber
+1, "200") == 0 )
282 memcpy(pnpx_hwid
+4, "01F2&DEV_0106", 17);
285 logname
= getenv("LOGNAME");
286 #ifndef STATIC // Disable for static linking
289 struct passwd
* pwent
;
290 pwent
= getpwuid(getuid());
292 logname
= pwent
->pw_name
;
295 snprintf(buf
+off
, len
-off
, "%s", logname
?logname
:"Unknown");
305 snprintf(path
, sizeof(path
), "%s/files.db", db_path
);
306 if( access(path
, F_OK
) != 0 )
309 make_dir(db_path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
311 if( sqlite3_open(path
, &db
) != SQLITE_OK
)
313 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: Failed to open sqlite database! Exiting...\n");
315 sqlite3_busy_timeout(db
, 5000);
316 sql_exec(db
, "pragma page_size = 4096");
317 sql_exec(db
, "pragma journal_mode = OFF");
318 sql_exec(db
, "pragma synchronous = OFF;");
320 // this sets the sqlite database cache size
321 // original code had 8192 = 32MB - reduce it to 4MB
322 sql_exec(db
, "pragma default_cache_size = 1024;");
327 * 1) read configuration file
328 * 2) read command line arguments
330 * 4) check and write pid file
331 * 5) set startup time stamp
332 * 6) compute presentation URL
333 * 7) set signal handlers */
335 init(int argc
, char * * argv
)
340 int options_flag
= 0;
342 /*const char * logfilename = 0;*/
343 const char * presurl
= 0;
344 const char * optionsfile
= "/etc/minidlna.conf";
346 char * string
, * word
;
347 enum media_types type
;
349 char real_path
[PATH_MAX
];
350 char ext_ip_addr
[INET_ADDRSTRLEN
] = {'\0'};
352 /* first check if "-f" option is used */
353 for(i
=2; i
<argc
; i
++)
355 if(0 == strcmp(argv
[i
-1], "-f"))
357 optionsfile
= argv
[i
];
363 /* set up uuid based on mac address */
364 if( getsyshwaddr(mac_str
, sizeof(mac_str
)) < 0 )
366 DPRINTF(E_OFF
, L_GENERAL
, "No MAC address found. Falling back to generic UUID.\n");
367 strcpy(mac_str
, "554e4b4e4f57");
369 strcpy(uuidvalue
+5, "4d696e69-444c-164e-9d41-");
370 strncat(uuidvalue
, mac_str
, 12);
372 getfriendlyname(friendly_name
, FRIENDLYNAME_MAX_LEN
);
374 runtime_vars
.port
= -1;
375 runtime_vars
.notify_interval
= 895; /* seconds between SSDP announces */
377 /* read options file first since
378 * command line arguments have final say */
379 if(readoptionsfile(optionsfile
) < 0)
381 /* only error if file exists or using -f */
382 if(access(optionsfile
, F_OK
) == 0 || options_flag
)
383 fprintf(stderr
, "Error reading configuration file %s\n", optionsfile
);
387 for(i
=0; i
<num_options
; i
++)
389 switch(ary_options
[i
].id
)
392 if(getifaddr(ary_options
[i
].value
, ext_ip_addr
, INET_ADDRSTRLEN
) >= 0)
394 if( *ext_ip_addr
&& parselanaddr(&lan_addr
[n_lan_addr
], ext_ip_addr
) == 0 )
398 fprintf(stderr
, "Interface %s not found, ignoring.\n", ary_options
[i
].value
);
400 case UPNPLISTENING_IP
:
401 if(n_lan_addr
< MAX_LAN_ADDR
)
403 if(parselanaddr(&lan_addr
[n_lan_addr
],
404 ary_options
[i
].value
) == 0)
409 fprintf(stderr
, "Too many listening ips (max: %d), ignoring %s\n",
410 MAX_LAN_ADDR
, ary_options
[i
].value
);
414 runtime_vars
.port
= atoi(ary_options
[i
].value
);
416 case UPNPPRESENTATIONURL
:
417 presurl
= ary_options
[i
].value
;
419 case UPNPNOTIFY_INTERVAL
:
420 runtime_vars
.notify_interval
= atoi(ary_options
[i
].value
);
423 strncpy(serialnumber
, ary_options
[i
].value
, SERIALNUMBER_MAX_LEN
);
424 serialnumber
[SERIALNUMBER_MAX_LEN
-1] = '\0';
427 strncpy(modelname
, ary_options
[i
].value
, MODELNAME_MAX_LEN
);
428 modelname
[MODELNAME_MAX_LEN
-1] = '\0';
430 case UPNPMODEL_NUMBER
:
431 strncpy(modelnumber
, ary_options
[i
].value
, MODELNUMBER_MAX_LEN
);
432 modelnumber
[MODELNUMBER_MAX_LEN
-1] = '\0';
434 case UPNPFRIENDLYNAME
:
435 strncpy(friendly_name
, ary_options
[i
].value
, FRIENDLYNAME_MAX_LEN
);
436 friendly_name
[FRIENDLYNAME_MAX_LEN
-1] = '\0';
441 switch( ary_options
[i
].value
[0] )
445 if( ary_options
[i
].value
[0] == 'A' || ary_options
[i
].value
[0] == 'a' )
449 if( ary_options
[i
].value
[0] == 'V' || ary_options
[i
].value
[0] == 'v' )
453 if( ary_options
[i
].value
[0] == 'P' || ary_options
[i
].value
[0] == 'p' )
455 myval
= index(ary_options
[i
].value
, '/');
457 path
= realpath(myval
? myval
:ary_options
[i
].value
, real_path
);
459 path
= (myval
? myval
:ary_options
[i
].value
);
460 if( access(path
, F_OK
) != 0 )
462 fprintf(stderr
, "Media directory not accessible! [%s]\n",
466 struct media_dir_s
* this_dir
= calloc(1, sizeof(struct media_dir_s
));
467 this_dir
->path
= strdup(path
);
468 this_dir
->type
= type
;
471 media_dirs
= this_dir
;
475 struct media_dir_s
* all_dirs
= media_dirs
;
476 while( all_dirs
->next
)
477 all_dirs
= all_dirs
->next
;
478 all_dirs
->next
= this_dir
;
482 fprintf(stderr
, "Media directory entry not understood! [%s]\n",
483 ary_options
[i
].value
);
487 case UPNPALBUMART_NAMES
:
488 for( string
= ary_options
[i
].value
; (word
= strtok(string
, "/")); string
= NULL
) {
489 struct album_art_name_s
* this_name
= calloc(1, sizeof(struct album_art_name_s
));
490 int len
= strlen(word
);
491 if( word
[len
-1] == '*' )
494 this_name
->wildcard
= 1;
496 this_name
->name
= strdup(word
);
497 if( !album_art_names
)
499 album_art_names
= this_name
;
503 struct album_art_name_s
* all_names
= album_art_names
;
504 while( all_names
->next
)
505 all_names
= all_names
->next
;
506 all_names
->next
= this_name
;
511 path
= realpath(ary_options
[i
].value
, real_path
);
513 path
= (ary_options
[i
].value
);
514 make_dir(path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
515 if( access(path
, F_OK
) != 0 )
517 DPRINTF(E_FATAL
, L_GENERAL
, "Database path not accessible! [%s]\n", path
);
520 strncpy(db_path
, path
, PATH_MAX
);
523 path
= realpath(ary_options
[i
].value
, real_path
);
525 path
= (ary_options
[i
].value
);
526 make_dir(path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
527 if( access(path
, F_OK
) != 0 )
529 DPRINTF(E_FATAL
, L_GENERAL
, "Log path not accessible! [%s]\n", path
);
532 strncpy(log_path
, path
, PATH_MAX
);
535 if( (strcmp(ary_options
[i
].value
, "yes") != 0) && !atoi(ary_options
[i
].value
) )
536 CLEARFLAG(INOTIFY_MASK
);
539 if( (strcmp(ary_options
[i
].value
, "yes") == 0) || atoi(ary_options
[i
].value
) )
542 case ENABLE_DLNA_STRICT
:
543 if( (strcmp(ary_options
[i
].value
, "yes") == 0) || atoi(ary_options
[i
].value
) )
544 SETFLAG(DLNA_STRICT_MASK
);
547 fprintf(stderr
, "Unknown option in file %s\n",
552 if( log_path
[0] == '\0' )
554 if( db_path
[0] == '\0' )
555 strncpy(log_path
, DEFAULT_LOG_PATH
, PATH_MAX
);
557 strncpy(log_path
, db_path
, PATH_MAX
);
559 if( db_path
[0] == '\0' )
560 strncpy(db_path
, DEFAULT_DB_PATH
, PATH_MAX
);
562 /* command line arguments processing */
563 for(i
=1; i
<argc
; i
++)
567 fprintf(stderr
, "Unknown option: %s\n", argv
[i
]);
569 else if(strcmp(argv
[i
], "--help")==0)
571 runtime_vars
.port
= -1;
574 else switch(argv
[i
][1])
578 runtime_vars
.notify_interval
= atoi(argv
[++i
]);
580 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
584 strncpy(serialnumber
, argv
[++i
], SERIALNUMBER_MAX_LEN
);
586 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
587 serialnumber
[SERIALNUMBER_MAX_LEN
-1] = '\0';
591 strncpy(modelnumber
, argv
[++i
], MODELNUMBER_MAX_LEN
);
593 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
594 modelnumber
[MODELNUMBER_MAX_LEN
-1] = '\0';
597 logfilename = argv[++i];
601 runtime_vars
.port
= atoi(argv
[++i
]);
603 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
607 pidfilename
= argv
[++i
];
609 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
618 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
623 int address_already_there
= 0;
626 for(j
=0; j
<n_lan_addr
; j
++)
628 struct lan_addr_s tmpaddr
;
629 parselanaddr(&tmpaddr
, argv
[i
]);
630 if(0 == strcmp(lan_addr
[j
].str
, tmpaddr
.str
))
631 address_already_there
= 1;
633 if(address_already_there
)
635 if(n_lan_addr
< MAX_LAN_ADDR
)
637 if(parselanaddr(&lan_addr
[n_lan_addr
], argv
[i
]) == 0)
642 fprintf(stderr
, "Too many listening ips (max: %d), ignoring %s\n",
643 MAX_LAN_ADDR
, argv
[i
]);
647 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
652 int address_already_there
= 0;
655 if( getifaddr(argv
[i
], ext_ip_addr
, INET_ADDRSTRLEN
) < 0 )
657 fprintf(stderr
, "Network interface '%s' not found.\n",
661 for(j
=0; j
<n_lan_addr
; j
++)
663 struct lan_addr_s tmpaddr
;
664 parselanaddr(&tmpaddr
, ext_ip_addr
);
665 if(0 == strcmp(lan_addr
[j
].str
, tmpaddr
.str
))
666 address_already_there
= 1;
668 if(address_already_there
)
670 if(n_lan_addr
< MAX_LAN_ADDR
)
672 if(parselanaddr(&lan_addr
[n_lan_addr
], ext_ip_addr
) == 0)
677 fprintf(stderr
, "Too many listening ips (max: %d), ignoring %s\n",
678 MAX_LAN_ADDR
, argv
[i
]);
682 fprintf(stderr
, "Option -%c takes one argument.\n", argv
[i
][1]);
685 i
++; /* discarding, the config file is already read */
688 runtime_vars
.port
= -1; // triggers help display
691 snprintf(real_path
, sizeof(real_path
), "rm -rf %s/files.db %s/art_cache", db_path
, db_path
);
695 printf("Version " MINIDLNA_VERSION
"\n");
699 fprintf(stderr
, "Unknown option: %s\n", argv
[i
]);
702 /* If no IP was specified, try to detect one */
705 if( (getsysaddr(ext_ip_addr
, INET_ADDRSTRLEN
) < 0) &&
706 (getifaddr("eth0", ext_ip_addr
, INET_ADDRSTRLEN
) < 0) &&
707 (getifaddr("eth1", ext_ip_addr
, INET_ADDRSTRLEN
) < 0) )
709 DPRINTF(E_OFF
, L_GENERAL
, "No IP address automatically detected!\n");
711 if( *ext_ip_addr
&& parselanaddr(&lan_addr
[n_lan_addr
], ext_ip_addr
) == 0 )
717 if( (n_lan_addr
==0) || (runtime_vars
.port
<0) )
719 fprintf(stderr
, "Usage:\n\t"
720 "%s [-d] [-f config_file]\n"
721 "\t\t[-a listening_ip] [-p port]\n"
722 /*"[-l logfile] " not functionnal */
723 "\t\t[-s serial] [-m model_number] \n"
724 "\t\t[-t notify_interval] [-P pid_filename]\n"
725 "\t\t[-w url] [-R] [-V] [-h]\n"
726 "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n"
727 "\tDefault pid file is %s.\n"
728 "\tWith -d minidlna will run in debug mode (not daemonize).\n"
729 "\t-w sets the presentation url. Default is http address on port 80\n"
730 "\t-h displays this text\n"
731 "\t-R forces a full rescan\n"
732 "\t-V print the version number\n",
733 argv
[0], pidfilename
);
740 log_init(NULL
, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=debug");
753 log_init("/var/log/upnp-av.log", "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
755 if( access(db_path
, F_OK
) != 0 )
756 make_dir(db_path
, S_ISVTX
|S_IRWXU
|S_IRWXG
|S_IRWXO
);
757 sprintf(real_path
, "%s/minidlna.log", log_path
);
758 log_init(real_path
, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
762 if(checkforrunning(pidfilename
) < 0)
764 DPRINTF(E_ERROR
, L_GENERAL
, "MiniDLNA is already running. EXITING.\n");
770 /* presentation url */
773 strncpy(presentationurl
, presurl
, PRESENTATIONURL_MAX_LEN
);
774 presentationurl
[PRESENTATIONURL_MAX_LEN
-1] = '\0';
779 snprintf(presentationurl
, PRESENTATIONURL_MAX_LEN
,
780 "http://%s/admin/", lan_addr
[0].str
);
782 snprintf(presentationurl
, PRESENTATIONURL_MAX_LEN
,
783 "http://%s/", lan_addr
[0].str
);
787 /* set signal handler */
788 signal(SIGCLD
, SIG_IGN
);
789 memset(&sa
, 0, sizeof(struct sigaction
));
790 sa
.sa_handler
= sigterm
;
791 if (sigaction(SIGTERM
, &sa
, NULL
))
793 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set SIGTERM handler. EXITING.\n");
795 if (sigaction(SIGINT
, &sa
, NULL
))
797 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to set SIGINT handler. EXITING.\n");
800 if(signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
) {
801 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to ignore SIGPIPE signals. EXITING.\n");
804 writepidfile(pidfilename
, pid
);
810 /* process HTTP or SSDP requests */
812 main(int argc
, char * * argv
)
815 int sudp
= -1, shttpl
= -1;
816 int snotify
[MAX_LAN_ADDR
];
817 LIST_HEAD(httplisthead
, upnphttp
) upnphttphead
;
818 struct upnphttp
* e
= 0;
819 struct upnphttp
* next
;
820 fd_set readset
; /* for select() */
822 struct timeval timeout
, timeofday
, lastnotifytime
= {0, 0}, lastupdatetime
= {0, 0};
824 int last_changecnt
= 0;
825 short int new_db
= 0;
826 pid_t scanner_pid
= 0;
827 pthread_t inotify_thread
= 0;
828 struct media_dir_s
*media_path
, *last_path
;
829 struct album_art_name_s
*art_names
, *last_name
;
831 unsigned short int beacon_interval
= 5;
833 struct sockaddr_in tivo_bcast
;
834 struct timeval lastbeacontime
= {0, 0};
838 setlocale(LC_MESSAGES
, "");
839 setlocale(LC_CTYPE
, "en_US.utf8");
840 DPRINTF(E_DEBUG
, L_GENERAL
, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR")));
841 textdomain("minidlna");
844 if(init(argc
, argv
) != 0)
848 DPRINTF(E_WARN
, L_GENERAL
, "Starting " SERVER_NAME
" version " MINIDLNA_VERSION
".\n");
849 unlink("/ramfs/.upnp-av_scan");
851 DPRINTF(E_WARN
, L_GENERAL
, "Starting " SERVER_NAME
" version " MINIDLNA_VERSION
" [SQLite %s].\n", sqlite3_libversion());
852 unlink("/var/notice/dlna");
853 if( !sqlite3_threadsafe() )
855 DPRINTF(E_ERROR
, L_GENERAL
, "SQLite library is not threadsafe! "
856 "Scanning must be finished before file serving can begin, "
857 "and inotify will be disabled.\n");
859 if( sqlite3_libversion_number() < 3005001 )
861 DPRINTF(E_WARN
, L_GENERAL
, "SQLite library is old. Please use version 3.5.1 or newer.\n");
864 LIST_INIT(&upnphttphead
);
869 updateID
= sql_get_int_field(db
, "SELECT UPDATE_ID from SETTINGS");
871 if( sql_get_int_field(db
, "pragma user_version") != DB_VERSION
)
875 DPRINTF(E_WARN
, L_GENERAL
, "Creating new database...\n");
879 DPRINTF(E_WARN
, L_GENERAL
, "Database version mismatch; need to recreate...\n");
883 asprintf(&cmd
, "rm -rf %s/files.db %s/art_cache", db_path
, db_path
);
887 if( CreateDatabase() != 0 )
889 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: Failed to create sqlite database! Exiting...\n");
894 scanner_pid
= fork();
896 if( !scanner_pid
) // child (scanner) process
900 media_path
= media_dirs
;
901 art_names
= album_art_names
;
904 free(media_path
->path
);
905 last_path
= media_path
;
906 media_path
= media_path
->next
;
911 free(art_names
->name
);
912 last_name
= art_names
;
913 art_names
= art_names
->next
;
923 if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 &&
924 GETFLAG(INOTIFY_MASK
) && pthread_create(&inotify_thread
, NULL
, start_inotify
, NULL
) )
926 DPRINTF(E_FATAL
, L_GENERAL
, "ERROR: pthread_create() failed for start_inotify.\n");
929 sudp
= OpenAndConfSSDPReceiveSocket(n_lan_addr
, lan_addr
);
932 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open socket for receiving SSDP. EXITING\n");
934 /* open socket for HTTP connections. Listen on the 1st LAN address */
935 shttpl
= OpenAndConfHTTPSocket((runtime_vars
.port
> 0) ? runtime_vars
.port
: 0);
938 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open socket for HTTP. EXITING\n");
940 if(runtime_vars
.port
<= 0)
942 struct sockaddr_in sockinfo
;
943 socklen_t len
= sizeof(struct sockaddr_in
);
944 if (getsockname(shttpl
, (struct sockaddr
*)&sockinfo
, &len
) < 0)
946 DPRINTF(E_FATAL
, L_GENERAL
, "getsockname(): %s. EXITING\n", strerror(errno
));
948 runtime_vars
.port
= ntohs(sockinfo
.sin_port
);
950 DPRINTF(E_WARN
, L_GENERAL
, "HTTP listening on port %d\n", runtime_vars
.port
);
952 /* open socket for sending notifications */
953 if(OpenAndConfSSDPNotifySockets(snotify
) < 0)
955 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open sockets for sending SSDP notify "
956 "messages. EXITING\n");
960 if( GETFLAG(TIVO_MASK
) )
962 DPRINTF(E_WARN
, L_GENERAL
, "TiVo support is enabled.\n");
963 /* Add TiVo-specific randomize function to sqlite */
964 if( sqlite3_create_function(db
, "tivorandom", 1, SQLITE_UTF8
, NULL
, &TiVoRandomSeedFunc
, NULL
, NULL
) != SQLITE_OK
)
966 DPRINTF(E_ERROR
, L_TIVO
, "ERROR: Failed to add sqlite randomize function for TiVo!\n");
968 /* open socket for sending Tivo notifications */
969 sbeacon
= OpenAndConfTivoBeaconSocket();
972 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to open sockets for sending Tivo beacon notify "
973 "messages. EXITING\n");
975 tivo_bcast
.sin_family
= AF_INET
;
976 tivo_bcast
.sin_addr
.s_addr
= htonl(getBcastAddress());
977 tivo_bcast
.sin_port
= htons(2190);
985 SendSSDPGoodbye(snotify
, n_lan_addr
);
990 /* Check if we need to send SSDP NOTIFY messages and do it if
992 if(gettimeofday(&timeofday
, 0) < 0)
994 DPRINTF(E_ERROR
, L_GENERAL
, "gettimeofday(): %s\n", strerror(errno
));
995 timeout
.tv_sec
= runtime_vars
.notify_interval
;
1000 /* the comparaison is not very precise but who cares ? */
1001 if(timeofday
.tv_sec
>= (lastnotifytime
.tv_sec
+ runtime_vars
.notify_interval
))
1003 SendSSDPNotifies2(snotify
,
1004 (unsigned short)runtime_vars
.port
,
1005 (runtime_vars
.notify_interval
<< 1)+10);
1006 memcpy(&lastnotifytime
, &timeofday
, sizeof(struct timeval
));
1007 timeout
.tv_sec
= runtime_vars
.notify_interval
;
1008 timeout
.tv_usec
= 0;
1012 timeout
.tv_sec
= lastnotifytime
.tv_sec
+ runtime_vars
.notify_interval
1014 if(timeofday
.tv_usec
> lastnotifytime
.tv_usec
)
1016 timeout
.tv_usec
= 1000000 + lastnotifytime
.tv_usec
1017 - timeofday
.tv_usec
;
1022 timeout
.tv_usec
= lastnotifytime
.tv_usec
- timeofday
.tv_usec
;
1026 if( GETFLAG(TIVO_MASK
) )
1028 if(timeofday
.tv_sec
>= (lastbeacontime
.tv_sec
+ beacon_interval
))
1030 sendBeaconMessage(sbeacon
, &tivo_bcast
, sizeof(struct sockaddr_in
), 1);
1031 memcpy(&lastbeacontime
, &timeofday
, sizeof(struct timeval
));
1032 if( timeout
.tv_sec
> beacon_interval
)
1034 timeout
.tv_sec
= beacon_interval
;
1035 timeout
.tv_usec
= 0;
1037 /* Beacons should be sent every 5 seconds or so for the first minute,
1038 * then every minute or so thereafter. */
1039 if( beacon_interval
== 5 && (timeofday
.tv_sec
- startup_time
) > 60 )
1041 beacon_interval
= 60;
1044 else if( timeout
.tv_sec
> (lastbeacontime
.tv_sec
+ beacon_interval
+ 1 - timeofday
.tv_sec
) )
1046 timeout
.tv_sec
= lastbeacontime
.tv_sec
+ beacon_interval
- timeofday
.tv_sec
;
1054 if( !scanner_pid
|| kill(scanner_pid
, 0) )
1058 /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
1063 FD_SET(sudp
, &readset
);
1064 max_fd
= MAX( max_fd
, sudp
);
1069 FD_SET(shttpl
, &readset
);
1070 max_fd
= MAX( max_fd
, shttpl
);
1075 FD_SET(sbeacon
, &readset
);
1076 max_fd
= MAX(max_fd
, sbeacon
);
1079 i
= 0; /* active HTTP connections count */
1080 for(e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1082 if((e
->socket
>= 0) && (e
->state
<= 2))
1084 FD_SET(e
->socket
, &readset
);
1085 max_fd
= MAX( max_fd
, e
->socket
);
1093 DPRINTF(E_DEBUG
, L_GENERAL
, "%d active incoming HTTP connections\n", i
);
1097 upnpevents_selectfds(&readset
, &writeset
, &max_fd
);
1099 if(select(max_fd
+1, &readset
, &writeset
, 0, &timeout
) < 0)
1101 if(quitting
) goto shutdown
;
1102 DPRINTF(E_ERROR
, L_GENERAL
, "select(all): %s\n", strerror(errno
));
1103 DPRINTF(E_FATAL
, L_GENERAL
, "Failed to select open sockets. EXITING\n");
1105 upnpevents_processfds(&readset
, &writeset
);
1106 /* process SSDP packets */
1107 if(sudp
>= 0 && FD_ISSET(sudp
, &readset
))
1109 /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
1110 ProcessSSDPRequest(sudp
, (unsigned short)runtime_vars
.port
);
1113 if(sbeacon
>= 0 && FD_ISSET(sbeacon
, &readset
))
1115 /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
1116 ProcessTiVoBeacon(sbeacon
);
1119 /* increment SystemUpdateID if the content database has changed,
1120 * and if there is an active HTTP connection, at most once every 2 seconds */
1121 if( i
&& (time(NULL
) >= (lastupdatetime
.tv_sec
+ 2)) )
1123 if( sqlite3_total_changes(db
) != last_changecnt
)
1126 last_changecnt
= sqlite3_total_changes(db
);
1127 upnp_event_var_change_notify(EContentDirectory
);
1128 memcpy(&lastupdatetime
, &timeofday
, sizeof(struct timeval
));
1131 /* process active HTTP connections */
1132 for(e
= upnphttphead
.lh_first
; e
!= NULL
; e
= e
->entries
.le_next
)
1134 if( (e
->socket
>= 0) && (e
->state
<= 2)
1135 &&(FD_ISSET(e
->socket
, &readset
)) )
1137 Process_upnphttp(e
);
1140 /* process incoming HTTP connections */
1141 if(shttpl
>= 0 && FD_ISSET(shttpl
, &readset
))
1144 socklen_t clientnamelen
;
1145 struct sockaddr_in clientname
;
1146 clientnamelen
= sizeof(struct sockaddr_in
);
1147 shttp
= accept(shttpl
, (struct sockaddr
*)&clientname
, &clientnamelen
);
1150 DPRINTF(E_ERROR
, L_GENERAL
, "accept(http): %s\n", strerror(errno
));
1154 struct upnphttp
* tmp
= 0;
1155 DPRINTF(E_DEBUG
, L_GENERAL
, "HTTP connection from %s:%d\n",
1156 inet_ntoa(clientname
.sin_addr
),
1157 ntohs(clientname
.sin_port
) );
1158 /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
1159 DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK");
1161 /* Create a new upnphttp object and add it to
1162 * the active upnphttp object list */
1163 tmp
= New_upnphttp(shttp
);
1166 tmp
->clientaddr
= clientname
.sin_addr
;
1167 LIST_INSERT_HEAD(&upnphttphead
, tmp
, entries
);
1171 DPRINTF(E_ERROR
, L_GENERAL
, "New_upnphttp() failed\n");
1176 /* delete finished HTTP connections */
1177 for(e
= upnphttphead
.lh_first
; e
!= NULL
; )
1179 next
= e
->entries
.le_next
;
1182 LIST_REMOVE(e
, entries
);
1190 /* kill the scanner */
1191 if( scanning
&& scanner_pid
)
1193 kill(scanner_pid
, 9);
1195 /* close out open sockets */
1196 while(upnphttphead
.lh_first
!= NULL
)
1198 e
= upnphttphead
.lh_first
;
1199 LIST_REMOVE(e
, entries
);
1203 if (sudp
>= 0) close(sudp
);
1204 if (shttpl
>= 0) close(shttpl
);
1206 if (sbeacon
>= 0) close(sbeacon
);
1209 if(SendSSDPGoodbye(snotify
, n_lan_addr
) < 0)
1211 DPRINTF(E_ERROR
, L_GENERAL
, "Failed to broadcast good-bye notifications\n");
1213 for(i
=0; i
<n_lan_addr
; i
++)
1216 if( inotify_thread
)
1217 pthread_join(inotify_thread
, NULL
);
1219 sql_exec(db
, "UPDATE SETTINGS set UPDATE_ID = %u", updateID
);
1222 media_path
= media_dirs
;
1223 art_names
= album_art_names
;
1226 free(media_path
->path
);
1227 last_path
= media_path
;
1228 media_path
= media_path
->next
;
1233 free(art_names
->name
);
1234 last_name
= art_names
;
1235 art_names
= art_names
->next
;
1239 if(unlink(pidfilename
) < 0)
1241 DPRINTF(E_ERROR
, L_GENERAL
, "Failed to remove pidfile %s: %s\n", pidfilename
, strerror(errno
));