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 strncpyt(buf, hn, len); 214 dot = strchr(buf, '.'); 215 if( dot ) 216 *dot = '\0'; 217 } 218 else 219 { 220 strcpy(buf, "Unknown"); 221 } 222 free(hn); 223 224 off = strlen(buf); 225 off += snprintf(buf+off, len-off, ": "); 226#ifdef READYNAS 227 FILE * info; 228 char ibuf[64], *key, *val; 229 snprintf(buf+off, len-off, "ReadyNAS"); 230 info = fopen("/proc/sys/dev/boot/info", "r"); 231 if( !info ) 232 return; 233 while( (val = fgets(ibuf, 64, info)) != NULL ) 234 { 235 key = strsep(&val, ": \t"); 236 val = trim(val); 237 if( strcmp(key, "model") == 0 ) 238 { 239 snprintf(buf+off, len-off, "%s", val); 240 key = strchr(val, ' '); 241 if( key ) 242 { 243 strncpyt(modelnumber, key+1, MODELNUMBER_MAX_LEN); 244 *key = '\0'; 245 } 246 snprintf(modelname, MODELNAME_MAX_LEN, 247 "Windows Media Connect compatible (%s)", val); 248 } 249 else if( strcmp(key, "serial") == 0 ) 250 { 251 strncpyt(serialnumber, val, SERIALNUMBER_MAX_LEN); 252 if( serialnumber[0] == '\0' ) 253 { 254 char mac_str[13]; 255 if( getsyshwaddr(mac_str, sizeof(mac_str)) == 0 ) 256 strcpy(serialnumber, mac_str); 257 else 258 strcpy(serialnumber, "0"); 259 } 260 break; 261 } 262 } 263 fclose(info); 264 memcpy(pnpx_hwid+4, "01F2", 4); 265 if( strcmp(modelnumber, "NVX") == 0 ) 266 memcpy(pnpx_hwid+17, "0101", 4); 267 else if( strcmp(modelnumber, "Pro") == 0 || 268 strcmp(modelnumber, "Pro 6") == 0 || 269 strncmp(modelnumber, "Ultra 6", 7) == 0 ) 270 memcpy(pnpx_hwid+17, "0102", 4); 271 else if( strcmp(modelnumber, "Pro 2") == 0 || 272 strncmp(modelnumber, "Ultra 2", 7) == 0 ) 273 memcpy(pnpx_hwid+17, "0103", 4); 274 else if( strcmp(modelnumber, "Pro 4") == 0 || 275 strncmp(modelnumber, "Ultra 4", 7) == 0 ) 276 memcpy(pnpx_hwid+17, "0104", 4); 277 else if( strcmp(modelnumber+1, "100") == 0 ) 278 memcpy(pnpx_hwid+17, "0105", 4); 279 else if( strcmp(modelnumber+1, "200") == 0 ) 280 memcpy(pnpx_hwid+17, "0106", 4); 281 /* 0107 = Stora */ 282 else if( strcmp(modelnumber, "Duo v2") == 0 ) 283 memcpy(pnpx_hwid+17, "0108", 4); 284 else if( strcmp(modelnumber, "NV+ v2") == 0 ) 285 memcpy(pnpx_hwid+17, "0109", 4); 286#else 287 char * logname; 288 logname = getenv("LOGNAME"); 289#ifndef STATIC // Disable for static linking 290 if( !logname ) 291 { 292 struct passwd * pwent; 293 pwent = getpwuid(getuid()); 294 if( pwent ) 295 logname = pwent->pw_name; 296 } 297#endif 298/* Modify by Foxconn Antony start 02/17/2012 set the properly friendly name */ 299// snprintf(buf+off, len-off, "%s", logname?logname:"Unknown"); 300 snprintf(buf+off, len-off, "%s", logname?logname:"ReadyShare"); 301/* Modify by Foxconn Antony end 02/17/2012 */ 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 verbose_flag = 0; 344 int options_flag = 0; 345 struct sigaction sa; 346 const char * presurl = NULL; 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 buf[PATH_MAX]; 353 char ip_addr[INET_ADDRSTRLEN + 3] = {'\0'}; 354 char log_str[72] = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn"; 355 char *log_level = NULL; 356 357 /* first check if "-f" option is used */ 358 for(i=2; i<argc; i++) 359 { 360 if(0 == strcmp(argv[i-1], "-f")) 361 { 362 optionsfile = argv[i]; 363 options_flag = 1; 364 break; 365 } 366 } 367 368 /* set up uuid based on mac address */ 369 if( getsyshwaddr(mac_str, sizeof(mac_str)) < 0 ) 370 { 371 DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n"); 372 strcpy(mac_str, "554e4b4e4f57"); 373 } 374 strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-"); 375 strncat(uuidvalue, mac_str, 12); 376 377 getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN); 378 379 runtime_vars.port = -1; 380 runtime_vars.notify_interval = 895; /* seconds between SSDP announces */ 381 runtime_vars.root_container = NULL; 382 383 /* read options file first since 384 * command line arguments have final say */ 385 if(readoptionsfile(optionsfile) < 0) 386 { 387 /* only error if file exists or using -f */ 388 if(access(optionsfile, F_OK) == 0 || options_flag) 389 DPRINTF(E_ERROR, L_GENERAL, "Error reading configuration file %s\n", optionsfile); 390 } 391 else 392 { 393 for(i=0; i<num_options; i++) 394 { 395 switch(ary_options[i].id) 396 { 397 case UPNPIFNAME: 398 for( string = ary_options[i].value; (word = strtok(string, ",")); string = NULL ) 399 { 400 if(n_lan_addr < MAX_LAN_ADDR) 401 { 402 if(getifaddr(word, ip_addr, sizeof(ip_addr)) >= 0) 403 { 404 if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 ) 405 if(n_lan_addr < MAX_LAN_ADDR) 406 n_lan_addr++; 407 } 408 } 409 else 410 { 411 DPRINTF(E_ERROR, L_GENERAL, "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 DPRINTF(E_ERROR, L_GENERAL, "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 strncpyt(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); 440 break; 441 case UPNPMODEL_NAME: 442 strncpyt(modelname, ary_options[i].value, MODELNAME_MAX_LEN); 443 break; 444 case UPNPMODEL_NUMBER: 445 strncpyt(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN); 446 break; 447 case UPNPFRIENDLYNAME: 448 strncpyt(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN); 449 break; 450 case UPNPMEDIADIR: 451 type = ALL_MEDIA; 452 char * myval = NULL; 453 switch( ary_options[i].value[0] ) 454 { 455 case 'A': 456 case 'a': 457 if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' ) 458 type = AUDIO_ONLY; 459 case 'V': 460 case 'v': 461 if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' ) 462 type = VIDEO_ONLY; 463 case 'P': 464 case 'p': 465 if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' ) 466 type = IMAGES_ONLY; 467 myval = index(ary_options[i].value, '/'); 468 case '/': 469 path = realpath(myval ? myval:ary_options[i].value, buf); 470 if( !path ) 471 path = (myval ? myval:ary_options[i].value); 472 if( access(path, F_OK) != 0 ) 473 { 474 DPRINTF(E_ERROR, L_GENERAL, "Media directory \"%s\" not accessible! [%s]\n", 475 path, strerror(errno)); 476 break; 477 } 478 struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s)); 479 this_dir->path = strdup(path); 480 this_dir->type = type; 481 if( !media_dirs ) 482 { 483 media_dirs = this_dir; 484 } 485 else 486 { 487 struct media_dir_s * all_dirs = media_dirs; 488 while( all_dirs->next ) 489 all_dirs = all_dirs->next; 490 all_dirs->next = this_dir; 491 } 492 break; 493 default: 494 DPRINTF(E_ERROR, L_GENERAL, "Media directory entry not understood! [%s]\n", 495 ary_options[i].value); 496 break; 497 } 498 break; 499 case UPNPALBUMART_NAMES: 500 for( string = ary_options[i].value; (word = strtok(string, "/")); string = NULL ) 501 { 502 struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s)); 503 int len = strlen(word); 504 if( word[len-1] == '*' ) 505 { 506 word[len-1] = '\0'; 507 this_name->wildcard = 1; 508 } 509 this_name->name = strdup(word); 510 if( !album_art_names ) 511 { 512 album_art_names = this_name; 513 } 514 else 515 { 516 struct album_art_name_s * all_names = album_art_names; 517 while( all_names->next ) 518 all_names = all_names->next; 519 all_names->next = this_name; 520 } 521 } 522 break; 523 case UPNPDBDIR: 524 path = realpath(ary_options[i].value, buf); 525 if( !path ) 526 path = (ary_options[i].value); 527 make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 528 if( access(path, F_OK) != 0 ) 529 { 530 DPRINTF(E_FATAL, L_GENERAL, "Database path not accessible! [%s]\n", path); 531 break; 532 } 533 strncpyt(db_path, path, PATH_MAX); 534 break; 535 case UPNPLOGDIR: 536 path = realpath(ary_options[i].value, buf); 537 if( !path ) 538 path = (ary_options[i].value); 539 make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 540 if( access(path, F_OK) != 0 ) 541 { 542 DPRINTF(E_FATAL, L_GENERAL, "Log path not accessible! [%s]\n", path); 543 break; 544 } 545 strncpyt(log_path, path, PATH_MAX); 546 break; 547 case UPNPLOGLEVEL: 548 log_level = ary_options[i].value; 549 break; 550 case UPNPINOTIFY: 551 if( (strcmp(ary_options[i].value, "yes") != 0) && !atoi(ary_options[i].value) ) 552 CLEARFLAG(INOTIFY_MASK); 553 break; 554 case ENABLE_TIVO: 555 if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) ) 556 SETFLAG(TIVO_MASK); 557 break; 558 case ENABLE_DLNA_STRICT: 559 if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) ) 560 SETFLAG(DLNA_STRICT_MASK); 561 break; 562 case ROOT_CONTAINER: 563 switch( ary_options[i].value[0] ) 564 { 565 case '.': 566 runtime_vars.root_container = NULL; 567 break; 568 case 'B': 569 case 'b': 570 runtime_vars.root_container = BROWSEDIR_ID; 571 break; 572 case 'M': 573 case 'm': 574 runtime_vars.root_container = MUSIC_ID; 575 break; 576 case 'V': 577 case 'v': 578 runtime_vars.root_container = VIDEO_ID; 579 break; 580 case 'P': 581 case 'p': 582 runtime_vars.root_container = IMAGE_ID; 583 break; 584 default: 585 DPRINTF(E_ERROR, L_GENERAL, "Invalid root container! [%s]\n", 586 ary_options[i].value); 587 break; 588 } 589 break; 590 case UPNPMINISSDPDSOCKET: 591 minissdpdsocketpath = ary_options[i].value; 592 break; 593 default: 594 DPRINTF(E_DEBUG, L_GENERAL, "Unknown option in file %s\n", 595 optionsfile); 596 } 597 } 598 } 599 if( log_path[0] == '\0' ) 600 { 601 if( db_path[0] == '\0' ) 602 strncpyt(log_path, "/tmp/log", PATH_MAX); 603 else 604 strncpyt(log_path, db_path, PATH_MAX); 605 } 606 if( db_path[0] == '\0' ) 607 strncpyt(db_path, DEFAULT_DB_PATH, PATH_MAX); 608 609 /* command line arguments processing */ 610 for(i=1; i<argc; i++) 611 { 612 if(argv[i][0]!='-') 613 { 614 DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[i]); 615 } 616 else if(strcmp(argv[i], "--help")==0) 617 { 618 runtime_vars.port = 0; 619 break; 620 } 621 else switch(argv[i][1]) 622 { 623 case 't': 624 if(i+1 < argc) 625 runtime_vars.notify_interval = atoi(argv[++i]); 626 else 627 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 628 break; 629 case 's': 630 if(i+1 < argc) 631 strncpyt(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN); 632 else 633 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 634 break; 635 case 'm': 636 if(i+1 < argc) 637 strncpyt(modelnumber, argv[++i], MODELNUMBER_MAX_LEN); 638 else 639 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 640 break; 641 case 'p': 642 if(i+1 < argc) 643 runtime_vars.port = atoi(argv[++i]); 644 else 645 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 646 break; 647 case 'P': 648 if(i+1 < argc) 649 pidfilename = argv[++i]; 650 else 651 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 652 break; 653 case 'd': 654 debug_flag = 1; 655 case 'v': 656 verbose_flag = 1; 657 break; 658 case 'L': 659 SETFLAG(NO_PLAYLIST_MASK); 660 break; 661 case 'w': 662 if(i+1 < argc) 663 presurl = argv[++i]; 664 else 665 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 666 break; 667 case 'a': 668 if(i+1 < argc) 669 { 670 int address_already_there = 0; 671 int j; 672 i++; 673 for(j=0; j<n_lan_addr; j++) 674 { 675 struct lan_addr_s tmpaddr; 676 parselanaddr(&tmpaddr, argv[i]); 677 if(0 == strcmp(lan_addr[j].str, tmpaddr.str)) 678 address_already_there = 1; 679 } 680 if(address_already_there) 681 break; 682 if(n_lan_addr < MAX_LAN_ADDR) 683 { 684 if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0) 685 n_lan_addr++; 686 } 687 else 688 { 689 DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", 690 MAX_LAN_ADDR, argv[i]); 691 } 692 } 693 else 694 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 695 break; 696 case 'i': 697 if(i+1 < argc) 698 { 699 int address_already_there = 0; 700 int j; 701 i++; 702 if( getifaddr(argv[i], ip_addr, sizeof(ip_addr)) < 0 ) 703 { 704 DPRINTF(E_FATAL, L_GENERAL, "Required network interface '%s' not found.\n", 705 argv[i]); 706 } 707 for(j=0; j<n_lan_addr; j++) 708 { 709 struct lan_addr_s tmpaddr; 710 parselanaddr(&tmpaddr, ip_addr); 711 if(0 == strcmp(lan_addr[j].str, tmpaddr.str)) 712 address_already_there = 1; 713 } 714 if(address_already_there) 715 break; 716 if(n_lan_addr < MAX_LAN_ADDR) 717 { 718 if(parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0) 719 n_lan_addr++; 720 } 721 else 722 { 723 DPRINTF(E_ERROR, L_GENERAL, "Too many listening ips (max: %d), ignoring %s\n", 724 MAX_LAN_ADDR, argv[i]); 725 } 726 } 727 else 728 DPRINTF(E_ERROR, L_GENERAL, "Option -%c takes one argument.\n", argv[i][1]); 729 break; 730 case 'f': 731 i++; /* discarding, the config file is already read */ 732 break; 733 case 'h': 734 runtime_vars.port = 0; // triggers help display 735 break; 736 case 'R': 737 snprintf(buf, sizeof(buf), "rm -rf %s/files.db %s/art_cache", db_path, db_path); 738 if( system(buf) != 0 ) 739 DPRINTF(E_WARN, L_GENERAL, "Failed to clean old file cache.\n"); 740 break; 741 case 'V': 742 printf("Version " MINIDLNA_VERSION "\n"); 743 exit(0); 744 break; 745 default: 746 DPRINTF(E_ERROR, L_GENERAL, "Unknown option: %s\n", argv[i]); 747 } 748 } 749 /* If no IP was specified, try to detect one */ 750 751 if( n_lan_addr < 1 ) 752 { 753 754 if( (getsysaddrs() <= 0 ) && 755 (getifaddr("eth0", ip_addr, sizeof(ip_addr)) < 0) && 756 (getifaddr("eth1", ip_addr, sizeof(ip_addr)) < 0) ) 757 { 758 DPRINTF(E_OFF, L_GENERAL, "No IP address automatically detected!\n"); 759 } 760 if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 ) 761 { 762 n_lan_addr++; 763 } 764 } 765 766 if( (n_lan_addr==0) || (runtime_vars.port<=0) ) 767 { 768 DPRINTF(E_ERROR, L_GENERAL, "Usage:\n\t" 769 "%s [-d] [-v] [-f config_file]\n" 770 "\t\t[-a listening_ip] [-p port]\n" 771 /*"[-l logfile] " not functionnal */ 772 "\t\t[-s serial] [-m model_number] \n" 773 "\t\t[-t notify_interval] [-P pid_filename]\n" 774 "\t\t[-w url] [-R] [-V] [-h]\n" 775 "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n" 776 "\tDefault pid file is %s.\n" 777 "\tWith -d minidlna will run in debug mode (not daemonize).\n" 778 "\t-w sets the presentation url. Default is http address on port 80\n" 779 "\t-h displays this text\n" 780 "\t-R forces a full rescan\n" 781 "\t-L do note create playlists\n" 782 "\t-V print the version number\n", 783 argv[0], pidfilename); 784 return 1; 785 } 786 787 if( verbose_flag ) 788 { 789 strcpy(log_str+65, "debug"); 790 log_level = log_str; 791 } 792 else if( !log_level ) 793 { 794 log_level = log_str; 795 } 796 if(debug_flag) 797 { 798 pid = getpid(); 799 log_init(NULL, log_level); 800 } 801 else 802 { 803 pid = daemonize(); 804 #ifdef READYNAS 805 log_init("/var/log/upnp-av.log", log_level); 806 #else 807 if( access(db_path, F_OK) != 0 ) 808 make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 809 sprintf(buf, "%s/minidlna.log", log_path); 810 log_init(buf, log_level); 811 #endif 812 } 813 814 if(checkforrunning(pidfilename) < 0) 815 { 816 DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n"); 817 return 1; 818 } 819 820 set_startup_time(); 821 822 /* presentation url */ 823 if(presurl) 824 strncpyt(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); 825 else 826 strcpy(presentationurl, "/"); 827 828 /* set signal handler */ 829 memset(&sa, 0, sizeof(struct sigaction)); 830 sa.sa_handler = sigterm; 831 if (sigaction(SIGTERM, &sa, NULL)) 832 { 833 DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGTERM handler. EXITING.\n"); 834 } 835 if (sigaction(SIGINT, &sa, NULL)) 836 { 837 DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGINT handler. EXITING.\n"); 838 } 839 840 if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) { 841 DPRINTF(E_FATAL, L_GENERAL, "Failed to ignore SIGPIPE signals. EXITING.\n"); 842 } 843 844 writepidfile(pidfilename, pid); 845 846 return 0; 847} 848 849/* === main === */ 850/* process HTTP or SSDP requests */ 851int 852main(int argc, char * * argv) 853{ 854 int i; 855 int sudp = -1, shttpl = -1; 856 int snotify[MAX_LAN_ADDR]; 857 LIST_HEAD(httplisthead, upnphttp) upnphttphead; 858 struct upnphttp * e = 0; 859 struct upnphttp * next; 860 fd_set readset; /* for select() */ 861 fd_set writeset; 862 struct timeval timeout, timeofday, lastnotifytime = {0, 0}; 863 time_t lastupdatetime = 0; 864 int max_fd = -1; 865 int last_changecnt = 0; 866 pid_t scanner_pid = 0; 867 pthread_t inotify_thread = 0; 868 struct media_dir_s *media_path, *last_path; 869 struct album_art_name_s *art_names, *last_name; 870#ifdef TIVO_SUPPORT 871 unsigned short int beacon_interval = 5; 872 int sbeacon = -1; 873 struct sockaddr_in tivo_bcast; 874 struct timeval lastbeacontime = {0, 0}; 875#endif 876 877 for (i = 0; i < L_MAX; i++) 878 log_level[i] = E_WARN; 879#ifdef ENABLE_NLS 880 setlocale(LC_MESSAGES, ""); 881 setlocale(LC_CTYPE, "en_US.utf8"); 882 DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR"))); 883 textdomain("minidlna"); 884#endif 885 886 if (init(argc, argv) != 0) 887 return 1; 888 889#ifdef READYNAS 890 DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n"); 891 unlink("/ramfs/.upnp-av_scan"); 892#else 893 DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION " [SQLite %s].\n", sqlite3_libversion()); 894 if( !sqlite3_threadsafe() ) 895 { 896 DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " 897 "Scanning must be finished before file serving can begin, " 898 "and inotify will be disabled.\n"); 899 } 900 if( sqlite3_libversion_number() < 3005001 ) 901 { 902 DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n"); 903 } 904#endif 905 LIST_INIT(&upnphttphead); 906 907 if( open_db() == 0 ) 908 { 909 updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS"); 910 } 911 i = db_upgrade(db); 912// if( i != 0 ) 913 if(1) 914 { 915 if( i < 0 ) 916 { 917 DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n"); 918 } 919 else 920 { 921 DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n"); 922 } 923 sqlite3_close(db); 924 char *cmd; 925 i = asprintf(&cmd, "rm -rf %s/files.db %s/art_cache", db_path, db_path); 926 if( i > 0 ) 927 i = system(cmd); 928 else 929 cmd = NULL; 930 if( i != 0 ) 931 { 932 DPRINTF(E_WARN, L_GENERAL, "Failed to clean old file cache.\n"); 933 } 934 free(cmd); 935 open_db(); 936 937 if( CreateDatabase() != 0 ) 938 { 939 DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n"); 940 } 941#if USE_FORK 942 scanning = 1; 943 sqlite3_close(db); 944 scanner_pid = fork(); 945 open_db(); 946 if( !scanner_pid ) // child (scanner) process 947 { 948 start_scanner(); 949 sqlite3_close(db); 950 media_path = media_dirs; 951 art_names = album_art_names; 952 while( media_path ) 953 { 954 free(media_path->path); 955 last_path = media_path; 956 media_path = media_path->next; 957 free(last_path); 958 } 959 while( art_names ) 960 { 961 free(art_names->name); 962 last_name = art_names; 963 art_names = art_names->next; 964 free(last_name); 965 } 966 freeoptions(); 967printf("\n\n\n\nminidlan:scan finished\n\n\n"); 968 exit(EXIT_SUCCESS); 969 } 970#else 971 start_scanner(); 972#endif 973 } 974 signal(SIGCHLD, SIG_IGN); 975 if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 && 976 GETFLAG(INOTIFY_MASK) && pthread_create(&inotify_thread, NULL, start_inotify, NULL) ) 977 { 978 DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n"); 979 } 980 981 for( i = 0; i < n_lan_addr; i++ ) 982 { 983 DPRINTF(E_INFO, L_GENERAL, "Enabled interface %s/%s\n", 984 lan_addr[i].str, inet_ntoa(lan_addr[i].mask)); 985 986 } 987 988 sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr); 989 if(sudp < 0) 990 { 991 DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n"); 992 if(SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) { 993 DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING"); 994 return 1; 995 } 996 } 997 /* open socket for HTTP connections. Listen on the 1st LAN address */ 998 shttpl = OpenAndConfHTTPSocket(runtime_vars.port); 999 if(shttpl < 0) 1000 { 1001 DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n"); 1002 } 1003 DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port); 1004 1005 /* open socket for sending notifications */ 1006 if(OpenAndConfSSDPNotifySockets(snotify) < 0) 1007 { 1008 DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify " 1009 "messages. EXITING\n"); 1010 } 1011 1012#ifdef TIVO_SUPPORT 1013 if( GETFLAG(TIVO_MASK) ) 1014 { 1015 DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n"); 1016 /* Add TiVo-specific randomize function to sqlite */ 1017 if( sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL) != SQLITE_OK ) 1018 { 1019 DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n"); 1020 } 1021 /* open socket for sending Tivo notifications */ 1022 sbeacon = OpenAndConfTivoBeaconSocket(); 1023 if(sbeacon < 0) 1024 { 1025 DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify " 1026 "messages. EXITING\n"); 1027 } 1028 tivo_bcast.sin_family = AF_INET; 1029 tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress()); 1030 tivo_bcast.sin_port = htons(2190); 1031 } 1032 else 1033 { 1034 sbeacon = -1; 1035 } 1036#endif 1037 1038 SendSSDPGoodbye(snotify, n_lan_addr); //Foxconn modify start, Kent Huang, 09/18/2012 @Fix minidlna TD issue 1039 1040 /* main loop */ 1041 while(!quitting) 1042 { 1043 /* Check if we need to send SSDP NOTIFY messages and do it if 1044 * needed */ 1045 if(gettimeofday(&timeofday, 0) < 0) 1046 { 1047 DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno)); 1048 timeout.tv_sec = runtime_vars.notify_interval; 1049 timeout.tv_usec = 0; 1050 } 1051 else 1052 { 1053 /* the comparaison is not very precise but who cares ? */ 1054 if(timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) 1055 { 1056 SendSSDPNotifies2(snotify, 1057 (unsigned short)runtime_vars.port, 1058 (runtime_vars.notify_interval << 1)+10); 1059 memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval)); 1060 timeout.tv_sec = runtime_vars.notify_interval; 1061 timeout.tv_usec = 0; 1062 } 1063 else 1064 { 1065 timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval 1066 - timeofday.tv_sec; 1067 if(timeofday.tv_usec > lastnotifytime.tv_usec) 1068 { 1069 timeout.tv_usec = 1000000 + lastnotifytime.tv_usec 1070 - timeofday.tv_usec; 1071 timeout.tv_sec--; 1072 } 1073 else 1074 { 1075 timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec; 1076 } 1077 } 1078#ifdef TIVO_SUPPORT 1079 if( GETFLAG(TIVO_MASK) ) 1080 { 1081 if(timeofday.tv_sec >= (lastbeacontime.tv_sec + beacon_interval)) 1082 { 1083 sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1); 1084 memcpy(&lastbeacontime, &timeofday, sizeof(struct timeval)); 1085 if( timeout.tv_sec > beacon_interval ) 1086 { 1087 timeout.tv_sec = beacon_interval; 1088 timeout.tv_usec = 0; 1089 } 1090 /* Beacons should be sent every 5 seconds or so for the first minute, 1091 * then every minute or so thereafter. */ 1092 if( beacon_interval == 5 && (timeofday.tv_sec - startup_time) > 60 ) 1093 { 1094 beacon_interval = 60; 1095 } 1096 } 1097 else if( timeout.tv_sec > (lastbeacontime.tv_sec + beacon_interval + 1 - timeofday.tv_sec) ) 1098 { 1099 timeout.tv_sec = lastbeacontime.tv_sec + beacon_interval - timeofday.tv_sec; 1100 } 1101 } 1102#endif 1103 } 1104 1105 if( scanning ) 1106 { 1107 if( !scanner_pid || kill(scanner_pid, 0) ) 1108 { 1109 scanning = 0; 1110 updateID++; 1111 } 1112 } 1113 1114 /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ 1115 FD_ZERO(&readset); 1116 1117 if (sudp >= 0) 1118 { 1119 FD_SET(sudp, &readset); 1120 max_fd = MAX(max_fd, sudp); 1121 } 1122 1123 if (shttpl >= 0) 1124 { 1125 FD_SET(shttpl, &readset); 1126 max_fd = MAX(max_fd, shttpl); 1127 } 1128#ifdef TIVO_SUPPORT 1129 if (sbeacon >= 0) 1130 { 1131 FD_SET(sbeacon, &readset); 1132 max_fd = MAX(max_fd, sbeacon); 1133 } 1134#endif 1135 i = 0; /* active HTTP connections count */ 1136 for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) 1137 { 1138 if((e->socket >= 0) && (e->state <= 2)) 1139 { 1140 FD_SET(e->socket, &readset); 1141 max_fd = MAX(max_fd, e->socket); 1142 i++; 1143 } 1144 } 1145#ifdef DEBUG 1146 /* for debug */ 1147 if(i > 1) 1148 { 1149 DPRINTF(E_DEBUG, L_GENERAL, "%d active incoming HTTP connections\n", i); 1150 } 1151#endif 1152 FD_ZERO(&writeset); 1153 upnpevents_selectfds(&readset, &writeset, &max_fd); 1154 1155 if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) 1156 { 1157 if(quitting) goto shutdown; 1158 DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno)); 1159 DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n"); 1160 } 1161 upnpevents_processfds(&readset, &writeset); 1162 /* process SSDP packets */ 1163 if(sudp >= 0 && FD_ISSET(sudp, &readset)) 1164 { 1165 /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ 1166 ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port); 1167 } 1168#ifdef TIVO_SUPPORT 1169 if(sbeacon >= 0 && FD_ISSET(sbeacon, &readset)) 1170 { 1171 /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/ 1172 ProcessTiVoBeacon(sbeacon); 1173 } 1174#endif 1175 /* increment SystemUpdateID if the content database has changed, 1176 * and if there is an active HTTP connection, at most once every 2 seconds */ 1177 if( i && (timeofday.tv_sec >= (lastupdatetime + 2)) ) 1178 { 1179 if( scanning || sqlite3_total_changes(db) != last_changecnt ) 1180 { 1181 updateID++; 1182 last_changecnt = sqlite3_total_changes(db); 1183 upnp_event_var_change_notify(EContentDirectory); 1184 lastupdatetime = timeofday.tv_sec; 1185 } 1186 } 1187 /* process active HTTP connections */ 1188 for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) 1189 { 1190 if( (e->socket >= 0) && (e->state <= 2) 1191 &&(FD_ISSET(e->socket, &readset)) ) 1192 { 1193 Process_upnphttp(e); 1194 } 1195 } 1196 /* process incoming HTTP connections */ 1197 if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) 1198 { 1199 int shttp; 1200 socklen_t clientnamelen; 1201 struct sockaddr_in clientname; 1202 clientnamelen = sizeof(struct sockaddr_in); 1203 shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); 1204 if(!is_disk_mounted()) 1205 close(shttp); 1206 else 1207 { 1208 if(shttp<0) 1209 { 1210 DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno)); 1211 } 1212 else 1213 { 1214 struct upnphttp * tmp = 0; 1215 DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n", 1216 inet_ntoa(clientname.sin_addr), 1217 ntohs(clientname.sin_port) ); 1218 /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) { 1219 DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n"); 1220 }*/ 1221 /* Create a new upnphttp object and add it to 1222 * the active upnphttp object list */ 1223 tmp = New_upnphttp(shttp); 1224 if(tmp) 1225 { 1226 tmp->clientaddr = clientname.sin_addr; 1227 LIST_INSERT_HEAD(&upnphttphead, tmp, entries); 1228 } 1229 else 1230 { 1231 DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n"); 1232 close(shttp); 1233 } 1234 } 1235 } 1236 } 1237 /* delete finished HTTP connections */ 1238 for(e = upnphttphead.lh_first; e != NULL; ) 1239 { 1240 next = e->entries.le_next; 1241 if(e->state >= 100) 1242 { 1243 LIST_REMOVE(e, entries); 1244 Delete_upnphttp(e); 1245 } 1246 e = next; 1247 } 1248 } 1249 1250shutdown: 1251 /* kill the scanner */ 1252 if( scanning && scanner_pid ) 1253 { 1254 kill(scanner_pid, 9); 1255 } 1256 /* close out open sockets */ 1257 while(upnphttphead.lh_first != NULL) 1258 { 1259 e = upnphttphead.lh_first; 1260 LIST_REMOVE(e, entries); 1261 Delete_upnphttp(e); 1262 } 1263 1264 if (sudp >= 0) close(sudp); 1265 if (shttpl >= 0) close(shttpl); 1266 #ifdef TIVO_SUPPORT 1267 if (sbeacon >= 0) close(sbeacon); 1268 #endif 1269 1270 if(SendSSDPGoodbye(snotify, n_lan_addr) < 0) 1271 { 1272 DPRINTF(E_ERROR, L_GENERAL, "Failed to broadcast good-bye notifications\n"); 1273 } 1274 for(i=0; i<n_lan_addr; i++) 1275 close(snotify[i]); 1276 1277 if( inotify_thread ) 1278 pthread_join(inotify_thread, NULL); 1279 1280 sql_exec(db, "UPDATE SETTINGS set UPDATE_ID = %u", updateID); 1281 sqlite3_close(db); 1282 1283 upnpevents_removeSubscribers(); 1284 1285 media_path = media_dirs; 1286 art_names = album_art_names; 1287 while( media_path ) 1288 { 1289 free(media_path->path); 1290 last_path = media_path; 1291 media_path = media_path->next; 1292 free(last_path); 1293 } 1294 while( art_names ) 1295 { 1296 free(art_names->name); 1297 last_name = art_names; 1298 art_names = art_names->next; 1299 free(last_name); 1300 } 1301 1302 if(unlink(pidfilename) < 0) 1303 { 1304 DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno)); 1305 } 1306 1307 freeoptions(); 1308 1309 exit(EXIT_SUCCESS); 1310} 1311 1312