source: titan/minidlna-1.0.22/minidlna.c @ 15746

Last change on this file since 15746 was 13567, checked in by obi, 12 years ago

[titan] add minidlna-1.0.22 first step

File size: 33.7 KB
Line 
1/* MiniDLNA project
2 *
3 * http://sourceforge.net/projects/minidlna/
4 *
5 * MiniDLNA media server
6 * Copyright (C) 2008-2009  Justin Maggard
7 *
8 * This file is part of MiniDLNA.
9 *
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.
13 *
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.
18 *
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/>.
21 *
22 * Portions of the code from the MiniUPnP project:
23 *
24 * Copyright (c) 2006-2007, Thomas Bernard
25 * All rights reserved.
26 *
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.
36 *
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.
48 */
49#include <stdlib.h>
50#include <unistd.h>
51#include <string.h>
52#include <stdio.h>
53#include <ctype.h>
54#include <sys/types.h>
55#include <sys/socket.h>
56#include <netinet/in.h>
57#include <arpa/inet.h>
58#include <fcntl.h>
59#include <sys/file.h>
60#include <sys/time.h>
61#include <time.h>
62#include <signal.h>
63#include <sys/param.h>
64#include <errno.h>
65#include <pthread.h>
66#include <pwd.h>
67
68#include "config.h"
69
70#ifdef ENABLE_NLS
71#include <libintl.h>
72#endif
73
74#include "upnpglobalvars.h"
75#include "sql.h"
76#include "upnphttp.h"
77#include "upnpdescgen.h"
78#include "minidlnapath.h"
79#include "getifaddr.h"
80#include "upnpsoap.h"
81#include "options.h"
82#include "utils.h"
83#include "minissdp.h"
84#include "minidlnatypes.h"
85#include "daemonize.h"
86#include "upnpevents.h"
87#include "scanner.h"
88#include "inotify.h"
89#include "log.h"
90#ifdef TIVO_SUPPORT
91#include "tivo_beacon.h"
92#include "tivo_utils.h"
93#endif
94
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
98#endif
99 
100/* OpenAndConfHTTPSocket() :
101 * setup the socket used to handle incoming HTTP connections. */
102static int
103OpenAndConfHTTPSocket(unsigned short port)
104{
105        int s;
106        int i = 1;
107        struct sockaddr_in listenname;
108
109        /* Initialize client type cache */
110        memset(&clients, 0, sizeof(struct client_cache_s));
111
112        if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
113        {
114                DPRINTF(E_ERROR, L_GENERAL, "socket(http): %s\n", strerror(errno));
115                return -1;
116        }
117
118        if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
119        {
120                DPRINTF(E_WARN, L_GENERAL, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno));
121        }
122
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);
127
128        if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0)
129        {
130                DPRINTF(E_ERROR, L_GENERAL, "bind(http): %s\n", strerror(errno));
131                close(s);
132                return -1;
133        }
134
135        if(listen(s, 6) < 0)
136        {
137                DPRINTF(E_ERROR, L_GENERAL, "listen(http): %s\n", strerror(errno));
138                close(s);
139                return -1;
140        }
141
142        return s;
143}
144
145/* Handler for the SIGTERM signal (kill)
146 * SIGINT is also handled */
147static void
148sigterm(int sig)
149{
150        /*int save_errno = errno;*/
151        signal(sig, SIG_IGN);   /* Ignore this signal while we are quitting */
152
153        DPRINTF(E_WARN, L_GENERAL, "received signal %d, good-bye\n", sig);
154
155        quitting = 1;
156        /*errno = save_errno;*/
157}
158
159/* record the startup time, for returning uptime */
160static void
161set_startup_time(void)
162{
163        startup_time = time(NULL);
164}
165
166/* parselanaddr()
167 * parse address with mask
168 * ex: 192.168.1.1/24
169 * return value :
170 *    0 : ok
171 *   -1 : error */
172static int
173parselanaddr(struct lan_addr_s * lan_addr, const char * str)
174{
175        const char * p;
176        int nbits = 24;
177        int n;
178        p = str;
179        while(*p && *p != '/' && !isspace(*p))
180                p++;
181        n = p - str;
182        if(*p == '/')
183        {
184                nbits = atoi(++p);
185                while(*p && !isspace(*p))
186                        p++;
187        }
188        if(n>15)
189        {
190                DPRINTF(E_OFF, L_GENERAL, "Error parsing address/mask: %s\n", str);
191                return -1;
192        }
193        memcpy(lan_addr->str, str, n);
194        lan_addr->str[n] = '\0';
195        if(!inet_aton(lan_addr->str, &lan_addr->addr))
196        {
197                DPRINTF(E_OFF, L_GENERAL, "Error parsing address: %s\n", str);
198                return -1;
199        }
200        lan_addr->mask.s_addr = htonl(nbits ? (0xffffffff << (32 - nbits)) : 0);
201        return 0;
202}
203
204static void
205getfriendlyname(char * buf, int len)
206{
207        char * dot = NULL;
208        char * hn = calloc(1, 256);
209        int off;
210
211        if( gethostname(hn, 256) == 0 )
212        {
213                strncpy(buf, hn, len-1);
214                buf[len] = '\0';
215                dot = strchr(buf, '.');
216                if( dot )
217                        *dot = '\0';
218        }
219        else
220        {
221                strcpy(buf, "Unknown");
222        }
223        free(hn);
224
225        off = strlen(buf);
226        off += snprintf(buf+off, len-off, ": ");
227#ifdef READYNAS
228        FILE * info;
229        char ibuf[64], *key, *val;
230        snprintf(buf+off, len-off, "ReadyNAS");
231        info = fopen("/proc/sys/dev/boot/info", "r");
232        if( !info )
233                return;
234        while( (val = fgets(ibuf, 64, info)) != NULL )
235        {
236                key = strsep(&val, ": \t");
237                val = trim(val);
238                if( strcmp(key, "model") == 0 )
239                {
240                        snprintf(buf+off, len-off, "%s", val);
241                        key = strchr(val, ' ');
242                        if( key )
243                        {
244                                strncpy(modelnumber, key+1, MODELNUMBER_MAX_LEN);
245                                modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
246                                *key = '\0';
247                        }
248                        snprintf(modelname, MODELNAME_MAX_LEN,
249                                "Windows Media Connect compatible (%s)", val);
250                }
251                else if( strcmp(key, "serial") == 0 )
252                {
253                        strncpy(serialnumber, val, SERIALNUMBER_MAX_LEN);
254                        serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
255                        if( serialnumber[0] == '\0' )
256                        {
257                                char mac_str[13];
258                                if( getsyshwaddr(mac_str, sizeof(mac_str)) == 0 )
259                                        strcpy(serialnumber, mac_str);
260                                else
261                                        strcpy(serialnumber, "0");
262                        }
263                        break;
264                }
265        }
266        fclose(info);
267        memcpy(pnpx_hwid+4, "01F2", 4);
268        if( strcmp(modelnumber, "NVX") == 0 )
269                memcpy(pnpx_hwid+17, "0101", 4);
270        else if( strcmp(modelnumber, "Pro") == 0 ||
271                 strcmp(modelnumber, "Pro 6") == 0 ||
272                 strncmp(modelnumber, "Ultra 6", 7) == 0 )
273                memcpy(pnpx_hwid+17, "0102", 4);
274        else if( strcmp(modelnumber, "Pro 2") == 0 ||
275                 strncmp(modelnumber, "Ultra 2", 7) == 0 )
276                memcpy(pnpx_hwid+17, "0103", 4);
277        else if( strcmp(modelnumber, "Pro 4") == 0 ||
278                 strncmp(modelnumber, "Ultra 4", 7) == 0 )
279                memcpy(pnpx_hwid+17, "0104", 4);
280        else if( strcmp(modelnumber+1, "100") == 0 )
281                memcpy(pnpx_hwid+17, "0105", 4);
282        else if( strcmp(modelnumber+1, "200") == 0 )
283                memcpy(pnpx_hwid+17, "0106", 4);
284        /* 0107 = Stora */
285        else if( strcmp(modelnumber, "Duo v2") == 0 )
286                memcpy(pnpx_hwid+17, "0108", 4);
287        else if( strcmp(modelnumber, "NV+ v2") == 0 )
288                memcpy(pnpx_hwid+17, "0109", 4);
289#else
290        char * logname;
291        logname = getenv("LOGNAME");
292#ifndef STATIC // Disable for static linking
293        if( !logname )
294        {
295                struct passwd * pwent;
296                pwent = getpwuid(getuid());
297                if( pwent )
298                        logname = pwent->pw_name;
299        }
300#endif
301        snprintf(buf+off, len-off, "%s", logname?logname:"Unknown");
302#endif
303}
304
305static int
306open_db(void)
307{
308        char path[PATH_MAX];
309        int new_db = 0;
310
311        snprintf(path, sizeof(path), "%s/files.db", db_path);
312        if( access(path, F_OK) != 0 )
313        {
314                new_db = 1;
315                make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
316        }
317        if( sqlite3_open(path, &db) != SQLITE_OK )
318        {
319                DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to open sqlite database!  Exiting...\n");
320        }
321        sqlite3_busy_timeout(db, 5000);
322        sql_exec(db, "pragma page_size = 4096");
323        sql_exec(db, "pragma journal_mode = OFF");
324        sql_exec(db, "pragma synchronous = OFF;");
325        sql_exec(db, "pragma default_cache_size = 8192;");
326        return new_db;
327}
328
329/* init phase :
330 * 1) read configuration file
331 * 2) read command line arguments
332 * 3) daemonize
333 * 4) check and write pid file
334 * 5) set startup time stamp
335 * 6) compute presentation URL
336 * 7) set signal handlers */
337static int
338init(int argc, char * * argv)
339{
340        int i;
341        int pid;
342        int debug_flag = 0;
343        int options_flag = 0;
344        struct sigaction sa;
345        /*const char * logfilename = 0;*/
346        const char * presurl = 0;
347        const char * optionsfile = "/etc/minidlna.conf";
348        char mac_str[13];
349        char * string, * word;
350        enum media_types type;
351        char * path;
352        char real_path[PATH_MAX];
353        char ip_addr[INET_ADDRSTRLEN + 3] = {'\0'};
354
355        /* first check if "-f" option is used */
356        for(i=2; i<argc; i++)
357        {
358                if(0 == strcmp(argv[i-1], "-f"))
359                {
360                        optionsfile = argv[i];
361                        options_flag = 1;
362                        break;
363                }
364        }
365
366        /* set up uuid based on mac address */
367        if( getsyshwaddr(mac_str, sizeof(mac_str)) < 0 )
368        {
369                DPRINTF(E_OFF, L_GENERAL, "No MAC address found.  Falling back to generic UUID.\n");
370                strcpy(mac_str, "554e4b4e4f57");
371        }
372        strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-");
373        strncat(uuidvalue, mac_str, 12);
374
375        getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
376       
377        runtime_vars.port = -1;
378        runtime_vars.notify_interval = 895;     /* seconds between SSDP announces */
379        runtime_vars.root_container = NULL;
380
381        /* read options file first since
382         * command line arguments have final say */
383        if(readoptionsfile(optionsfile) < 0)
384        {
385                /* only error if file exists or using -f */
386                if(access(optionsfile, F_OK) == 0 || options_flag)
387                        fprintf(stderr, "Error reading configuration file %s\n", optionsfile);
388        }
389        else
390        {
391                for(i=0; i<num_options; i++)
392                {
393                        switch(ary_options[i].id)
394                        {
395                        case UPNPIFNAME:
396                                for( string = ary_options[i].value; (word = strtok(string, ",")); string = NULL )
397                                {
398                                        if(n_lan_addr < MAX_LAN_ADDR)
399                                        {
400                                                if(getifaddr(word, ip_addr, sizeof(ip_addr)) >= 0)
401                                                {
402                                                        if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 )
403                                                                if(n_lan_addr < MAX_LAN_ADDR)
404                                                                        n_lan_addr++;
405                                                }
406                                                else
407                                                        fprintf(stderr, "Interface %s not found, ignoring.\n", word);
408                                        }
409                                        else
410                                        {
411                                                fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
412                                                    MAX_LAN_ADDR, word);
413                                        }
414                                }
415                                break;
416                        case UPNPLISTENING_IP:
417                                if(n_lan_addr < MAX_LAN_ADDR)
418                                {
419                                        if(parselanaddr(&lan_addr[n_lan_addr],
420                                                     ary_options[i].value) == 0)
421                                                n_lan_addr++;
422                                }
423                                else
424                                {
425                                        fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
426                                            MAX_LAN_ADDR, ary_options[i].value);
427                                }
428                                break;
429                        case UPNPPORT:
430                                runtime_vars.port = atoi(ary_options[i].value);
431                                break;
432                        case UPNPPRESENTATIONURL:
433                                presurl = ary_options[i].value;
434                                break;
435                        case UPNPNOTIFY_INTERVAL:
436                                runtime_vars.notify_interval = atoi(ary_options[i].value);
437                                break;
438                        case UPNPSERIAL:
439                                strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN);
440                                serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
441                                break;                         
442                        case UPNPMODEL_NAME:
443                                strncpy(modelname, ary_options[i].value, MODELNAME_MAX_LEN);
444                                modelname[MODELNAME_MAX_LEN-1] = '\0';
445                                break;
446                        case UPNPMODEL_NUMBER:
447                                strncpy(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN);
448                                modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
449                                break;
450                        case UPNPFRIENDLYNAME:
451                                strncpy(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN);
452                                friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
453                                break;
454                        case UPNPMEDIADIR:
455                                type = ALL_MEDIA;
456                                char * myval = NULL;
457                                switch( ary_options[i].value[0] )
458                                {
459                                case 'A':
460                                case 'a':
461                                        if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' )
462                                                type = AUDIO_ONLY;
463                                case 'V':
464                                case 'v':
465                                        if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' )
466                                                type = VIDEO_ONLY;
467                                case 'P':
468                                case 'p':
469                                        if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' )
470                                                type = IMAGES_ONLY;
471                                        myval = index(ary_options[i].value, '/');
472                                case '/':
473                                        path = realpath(myval ? myval:ary_options[i].value, real_path);
474                                        if( !path )
475                                                path = (myval ? myval:ary_options[i].value);
476                                        if( access(path, F_OK) != 0 )
477                                        {
478                                                fprintf(stderr, "Media directory not accessible! [%s]\n",
479                                                        path);
480                                                break;
481                                        }
482                                        struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s));
483                                        this_dir->path = strdup(path);
484                                        this_dir->type = type;
485                                        if( !media_dirs )
486                                        {
487                                                media_dirs = this_dir;
488                                        }
489                                        else
490                                        {
491                                                struct media_dir_s * all_dirs = media_dirs;
492                                                while( all_dirs->next )
493                                                        all_dirs = all_dirs->next;
494                                                all_dirs->next = this_dir;
495                                        }
496                                        break;
497                                default:
498                                        fprintf(stderr, "Media directory entry not understood! [%s]\n",
499                                                ary_options[i].value);
500                                        break;
501                                }
502                                break;
503                        case UPNPALBUMART_NAMES:
504                                for( string = ary_options[i].value; (word = strtok(string, "/")); string = NULL )
505                                {
506                                        struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s));
507                                        int len = strlen(word);
508                                        if( word[len-1] == '*' )
509                                        {
510                                                word[len-1] = '\0';
511                                                this_name->wildcard = 1;
512                                        }
513                                        this_name->name = strdup(word);
514                                        if( !album_art_names )
515                                        {
516                                                album_art_names = this_name;
517                                        }
518                                        else
519                                        {
520                                                struct album_art_name_s * all_names = album_art_names;
521                                                while( all_names->next )
522                                                        all_names = all_names->next;
523                                                all_names->next = this_name;
524                                        }
525                                }
526                                break;
527                        case UPNPDBDIR:
528                                path = realpath(ary_options[i].value, real_path);
529                                if( !path )
530                                        path = (ary_options[i].value);
531                                make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
532                                if( access(path, F_OK) != 0 )
533                                {
534                                        DPRINTF(E_FATAL, L_GENERAL, "Database path not accessible! [%s]\n", path);
535                                        break;
536                                }
537                                strncpy(db_path, path, PATH_MAX);
538                                break;
539                        case UPNPLOGDIR:
540                                path = realpath(ary_options[i].value, real_path);
541                                if( !path )
542                                        path = (ary_options[i].value);
543                                make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
544                                if( access(path, F_OK) != 0 )
545                                {
546                                        DPRINTF(E_FATAL, L_GENERAL, "Log path not accessible! [%s]\n", path);
547                                        break;
548                                }
549                                strncpy(log_path, path, PATH_MAX);
550                                break;
551                        case UPNPINOTIFY:
552                                if( (strcmp(ary_options[i].value, "yes") != 0) && !atoi(ary_options[i].value) )
553                                        CLEARFLAG(INOTIFY_MASK);
554                                break;
555                        case ENABLE_TIVO:
556                                if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) )
557                                        SETFLAG(TIVO_MASK);
558                                break;
559                        case ENABLE_DLNA_STRICT:
560                                if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) )
561                                        SETFLAG(DLNA_STRICT_MASK);
562                                break;
563                        case ROOT_CONTAINER:
564                                switch( ary_options[i].value[0] )
565                                {
566                                case '.':
567                                        runtime_vars.root_container = NULL;
568                                        break;
569                                case 'B':
570                                case 'b':
571                                        runtime_vars.root_container = BROWSEDIR_ID;
572                                        break;
573                                case 'M':
574                                case 'm':
575                                        runtime_vars.root_container = MUSIC_ID;
576                                        break;
577                                case 'V':
578                                case 'v':
579                                        runtime_vars.root_container = VIDEO_ID;
580                                        break;
581                                case 'P':
582                                case 'p':
583                                        runtime_vars.root_container = IMAGE_ID;
584                                        break;
585                                default:
586                                        fprintf(stderr, "Invalid root container! [%s]\n",
587                                                ary_options[i].value);
588                                        break;
589                                }
590                                break;
591                        case UPNPMINISSDPDSOCKET:
592                                minissdpdsocketpath = ary_options[i].value;
593                                break;
594                        default:
595                                fprintf(stderr, "Unknown option in file %s\n",
596                                        optionsfile);
597                        }
598                }
599        }
600        if( log_path[0] == '\0' )
601        {
602                if( db_path[0] == '\0' )
603                        strncpy(log_path, DEFAULT_LOG_PATH, PATH_MAX);
604                else
605                        strncpy(log_path, db_path, PATH_MAX);
606        }
607        if( db_path[0] == '\0' )
608                strncpy(db_path, DEFAULT_DB_PATH, PATH_MAX);
609
610        /* command line arguments processing */
611        for(i=1; i<argc; i++)
612        {
613                if(argv[i][0]!='-')
614                {
615                        fprintf(stderr, "Unknown option: %s\n", argv[i]);
616                }
617                else if(strcmp(argv[i], "--help")==0)
618                {
619                        runtime_vars.port = 0;
620                        break;
621                }
622                else switch(argv[i][1])
623                {
624                case 't':
625                        if(i+1 < argc)
626                                runtime_vars.notify_interval = atoi(argv[++i]);
627                        else
628                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
629                        break;
630                case 's':
631                        if(i+1 < argc)
632                                strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN);
633                        else
634                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
635                        serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
636                        break;
637                case 'm':
638                        if(i+1 < argc)
639                                strncpy(modelnumber, argv[++i], MODELNUMBER_MAX_LEN);
640                        else
641                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
642                        modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
643                        break;
644                /*case 'l':
645                        logfilename = argv[++i];
646                        break;*/
647                case 'p':
648                        if(i+1 < argc)
649                                runtime_vars.port = atoi(argv[++i]);
650                        else
651                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
652                        break;
653                case 'P':
654                        if(i+1 < argc)
655                                pidfilename = argv[++i];
656                        else
657                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
658                        break;
659                case 'd':
660                        debug_flag = 1;
661                        break;
662                case 'w':
663                        if(i+1 < argc)
664                                presurl = argv[++i];
665                        else
666                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
667                        break;
668                case 'a':
669                        if(i+1 < argc)
670                        {
671                                int address_already_there = 0;
672                                int j;
673                                i++;
674                                for(j=0; j<n_lan_addr; j++)
675                                {
676                                        struct lan_addr_s tmpaddr;
677                                        parselanaddr(&tmpaddr, argv[i]);
678                                        if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
679                                                address_already_there = 1;
680                                }
681                                if(address_already_there)
682                                        break;
683                                if(n_lan_addr < MAX_LAN_ADDR)
684                                {
685                                        if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0)
686                                                n_lan_addr++;
687                                }
688                                else
689                                {
690                                        fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
691                                            MAX_LAN_ADDR, argv[i]);
692                                }
693                        }
694                        else
695                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
696                        break;
697                case 'i':
698                        if(i+1 < argc)
699                        {
700                                int address_already_there = 0;
701                                int j;
702                                i++;
703                                if( getifaddr(argv[i], ip_addr, sizeof(ip_addr)) < 0 )
704                                {
705                                        fprintf(stderr, "Network interface '%s' not found.\n",
706                                                argv[i]);
707                                        exit(-1);
708                                }
709                                for(j=0; j<n_lan_addr; j++)
710                                {
711                                        struct lan_addr_s tmpaddr;
712                                        parselanaddr(&tmpaddr, ip_addr);
713                                        if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
714                                                address_already_there = 1;
715                                }
716                                if(address_already_there)
717                                        break;
718                                if(n_lan_addr < MAX_LAN_ADDR)
719                                {
720                                        if(parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0)
721                                                n_lan_addr++;
722                                }
723                                else
724                                {
725                                        fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
726                                            MAX_LAN_ADDR, argv[i]);
727                                }
728                        }
729                        else
730                                fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
731                        break;
732                case 'f':
733                        i++;    /* discarding, the config file is already read */
734                        break;
735                case 'h':
736                        runtime_vars.port = 0; // triggers help display
737                        break;
738                case 'R':
739                        snprintf(real_path, sizeof(real_path), "rm -rf %s/files.db %s/art_cache", db_path, db_path);
740                        system(real_path);
741                        break;
742                case 'V':
743                        printf("Version " MINIDLNA_VERSION "\n");
744                        exit(0);
745                        break;
746                default:
747                        fprintf(stderr, "Unknown option: %s\n", argv[i]);
748                }
749        }
750        /* If no IP was specified, try to detect one */
751        if( n_lan_addr < 1 )
752        {
753                if( (getsysaddr(ip_addr, sizeof(ip_addr)) < 0) &&
754                    (getifaddr("eth0", ip_addr, sizeof(ip_addr)) < 0) &&
755                    (getifaddr("eth1", ip_addr, sizeof(ip_addr)) < 0) )
756                {
757                        DPRINTF(E_OFF, L_GENERAL, "No IP address automatically detected!\n");
758                }
759                if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 )
760                {
761                        n_lan_addr++;
762                }
763        }
764
765        if( (n_lan_addr==0) || (runtime_vars.port<=0) )
766        {
767                fprintf(stderr, "Usage:\n\t"
768                        "%s [-d] [-f config_file]\n"
769                        "\t\t[-a listening_ip] [-p port]\n"
770                        /*"[-l logfile] " not functionnal */
771                        "\t\t[-s serial] [-m model_number] \n"
772                        "\t\t[-t notify_interval] [-P pid_filename]\n"
773                        "\t\t[-w url] [-R] [-V] [-h]\n"
774                        "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n"
775                        "\tDefault pid file is %s.\n"
776                        "\tWith -d minidlna will run in debug mode (not daemonize).\n"
777                        "\t-w sets the presentation url. Default is http address on port 80\n"
778                        "\t-h displays this text\n"
779                        "\t-R forces a full rescan\n"
780                        "\t-V print the version number\n",
781                        argv[0], pidfilename);
782                return 1;
783        }
784
785        if(debug_flag)
786        {
787                pid = getpid();
788                log_init(NULL, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=debug");
789        }
790        else
791        {
792#ifdef USE_DAEMON
793                if(daemon(0, 0)<0) {
794                        perror("daemon()");
795                }
796                pid = getpid();
797#else
798                pid = daemonize();
799#endif
800                #ifdef READYNAS
801                log_init("/var/log/upnp-av.log", "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
802                #else
803                if( access(db_path, F_OK) != 0 )
804                        make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
805                sprintf(real_path, "%s/minidlna.log", log_path);
806                log_init(real_path, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
807                #endif
808        }
809
810        if(checkforrunning(pidfilename) < 0)
811        {
812                DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n");
813                return 1;
814        }       
815
816        set_startup_time();
817
818        /* presentation url */
819        if(presurl)
820        {
821                strncpy(presentationurl, presurl, PRESENTATIONURL_MAX_LEN);
822                presentationurl[PRESENTATIONURL_MAX_LEN-1] = '\0';
823        }
824        else
825        {
826#ifdef READYNAS
827                snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
828                         "http://%s/admin/", lan_addr[0].str);
829#else
830                snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
831                         "http://%s:%d/", lan_addr[0].str, runtime_vars.port);
832#endif
833        }
834
835        /* set signal handler */
836        signal(SIGCLD, SIG_IGN);
837        memset(&sa, 0, sizeof(struct sigaction));
838        sa.sa_handler = sigterm;
839        if (sigaction(SIGTERM, &sa, NULL))
840        {
841                DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGTERM handler. EXITING.\n");
842        }
843        if (sigaction(SIGINT, &sa, NULL))
844        {
845                DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGINT handler. EXITING.\n");
846        }
847
848        if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
849                DPRINTF(E_FATAL, L_GENERAL, "Failed to ignore SIGPIPE signals. EXITING.\n");
850        }
851
852        writepidfile(pidfilename, pid);
853
854        return 0;
855}
856
857/* === main === */
858/* process HTTP or SSDP requests */
859int
860main(int argc, char * * argv)
861{
862        int i;
863        int sudp = -1, shttpl = -1;
864        int snotify[MAX_LAN_ADDR];
865        LIST_HEAD(httplisthead, upnphttp) upnphttphead;
866        struct upnphttp * e = 0;
867        struct upnphttp * next;
868        fd_set readset; /* for select() */
869        fd_set writeset;
870        struct timeval timeout, timeofday, lastnotifytime = {0, 0};
871        time_t lastupdatetime = 0;
872        int max_fd = -1;
873        int last_changecnt = 0;
874        pid_t scanner_pid = 0;
875        pthread_t inotify_thread = 0;
876        struct media_dir_s *media_path, *last_path;
877        struct album_art_name_s *art_names, *last_name;
878#ifdef TIVO_SUPPORT
879        unsigned short int beacon_interval = 5;
880        int sbeacon = -1;
881        struct sockaddr_in tivo_bcast;
882        struct timeval lastbeacontime = {0, 0};
883#endif
884
885#ifdef ENABLE_NLS
886        setlocale(LC_MESSAGES, "");
887        setlocale(LC_CTYPE, "en_US.utf8");
888        DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR")));
889        textdomain("minidlna");
890#endif
891
892        if(init(argc, argv) != 0)
893                return 1;
894
895#ifdef READYNAS
896        DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n");
897        unlink("/ramfs/.upnp-av_scan");
898#else
899        DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION " [SQLite %s].\n", sqlite3_libversion());
900        if( !sqlite3_threadsafe() )
901        {
902                DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe!  "
903                                            "Scanning must be finished before file serving can begin, "
904                                            "and inotify will be disabled.\n");
905        }
906        if( sqlite3_libversion_number() < 3005001 )
907        {
908                DPRINTF(E_WARN, L_GENERAL, "SQLite library is old.  Please use version 3.5.1 or newer.\n");
909        }
910#endif
911        LIST_INIT(&upnphttphead);
912
913        if( open_db() == 0 )
914        {
915                updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS");
916        }
917        i = db_upgrade(db);
918        if( i != 0 )
919        {
920                if( i < 0 )
921                {
922                        DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n");
923                }
924                else
925                {
926                        DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n");
927                }
928                sqlite3_close(db);
929                char *cmd;
930                asprintf(&cmd, "rm -rf %s/files.db %s/art_cache", db_path, db_path);
931                system(cmd);
932                free(cmd);
933                open_db();
934                if( CreateDatabase() != 0 )
935                {
936                        DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database!  Exiting...\n");
937                }
938#if USE_FORK
939                scanning = 1;
940                sqlite3_close(db);
941                scanner_pid = fork();
942                open_db();
943                if( !scanner_pid ) // child (scanner) process
944                {
945                        start_scanner();
946                        sqlite3_close(db);
947                        media_path = media_dirs;
948                        art_names = album_art_names;
949                        while( media_path )
950                        {
951                                free(media_path->path);
952                                last_path = media_path;
953                                media_path = media_path->next;
954                                free(last_path);
955                        }
956                        while( art_names )
957                        {
958                                free(art_names->name);
959                                last_name = art_names;
960                                art_names = art_names->next;
961                                free(last_name);
962                        }
963                        freeoptions();
964                        exit(EXIT_SUCCESS);
965                }
966#else
967                start_scanner();
968#endif
969        }
970        if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 &&
971            GETFLAG(INOTIFY_MASK) && pthread_create(&inotify_thread, NULL, start_inotify, NULL) )
972        {
973                DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n");
974        }
975
976        sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr);
977        if(sudp < 0)
978        {
979                DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n");
980                if(SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) {
981                        DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING");
982                        return 1;
983                }
984        }
985        /* open socket for HTTP connections. Listen on the 1st LAN address */
986        shttpl = OpenAndConfHTTPSocket(runtime_vars.port);
987        if(shttpl < 0)
988        {
989                DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n");
990        }
991        DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port);
992
993        /* open socket for sending notifications */
994        if(OpenAndConfSSDPNotifySockets(snotify) < 0)
995        {
996                DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify "
997                        "messages. EXITING\n");
998        }
999
1000#ifdef TIVO_SUPPORT
1001        if( GETFLAG(TIVO_MASK) )
1002        {
1003                DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n");
1004                /* Add TiVo-specific randomize function to sqlite */
1005                if( sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL) != SQLITE_OK )
1006                {
1007                        DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n");
1008                }
1009                /* open socket for sending Tivo notifications */
1010                sbeacon = OpenAndConfTivoBeaconSocket();
1011                if(sbeacon < 0)
1012                {
1013                        DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify "
1014                                "messages. EXITING\n");
1015                }
1016                tivo_bcast.sin_family = AF_INET;
1017                tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress());
1018                tivo_bcast.sin_port = htons(2190);
1019        }
1020        else
1021        {
1022                sbeacon = -1;
1023        }
1024#endif
1025
1026        SendSSDPGoodbye(snotify, n_lan_addr);
1027
1028        /* main loop */
1029        while(!quitting)
1030        {
1031                /* Check if we need to send SSDP NOTIFY messages and do it if
1032                 * needed */
1033                if(gettimeofday(&timeofday, 0) < 0)
1034                {
1035                        DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));
1036                        timeout.tv_sec = runtime_vars.notify_interval;
1037                        timeout.tv_usec = 0;
1038                }
1039                else
1040                {
1041                        /* the comparaison is not very precise but who cares ? */
1042                        if(timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval))
1043                        {
1044                                SendSSDPNotifies2(snotify,
1045                                          (unsigned short)runtime_vars.port,
1046                                          (runtime_vars.notify_interval << 1)+10);
1047                                memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval));
1048                                timeout.tv_sec = runtime_vars.notify_interval;
1049                                timeout.tv_usec = 0;
1050                        }
1051                        else
1052                        {
1053                                timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval
1054                                                 - timeofday.tv_sec;
1055                                if(timeofday.tv_usec > lastnotifytime.tv_usec)
1056                                {
1057                                        timeout.tv_usec = 1000000 + lastnotifytime.tv_usec
1058                                                          - timeofday.tv_usec;
1059                                        timeout.tv_sec--;
1060                                }
1061                                else
1062                                {
1063                                        timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec;
1064                                }
1065                        }
1066#ifdef TIVO_SUPPORT
1067                        if( GETFLAG(TIVO_MASK) )
1068                        {
1069                                if(timeofday.tv_sec >= (lastbeacontime.tv_sec + beacon_interval))
1070                                {
1071                                        sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1);
1072                                        memcpy(&lastbeacontime, &timeofday, sizeof(struct timeval));
1073                                        if( timeout.tv_sec > beacon_interval )
1074                                        {
1075                                                timeout.tv_sec = beacon_interval;
1076                                                timeout.tv_usec = 0;
1077                                        }
1078                                        /* Beacons should be sent every 5 seconds or so for the first minute,
1079                                         * then every minute or so thereafter. */
1080                                        if( beacon_interval == 5 && (timeofday.tv_sec - startup_time) > 60 )
1081                                        {
1082                                                beacon_interval = 60;
1083                                        }
1084                                }
1085                                else if( timeout.tv_sec > (lastbeacontime.tv_sec + beacon_interval + 1 - timeofday.tv_sec) )
1086                                {
1087                                        timeout.tv_sec = lastbeacontime.tv_sec + beacon_interval - timeofday.tv_sec;
1088                                }
1089                        }
1090#endif
1091                }
1092
1093                if( scanning )
1094                {
1095                        if( !scanner_pid || kill(scanner_pid, 0) )
1096                        {
1097                                scanning = 0;
1098                                updateID++;
1099                        }
1100                }
1101
1102                /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
1103                FD_ZERO(&readset);
1104
1105                if (sudp >= 0)
1106                {
1107                        FD_SET(sudp, &readset);
1108                        max_fd = MAX(max_fd, sudp);
1109                }
1110               
1111                if (shttpl >= 0)
1112                {
1113                        FD_SET(shttpl, &readset);
1114                        max_fd = MAX(max_fd, shttpl);
1115                }
1116#ifdef TIVO_SUPPORT
1117                if (sbeacon >= 0)
1118                {
1119                        FD_SET(sbeacon, &readset);
1120                        max_fd = MAX(max_fd, sbeacon);
1121                }
1122#endif
1123                i = 0;  /* active HTTP connections count */
1124                for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
1125                {
1126                        if((e->socket >= 0) && (e->state <= 2))
1127                        {
1128                                FD_SET(e->socket, &readset);
1129                                max_fd = MAX(max_fd, e->socket);
1130                                i++;
1131                        }
1132                }
1133#ifdef DEBUG
1134                /* for debug */
1135                if(i > 1)
1136                {
1137                        DPRINTF(E_DEBUG, L_GENERAL, "%d active incoming HTTP connections\n", i);
1138                }
1139#endif
1140                FD_ZERO(&writeset);
1141                upnpevents_selectfds(&readset, &writeset, &max_fd);
1142
1143                if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0)
1144                {
1145                        if(quitting) goto shutdown;
1146                        DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno));
1147                        DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n");
1148                }
1149                upnpevents_processfds(&readset, &writeset);
1150                /* process SSDP packets */
1151                if(sudp >= 0 && FD_ISSET(sudp, &readset))
1152                {
1153                        /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
1154                        ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port);
1155                }
1156#ifdef TIVO_SUPPORT
1157                if(sbeacon >= 0 && FD_ISSET(sbeacon, &readset))
1158                {
1159                        /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
1160                        ProcessTiVoBeacon(sbeacon);
1161                }
1162#endif
1163                /* increment SystemUpdateID if the content database has changed,
1164                 * and if there is an active HTTP connection, at most once every 2 seconds */
1165                if( i && (timeofday.tv_sec >= (lastupdatetime + 2)) )
1166                {
1167                        if( scanning || sqlite3_total_changes(db) != last_changecnt )
1168                        {
1169                                updateID++;
1170                                last_changecnt = sqlite3_total_changes(db);
1171                                upnp_event_var_change_notify(EContentDirectory);
1172                                lastupdatetime = timeofday.tv_sec;
1173                        }
1174                }
1175                /* process active HTTP connections */
1176                for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
1177                {
1178                        if(  (e->socket >= 0) && (e->state <= 2)
1179                                &&(FD_ISSET(e->socket, &readset)) )
1180                        {
1181                                Process_upnphttp(e);
1182                        }
1183                }
1184                /* process incoming HTTP connections */
1185                if(shttpl >= 0 && FD_ISSET(shttpl, &readset))
1186                {
1187                        int shttp;
1188                        socklen_t clientnamelen;
1189                        struct sockaddr_in clientname;
1190                        clientnamelen = sizeof(struct sockaddr_in);
1191                        shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen);
1192                        if(shttp<0)
1193                        {
1194                                DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno));
1195                        }
1196                        else
1197                        {
1198                                struct upnphttp * tmp = 0;
1199                                DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n",
1200                                        inet_ntoa(clientname.sin_addr),
1201                                        ntohs(clientname.sin_port) );
1202                                /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
1203                                        DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n");
1204                                }*/
1205                                /* Create a new upnphttp object and add it to
1206                                 * the active upnphttp object list */
1207                                tmp = New_upnphttp(shttp);
1208                                if(tmp)
1209                                {
1210                                        tmp->clientaddr = clientname.sin_addr;
1211                                        LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
1212                                }
1213                                else
1214                                {
1215                                        DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n");
1216                                        close(shttp);
1217                                }
1218                        }
1219                }
1220                /* delete finished HTTP connections */
1221                for(e = upnphttphead.lh_first; e != NULL; )
1222                {
1223                        next = e->entries.le_next;
1224                        if(e->state >= 100)
1225                        {
1226                                LIST_REMOVE(e, entries);
1227                                Delete_upnphttp(e);
1228                        }
1229                        e = next;
1230                }
1231        }
1232
1233shutdown:
1234        /* kill the scanner */
1235        if( scanning && scanner_pid )
1236        {
1237                kill(scanner_pid, 9);
1238        }
1239        /* close out open sockets */
1240        while(upnphttphead.lh_first != NULL)
1241        {
1242                e = upnphttphead.lh_first;
1243                LIST_REMOVE(e, entries);
1244                Delete_upnphttp(e);
1245        }
1246
1247        if (sudp >= 0) close(sudp);
1248        if (shttpl >= 0) close(shttpl);
1249        #ifdef TIVO_SUPPORT
1250        if (sbeacon >= 0) close(sbeacon);
1251        #endif
1252       
1253        if(SendSSDPGoodbye(snotify, n_lan_addr) < 0)
1254        {
1255                DPRINTF(E_ERROR, L_GENERAL, "Failed to broadcast good-bye notifications\n");
1256        }
1257        for(i=0; i<n_lan_addr; i++)
1258                close(snotify[i]);
1259
1260        if( inotify_thread )
1261                pthread_join(inotify_thread, NULL);
1262
1263        sql_exec(db, "UPDATE SETTINGS set UPDATE_ID = %u", updateID);
1264        sqlite3_close(db);
1265
1266        media_path = media_dirs;
1267        art_names = album_art_names;
1268        while( media_path )
1269        {
1270                free(media_path->path);
1271                last_path = media_path;
1272                media_path = media_path->next;
1273                free(last_path);
1274        }
1275        while( art_names )
1276        {
1277                free(art_names->name);
1278                last_name = art_names;
1279                art_names = art_names->next;
1280                free(last_name);
1281        }
1282
1283        if(unlink(pidfilename) < 0)
1284        {
1285                DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno));
1286        }
1287
1288        freeoptions();
1289
1290        exit(EXIT_SUCCESS);
1291}
1292
Note: See TracBrowser for help on using the repository browser.