00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00036
00037 #ifdef HAVE_CONFIG_H
00038 #include "autoconfig.h"
00039 #endif
00040
00041 #include "common.h"
00042 #include "singleton.h"
00043 #include "server.h"
00044 #include "process.h"
00045
00046 #ifdef HAVE_GETOPT_H
00047 #include <getopt.h>
00048 #endif
00049
00050 #include "config_manager.h"
00051 #include "content_manager.h"
00052 #include "timer.h"
00053 #include "common.h"
00054
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include <stdio.h>
00058 #include <stdlib.h>
00059 #include <fcntl.h>
00060 #include <errno.h>
00061 #include <unistd.h>
00062 #include <syslog.h>
00063 #include <string.h>
00064 #include <pthread.h>
00065 #include <signal.h>
00066 #include <pwd.h>
00067 #include <grp.h>
00068 #include <limits.h>
00069
00070 #ifdef HAVE_GETOPT_LONG
00071 #define OPTSTR "i:e:p:c:m:f:u:g:a:l:P:dhD"
00072 #endif
00073
00074 using namespace zmm;
00075
00076 int shutdown_flag = 0;
00077 int restart_flag = 0;
00078 pthread_t main_thread_id;
00079 Ref<Timer> timer = nil;
00080
00081 void print_copyright()
00082 {
00083 printf("\nMediaTomb UPnP Server version %s - %s\n\n", VERSION,
00084 DESC_MANUFACTURER_URL);
00085 printf("===============================================================================\n");
00086 printf("Copyright 2005-2010 Gena Batsyan, Sergey Bostandzhyan, Leonhard Wimmer.\n");
00087 printf("MediaTomb is free software, covered by the GNU General Public License version 2\n\n");
00088 }
00089 void signal_handler(int signum);
00090
00091 int main(int argc, char **argv, char **envp)
00092 {
00093 char * err = NULL;
00094 int port = -1;
00095 bool daemon = false;
00096 struct sigaction action;
00097 sigset_t mask_set;
00098
00099 int devnull = -1;
00100 int redirect1 = -1;
00101 int redirect2 = -1;
00102 struct passwd *pwd;
00103 struct group *grp;
00104 #ifdef HAVE_GETOPT_LONG
00105 int opt_index = 0;
00106 int o;
00107 static struct option long_options[] = {
00108 {"ip", 1, 0, 'i'},
00109 {"interface", 1, 0, 'e'},
00110 {"port", 1, 0, 'p'},
00111 {"config", 1, 0, 'c'},
00112 {"home", 1, 0, 'm'},
00113 {"cfgdir", 1, 0, 'f'},
00114 {"user", 1, 0, 'u'},
00115 {"group", 1, 0, 'g'},
00116 {"daemon", 0, 0, 'd'},
00117 {"pidfile", 1, 0, 'P'},
00118 {"add", 1, 0, 'a'},
00119 {"logfile", 1, 0, 'l'},
00120 {"debug", 0, 0, 'D'},
00121 {"compile-info", 0, 0, 0},
00122 {"version", 0, 0, 0},
00123 {"help", 0, 0, 'h'},
00124 {0, 0, 0, 0}
00125 };
00126 #endif
00127
00128 String config_file;
00129 String home;
00130 String confdir;
00131 String user;
00132 String group;
00133 String pid_file;
00134 FILE* pid_fd = NULL;
00135 String interface;
00136 String ip;
00137 String prefix;
00138 String magic;
00139 bool debug_logging = false;
00140 bool print_version = false;
00141
00142 Ref<Array<StringBase> > addFile(new Array<StringBase>());
00143
00144 #ifdef SOLARIS
00145 String ld_preload;
00146 char *preload = getenv("LD_PRELOAD");
00147 if (preload != NULL)
00148 ld_preload = String(preload);
00149
00150 if ((preload == NULL) || (ld_preload.find("0@0") == -1))
00151 {
00152 printf("MediaTomb: Solaris check failed!\n");
00153 printf("Please set the environment to match glibc behaviour!\n");
00154 printf("LD_PRELOAD=/usr/lib/0@0.so.1\n");
00155 exit(EXIT_FAILURE);
00156 }
00157 #endif
00158
00159 #ifdef HAVE_GETOPT_LONG
00160 while (1)
00161 {
00162 o = getopt_long(argc, argv, OPTSTR, long_options, &opt_index);
00163 if (o == -1) break;
00164
00165 switch (o)
00166 {
00167 case 'i':
00168 log_debug("Option ip with param %s\n", optarg);
00169 ip = optarg;
00170 break;
00171
00172 case 'e':
00173 log_debug("Option interface with param %s\n", optarg);
00174 interface = optarg;
00175 break;
00176
00177 case 'p':
00178 log_debug("Option port with param %s\n", optarg);
00179 errno = 0;
00180 port = strtol(optarg, &err, 10);
00181 if ((port == 0) && (*err))
00182 {
00183 log_error("Invalid port argument: %s\n", optarg);
00184 exit(EXIT_FAILURE);
00185 }
00186
00187 if (port > USHRT_MAX)
00188 {
00189 log_error("Invalid port value %d. Maximum allowed port value is %d\n", port, USHRT_MAX);
00190 }
00191 log_debug("port set to: %d\n", port);
00192 break;
00193
00194 case 'c':
00195 log_debug("Option config with param %s\n", optarg);
00196 config_file = optarg;
00197 break;
00198
00199 case 'd':
00200 log_debug("Starting in deamon mode...");
00201 daemon = true;
00202 break;
00203
00204 case 'u':
00205 log_debug("Running as user: %s\n", optarg);
00206 user = optarg;
00207 break;
00208
00209 case 'g':
00210 log_debug("Running as group: %s\n", optarg);
00211 group = optarg;
00212 break;
00213
00214 case 'a':
00215 log_debug("Adding file/directory:: %s\n", optarg);
00216 addFile->append(String::copy(optarg));
00217 break;
00218
00219 case 'P':
00220 log_debug("Pid file: %s\n", optarg);
00221 pid_file = optarg;
00222 break;
00223
00224 case 'l':
00225 log_debug("Log file: %s\n", optarg);
00226 log_open(optarg);
00227 break;
00228
00229 case 'm':
00230 log_debug("Home setting: %s\n", optarg);
00231 home = optarg;
00232 break;
00233
00234 case 'f':
00235 log_debug("Confdir setting: %s\n", optarg);
00236 confdir = optarg;
00237 break;
00238
00239 case 'D':
00240
00241 #ifndef DEBUG_LOG_ENABLED
00242 print_copyright();
00243 printf("ERROR: MediaTomb wasn't compiled with debug output, but was run with -D/--debug.\n");
00244 exit(EXIT_FAILURE);
00245 #endif
00246 log_debug("enabling debug output\n");
00247 debug_logging = true;
00248 break;
00249 case '?':
00250 case 'h':
00251 print_copyright();
00252 printf("Usage: mediatomb [options]\n\
00253 \n\
00254 Supported options:\n\
00255 --ip or -i ip address to bind to\n\
00256 --interface or -e network interface to bind to\n\
00257 --port or -p server port (the SDK only permits values >= 49152)\n\
00258 --config or -c configuration file to use\n\
00259 --daemon or -d run server in background\n\
00260 --home or -m define the home directory\n\
00261 --cfgdir or -f name of the directory that is holding the configuration\n\
00262 --pidfile or -P file to hold the process id\n\
00263 --user or -u run server under specified username\n\
00264 --group or -g run server under specified group\n\
00265 --add or -a add the given file/directory\n\
00266 --logfile or -l log to specified file\n\
00267 --debug or -D enable debug output\n\
00268 --compile-info print configuration summary and exit\n\
00269 --version print version information and exit\n\
00270 --help or -h this help message\n\
00271 \n\
00272 For more information visit " DESC_MANUFACTURER_URL "\n\n");
00273
00274 exit(EXIT_FAILURE);
00275 break;
00276 case 0:
00277 switch (opt_index)
00278 {
00279 case 13:
00280 print_copyright();
00281 printf("Compile info:\n");
00282 printf("-------------\n");
00283 printf("%s\n\n", COMPILE_INFO);
00284 exit(EXIT_SUCCESS);
00285 break;
00286 case 14:
00287 print_version = true;
00288 break;
00289 }
00290 break;
00291 default:
00292 break;
00293 }
00294 }
00295 #else
00296 log_warning("No getopt_long() support, all command line options disabled");
00297 #endif
00298
00299 if (print_version || ! daemon)
00300 {
00301 print_copyright();
00302
00303 if (print_version)
00304 exit(EXIT_FAILURE);
00305 }
00306
00307
00308 if (pid_file != nil)
00309 {
00310 pid_fd = fopen(pid_file.c_str(), "w");
00311 if (pid_fd == NULL)
00312 {
00313 log_error("Could not write pid file %s : %s\n",
00314 pid_file.c_str(), strerror(errno));
00315 }
00316 }
00317
00318
00319
00320 if (group != nil)
00321 {
00322 grp = getgrnam(group.c_str());
00323 if (grp == NULL)
00324 {
00325 log_error("Group %s not found!\n", group.c_str());
00326 exit(EXIT_FAILURE);
00327 }
00328
00329 if (setgid(grp->gr_gid) < 0)
00330 {
00331 log_error("setgid failed %s\n", strerror(errno));
00332 exit(EXIT_FAILURE);
00333 }
00334
00335
00336 if (setgroups(0, NULL) < 0)
00337 {
00338 log_error("setgroups failed %s\n", strerror(errno));
00339 exit(EXIT_FAILURE);
00340 }
00341 }
00342
00343 if (user != nil)
00344 {
00345 pwd = getpwnam(user.c_str());
00346 if (pwd == NULL)
00347 {
00348 log_error("User %s not found!\n", user.c_str());
00349 exit(EXIT_FAILURE);
00350 }
00351
00352
00353 if (initgroups(user.c_str(), getegid()) < 0)
00354 {
00355 log_error("initgroups failed %s\n", strerror(errno));
00356 exit(EXIT_FAILURE);
00357 }
00358
00359 if (setuid(pwd->pw_uid) < 0)
00360 {
00361 log_error("setuid failed %s\n", strerror(errno));
00362 exit(EXIT_FAILURE);
00363 }
00364
00365 }
00366
00367 try
00368 {
00369
00370 if (!string_ok(home) && (!string_ok(config_file)))
00371 {
00372 #ifndef __CYGWIN__
00373 char *h = getenv("HOME");
00374 if (h != NULL)
00375 home = h;
00376
00377 #else
00378 char *h = getenv("HOMEPATH");
00379 char *d = getenv("HOMEDRIVE");
00380 if ((h != NULL) && (d != NULL))
00381 home = d + h;
00382
00383 #endif // __CYGWIN__
00384 }
00385
00386 if (!string_ok(home) && (!string_ok(config_file)))
00387 {
00388 log_error("Could not determine users home directory\n");
00389 exit(EXIT_FAILURE);
00390 }
00391
00392 if (!string_ok(confdir))
00393 confdir = _(DEFAULT_CONFIG_HOME);
00394
00395 char *pref = getenv("MEDIATOMB_DATADIR");
00396 if (pref != NULL)
00397 prefix = pref;
00398
00399 if (!string_ok(prefix))
00400 prefix = _(PACKAGE_DATADIR);
00401
00402 char *mgc = getenv("MEDIATOMB_MAGIC_FILE");
00403 if (mgc != NULL)
00404 magic = mgc;
00405
00406 if (!string_ok(magic))
00407 magic = nil;
00408
00409 ConfigManager::setStaticArgs(config_file, home, confdir, prefix, magic, debug_logging);
00410 ConfigManager::getInstance();
00411 }
00412 catch (mxml::ParseException pe)
00413 {
00414 log_error("Error parsing config file: %s line %d:\n%s\n",
00415 pe.context->location.c_str(),
00416 pe.context->line,
00417 pe.getMessage().c_str());
00418 exit(EXIT_FAILURE);
00419 }
00420 catch (Exception e)
00421 {
00422 log_error("%s\n", e.getMessage().c_str());
00423 exit(EXIT_FAILURE);
00424 }
00425
00426
00427 if (daemon)
00428 {
00429
00430
00431
00432
00433
00434 pid_t pid, sid;
00435
00436
00437
00438 pid = fork();
00439 if (pid < 0)
00440 {
00441 log_error("Failed to fork: %s\n", strerror(errno));
00442 exit(EXIT_FAILURE);
00443 }
00444
00445
00446 if (pid > 0)
00447 {
00448 exit(EXIT_SUCCESS);
00449 }
00450
00451
00452 umask(0133);
00453
00454
00455
00456
00457 sid = setsid();
00458 if (sid < 0)
00459 {
00460 log_error("setsid failed: %s\n", strerror(errno));
00461 exit(EXIT_FAILURE);
00462 }
00463
00464
00465 if ((chdir("/")) < 0)
00466 {
00467 log_error("Failed to chdir to / : %s\n", strerror(errno));
00468 exit(EXIT_FAILURE);
00469 }
00470
00471
00472 close(STDIN_FILENO);
00473 close(STDOUT_FILENO);
00474 close(STDERR_FILENO);
00475
00476 devnull = open("/dev/null", O_RDWR);
00477 if (devnull == -1)
00478 {
00479 log_error("Failed to open /dev/null: %s\n", strerror(errno));
00480 exit(EXIT_FAILURE);
00481 }
00482
00483 redirect1 = dup(devnull);
00484 if (redirect1 == -1)
00485 {
00486 log_error("Failed to redirect output: %s\n", strerror(errno));
00487 close(devnull);
00488 exit(EXIT_FAILURE);
00489 }
00490
00491 redirect2 = dup(devnull);
00492 if (redirect2 == -1)
00493 {
00494 log_error("Failed to redirect output: %s\n", strerror(errno));
00495 close(devnull);
00496 close(redirect1);
00497 exit(EXIT_FAILURE);
00498 }
00499
00500 }
00501
00502 if (pid_fd != NULL)
00503 {
00504 pid_t cur_pid;
00505 int size;
00506
00507 cur_pid = getpid();
00508 String pid = String::from(cur_pid);
00509
00510 size = fwrite(pid.c_str(), sizeof(char), pid.length(), pid_fd);
00511 fclose(pid_fd);
00512
00513 if (size < pid.length())
00514 log_error("Error when writing pid file %s : %s\n",
00515 pid_file.c_str(), strerror(errno));
00516 }
00517
00518 main_thread_id = pthread_self();
00519
00520 sigfillset(&mask_set);
00521 pthread_sigmask(SIG_SETMASK, &mask_set, NULL);
00522
00523 memset(&action, 0, sizeof(action));
00524 action.sa_handler = signal_handler;
00525 action.sa_flags = 0;
00526 sigfillset(&action.sa_mask);
00527 if (sigaction(SIGINT, &action, NULL) < 0)
00528 {
00529 log_error("Could not register SIGINT handler!\n");
00530 }
00531
00532 if (sigaction(SIGTERM, &action, NULL) < 0)
00533 {
00534 log_error("Could not register SIGTERM handler!\n");
00535 }
00536
00537 if (sigaction(SIGHUP, &action, NULL) < 0)
00538 {
00539 log_error("Could not register SIGHUP handler!\n");
00540 }
00541
00542 if (sigaction(SIGPIPE, &action, NULL) < 0)
00543 {
00544 log_error("Could not register SIGPIPE handler!\n");
00545 }
00546
00547 Ref<SingletonManager> singletonManager = SingletonManager::getInstance();
00548 Ref<Server> server;
00549 try
00550 {
00551 server = Server::getInstance();
00552 server->upnp_init(interface, ip, port);
00553 }
00554 catch(UpnpException upnp_e)
00555 {
00556
00557 sigemptyset(&mask_set);
00558 pthread_sigmask(SIG_SETMASK, &mask_set, NULL);
00559
00560 upnp_e.printStackTrace();
00561 log_error("main: upnp error %d\n", upnp_e.getErrorCode());
00562 if (upnp_e.getErrorCode() == UPNP_E_SOCKET_BIND)
00563 {
00564 log_error("Could not bind to socket.\n");
00565 log_info("Please check if another instance of MediaTomb or\n");
00566 log_info("another application is running on the same port.\n");
00567 }
00568 else if (upnp_e.getErrorCode() == UPNP_E_SOCKET_ERROR)
00569 {
00570 log_error("Socket error.\n");
00571 log_info("Please check if your network interface was configured for multicast!\n");
00572 log_info("Refer to the README file for more information.\n");
00573 }
00574
00575 try
00576 {
00577 singletonManager->shutdown(true);
00578 }
00579 catch (Exception e)
00580 {
00581 log_error("%s\n", e.getMessage().c_str());
00582 e.printStackTrace();
00583 }
00584 if (daemon)
00585 {
00586 close(devnull);
00587 close(redirect1);
00588 close(redirect2);
00589 }
00590 exit(EXIT_FAILURE);
00591 }
00592 catch (Exception e)
00593 {
00594 log_error("%s\n", e.getMessage().c_str());
00595 e.printStackTrace();
00596 if (daemon)
00597 close(devnull);
00598 exit(EXIT_FAILURE);
00599 }
00600
00601 if (addFile->size() > 0)
00602 {
00603 for (int i = 0; i < addFile->size(); i++)
00604 {
00605 try
00606 {
00607
00608 log_info("Adding %s\n", String(addFile->get(i)).c_str());
00609 ContentManager::getInstance()->addFile(String(addFile->get(i)),
00610 true, true,
00611 ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_HIDDEN_FILES));
00612 }
00613 catch (Exception e)
00614 {
00615 e.printStackTrace();
00616 if (daemon)
00617 {
00618 close(devnull);
00619 close(redirect1);
00620 close(redirect2);
00621 }
00622 exit(EXIT_FAILURE);
00623 }
00624 }
00625 }
00626
00627 sigemptyset(&mask_set);
00628 pthread_sigmask(SIG_SETMASK, &mask_set, NULL);
00629
00630
00631 while (!shutdown_flag)
00632 {
00633
00634
00635
00636 if (timer == nil)
00637 timer = Timer::getInstance();
00638
00639 timer->triggerWait();
00640
00641 if (restart_flag != 0)
00642 {
00643 log_info("Restarting MediaTomb!\n");
00644 try
00645 {
00646 server = nil;
00647 timer = nil;
00648
00649 singletonManager->shutdown(true);
00650 singletonManager = nil;
00651 singletonManager = SingletonManager::getInstance();
00652
00653 try
00654 {
00655 ConfigManager::setStaticArgs(config_file, home, confdir,
00656 prefix, magic);
00657 ConfigManager::getInstance();
00658 }
00659 catch (mxml::ParseException pe)
00660 {
00661 log_error("Error parsing config file: %s line %d:\n%s\n",
00662 pe.context->location.c_str(),
00663 pe.context->line,
00664 pe.getMessage().c_str());
00665 log_error("Could not restart MediaTomb\n");
00666
00667
00668 exit(EXIT_FAILURE);
00669 }
00670 catch (Exception e)
00671 {
00672 log_error("Error reloading configuration: %s\n",
00673 e.getMessage().c_str());
00674 e.printStackTrace();
00675 if (daemon)
00676 {
00677 close(devnull);
00678 close(redirect1);
00679 close(redirect2);
00680 }
00681 exit(EXIT_FAILURE);
00682 }
00683
00685 server = Server::getInstance();
00686 server->upnp_init(interface, ip, port);
00687
00688 restart_flag = 0;
00689 }
00690 catch(Exception e)
00691 {
00692 restart_flag = 0;
00693 shutdown_flag = 1;
00694 sigemptyset(&mask_set);
00695 pthread_sigmask(SIG_SETMASK, &mask_set, NULL);
00696 log_error("Could not restart MediaTomb\n");
00697 }
00698 }
00699 }
00700
00701
00702 int ret = EXIT_SUCCESS;
00703 try
00704 {
00705 singletonManager->shutdown(true);
00706 }
00707 catch(UpnpException upnp_e)
00708 {
00709 log_error("main: upnp error %d\n", upnp_e.getErrorCode());
00710 ret = EXIT_FAILURE;
00711 }
00712 catch (Exception e)
00713 {
00714 e.printStackTrace();
00715 ret = EXIT_FAILURE;
00716 }
00717
00718 log_info("Server terminating\n");
00719 log_close();
00720
00721 if (daemon)
00722 {
00723 close(devnull);
00724 close(redirect1);
00725 close(redirect2);
00726 }
00727
00728 exit(ret);
00729 }
00730
00731 void signal_handler(int signum)
00732 {
00733
00734 if (main_thread_id != pthread_self())
00735 {
00736 return;
00737 }
00738
00739 if ((signum == SIGINT) || (signum == SIGTERM))
00740 {
00741 shutdown_flag++;
00742 if (shutdown_flag == 1)
00743 log_info("MediaTomb shutting down. Please wait...\n");
00744 else if (shutdown_flag == 2)
00745 log_info("Mediatomb still shutting down, signal again to kill.\n");
00746 else if (shutdown_flag > 2)
00747 {
00748 log_error("Clean shutdown failed, killing MediaTomb!\n");
00749 exit(1);
00750 }
00751 if (timer != nil)
00752 timer->signal();
00753 }
00754 else if (signum == SIGHUP)
00755 {
00756 #if defined(ENABLE_SIGHUP)
00757 restart_flag = 1;
00758 if (timer != nil)
00759 timer->signal();
00760 #else
00761 log_warning("SIGHUP handling was disabled during compilation.\n");
00762 #endif
00763 }
00764
00765 return;
00766 }