35 #include <sys/capability.h>
36 #include <sys/prctl.h>
70 #define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
71 #define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
72 #define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
73 #define SHUTDOWNRETRY 360 // seconds before trying again to shut down
74 #define SHUTDOWNFORCEPROMPT 5 // seconds to wait in user prompt to allow forcing shutdown
75 #define SHUTDOWNCANCELPROMPT 5 // seconds to wait in user prompt to allow canceling shutdown
76 #define RESTARTCANCELPROMPT 5 // seconds to wait in user prompt before restarting on SIGHUP
77 #define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
78 #define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
79 #define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
80 #define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
81 #define TIMERCHECKDELTA 10 // seconds between checks for timers that need to see their channel
82 #define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused
83 #define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible
84 #define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date
85 #define VPSUPTODATETIME 3600 // seconds before the event or schedule of a VPS timer needs to be refreshed
87 #define EXIT(v) { ShutdownHandler.Exit(v); goto Exit; }
91 static bool SetUser(
const char *UserName,
bool UserDump)
94 struct passwd *user = getpwnam(UserName);
96 fprintf(stderr,
"vdr: unknown user: '%s'\n", UserName);
99 if (setgid(user->pw_gid) < 0) {
100 fprintf(stderr,
"vdr: cannot set group id %u: %s\n", (
unsigned int)user->pw_gid, strerror(errno));
103 if (initgroups(user->pw_name, user->pw_gid) < 0) {
104 fprintf(stderr,
"vdr: cannot set supplemental group ids for user %s: %s\n", user->pw_name, strerror(errno));
107 if (setuid(user->pw_uid) < 0) {
108 fprintf(stderr,
"vdr: cannot set user id %u: %s\n", (
unsigned int)user->pw_uid, strerror(errno));
111 if (UserDump && prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0)
112 fprintf(stderr,
"vdr: warning - cannot set dumpable: %s\n", strerror(errno));
113 setenv(
"HOME", user->pw_dir, 1);
114 setenv(
"USER", user->pw_name, 1);
115 setenv(
"LOGNAME", user->pw_name, 1);
116 setenv(
"SHELL", user->pw_shell, 1);
124 cap_t caps = cap_from_text(
"= cap_sys_nice,cap_sys_time,cap_net_raw=ep");
126 fprintf(stderr,
"vdr: cap_from_text failed: %s\n", strerror(errno));
129 if (cap_set_proc(caps) == -1) {
130 fprintf(stderr,
"vdr: cap_set_proc failed: %s\n", strerror(errno));
141 if (prctl(PR_SET_KEEPCAPS, On ? 1 : 0, 0, 0, 0) != 0) {
142 fprintf(stderr,
"vdr: prctl failed\n");
168 esyslog(
"PANIC: watchdog timer expired - exiting!");
172 int main(
int argc,
char *argv[])
176 struct termios savedTm;
177 bool HasStdin = (tcgetpgrp(STDIN_FILENO) == getpid() || getppid() != (pid_t)1) && tcgetattr(STDIN_FILENO, &savedTm) == 0;
181 setlocale(LC_ALL,
"");
185 #define dd(a, b) (*a ? a : b)
186 #define DEFAULTSVDRPPORT 6419
187 #define DEFAULTWATCHDOG 0 // seconds
188 #define DEFAULTVIDEODIR VIDEODIR
189 #define DEFAULTCONFDIR dd(CONFDIR, VideoDirectory)
190 #define DEFAULTCACHEDIR dd(CACHEDIR, VideoDirectory)
191 #define DEFAULTRESDIR dd(RESDIR, ConfigDirectory)
192 #define DEFAULTPLUGINDIR PLUGINDIR
193 #define DEFAULTLOCDIR LOCDIR
194 #define DEFAULTEPGDATAFILENAME "epg.data"
196 bool StartedAsRoot =
false;
197 const char *VdrUser = NULL;
198 bool UserDump =
false;
200 const char *AudioCommand = NULL;
202 const char *ConfigDirectory = NULL;
203 const char *CacheDirectory = NULL;
204 const char *ResourceDirectory = NULL;
207 bool DisplayHelp =
false;
208 bool DisplayVersion =
false;
209 bool DaemonMode =
false;
210 int SysLogTarget = LOG_USER;
211 bool MuteAudio =
false;
213 const char *Terminal = NULL;
216 const char *LircDevice = NULL;
217 #if !defined(REMOTE_KBD)
220 #if defined(REMOTE_LIRC)
221 LircDevice = LIRC_DEVICE;
223 #if defined(VDR_USER)
229 static struct option long_options[] = {
230 {
"audio", required_argument, NULL,
'a' },
231 {
"cachedir", required_argument, NULL,
'c' | 0x100 },
232 {
"config", required_argument, NULL,
'c' },
233 {
"daemon", no_argument, NULL,
'd' },
234 {
"device", required_argument, NULL,
'D' },
235 {
"dirnames", required_argument, NULL,
'd' | 0x100 },
236 {
"edit", required_argument, NULL,
'e' | 0x100 },
237 {
"epgfile", required_argument, NULL,
'E' },
238 {
"filesize", required_argument, NULL,
'f' | 0x100 },
239 {
"genindex", required_argument, NULL,
'g' | 0x100 },
240 {
"grab", required_argument, NULL,
'g' },
241 {
"help", no_argument, NULL,
'h' },
242 {
"instance", required_argument, NULL,
'i' },
243 {
"lib", required_argument, NULL,
'L' },
244 {
"lirc", optional_argument, NULL,
'l' | 0x100 },
245 {
"localedir",required_argument, NULL,
'l' | 0x200 },
246 {
"log", required_argument, NULL,
'l' },
247 {
"mute", no_argument, NULL,
'm' },
248 {
"no-kbd", no_argument, NULL,
'n' | 0x100 },
249 {
"plugin", required_argument, NULL,
'P' },
250 {
"port", required_argument, NULL,
'p' },
251 {
"record", required_argument, NULL,
'r' },
252 {
"resdir", required_argument, NULL,
'r' | 0x100 },
253 {
"shutdown", required_argument, NULL,
's' },
254 {
"split", no_argument, NULL,
's' | 0x100 },
255 {
"terminal", required_argument, NULL,
't' },
256 {
"user", required_argument, NULL,
'u' },
257 {
"userdump", no_argument, NULL,
'u' | 0x100 },
258 {
"version", no_argument, NULL,
'V' },
259 {
"vfat", no_argument, NULL,
'v' | 0x100 },
260 {
"video", required_argument, NULL,
'v' },
261 {
"watchdog", required_argument, NULL,
'w' },
262 { NULL, no_argument, NULL, 0 }
266 while ((c = getopt_long(argc, argv,
"a:c:dD:e:E:g:hi:l:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
268 case 'a': AudioCommand = optarg;
271 CacheDirectory = optarg;
273 case 'c': ConfigDirectory = optarg;
275 case 'd': DaemonMode =
true;
278 int n = atoi(optarg);
284 fprintf(stderr,
"vdr: invalid DVB device number: %s\n", optarg);
289 int n = strtol(s, &s, 10);
290 if (n <= 0 || n >= PATH_MAX) {
291 fprintf(stderr,
"vdr: invalid directory path length: %s\n", optarg);
298 fprintf(stderr,
"vdr: invalid delimiter: %s\n", optarg);
306 int n = strtol(s, &s, 10);
307 if (n <= 0 || n > NAME_MAX) {
308 fprintf(stderr,
"vdr: invalid directory name length: %s\n", optarg);
315 fprintf(stderr,
"vdr: invalid delimiter: %s\n", optarg);
322 int n = strtol(s, &s, 10);
323 if (n != 0 && n != 1) {
324 fprintf(stderr,
"vdr: invalid directory encoding: %s\n", optarg);
329 fprintf(stderr,
"vdr: unexpected data: %s\n", optarg);
336 case 'E': EpgDataFileName = (*optarg !=
'-' ? optarg : NULL);
349 case 'h': DisplayHelp =
true;
356 fprintf(stderr,
"vdr: invalid instance id: %s\n", optarg);
359 char *p = strchr(optarg,
'.');
363 int l = atoi(optarg);
364 if (0 <= l && l <= 3) {
370 if (0 <= l && l <= 7) {
371 int targets[] = { LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7 };
372 SysLogTarget = targets[l];
380 fprintf(stderr,
"vdr: invalid log level: %s\n", optarg);
383 case 'L':
if (access(optarg, R_OK | X_OK) == 0)
386 fprintf(stderr,
"vdr: can't access plugin directory: %s\n", optarg);
391 LircDevice = optarg ? optarg : LIRC_DEVICE;
394 if (access(optarg, R_OK | X_OK) == 0)
395 LocaleDirectory = optarg;
397 fprintf(stderr,
"vdr: can't access locale directory: %s\n", optarg);
401 case 'm': MuteAudio =
true;
407 SVDRPport = atoi(optarg);
409 fprintf(stderr,
"vdr: invalid port number: %s\n", optarg);
413 case 'P': PluginManager.
AddPlugin(optarg);
418 ResourceDirectory = optarg;
425 case 't': Terminal = optarg;
426 if (access(Terminal, R_OK | W_OK) < 0) {
427 fprintf(stderr,
"vdr: can't access terminal: %s\n", Terminal);
431 case 'u':
if (*optarg)
437 case 'V': DisplayVersion =
true;
444 case 'v': VideoDirectory = optarg;
445 while (optarg && *optarg && optarg[strlen(optarg) - 1] ==
'/')
446 optarg[strlen(optarg) - 1] = 0;
449 int t = atoi(optarg);
455 fprintf(stderr,
"vdr: invalid watchdog timeout: %s\n", optarg);
463 if (VdrUser && geteuid() == 0) {
464 StartedAsRoot =
true;
465 if (strcmp(VdrUser,
"root")) {
468 if (!
SetUser(VdrUser, UserDump))
479 if (DisplayHelp || DisplayVersion) {
484 printf(
"Usage: vdr [OPTIONS]\n\n"
485 " -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
486 " --cachedir=DIR save cache files in DIR (default: %s)\n"
487 " -c DIR, --config=DIR read config files from DIR (default: %s)\n"
488 " -d, --daemon run in daemon mode\n"
489 " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
490 " there may be several -D options (default: all DVB\n"
491 " devices will be used)\n"
492 " --dirnames=PATH[,NAME[,ENC]]\n"
493 " set the maximum directory path length to PATH\n"
494 " (default: %d); if NAME is also given, it defines\n"
495 " the maximum directory name length (default: %d);\n"
496 " the optional ENC can be 0 or 1, and controls whether\n"
497 " special characters in directory names are encoded as\n"
498 " hex values (default: 0); if PATH or NAME are left\n"
499 " empty (as in \",,1\" to only set ENC), the defaults\n"
501 " --edit=REC cut recording REC and exit\n"
502 " -E FILE, --epgfile=FILE write the EPG data into the given FILE (default is\n"
503 " '%s' in the cache directory)\n"
504 " '-E-' disables this\n"
505 " if FILE is a directory, the default EPG file will be\n"
506 " created in that directory\n"
507 " --filesize=SIZE limit video files to SIZE bytes (default is %dM)\n"
508 " only useful in conjunction with --edit\n"
509 " --genindex=REC generate index for recording REC and exit\n"
510 " -g DIR, --grab=DIR write images from the SVDRP command GRAB into the\n"
511 " given DIR; DIR must be the full path name of an\n"
512 " existing directory, without any \"..\", double '/'\n"
513 " or symlinks (default: none, same as -g-)\n"
514 " -h, --help print this help and exit\n"
515 " -i ID, --instance=ID use ID as the id of this VDR instance (default: 0)\n"
516 " -l LEVEL, --log=LEVEL set log level (default: 3)\n"
517 " 0 = no logging, 1 = errors only,\n"
518 " 2 = errors and info, 3 = errors, info and debug\n"
519 " if logging should be done to LOG_LOCALn instead of\n"
520 " LOG_USER, add '.n' to LEVEL, as in 3.7 (n=0..7)\n"
521 " -L DIR, --lib=DIR search for plugins in DIR (default is %s)\n"
522 " --lirc[=PATH] use a LIRC remote control device, attached to PATH\n"
524 " --localedir=DIR search for locale files in DIR (default is\n"
526 " -m, --mute mute audio of the primary DVB device at startup\n"
527 " --no-kbd don't use the keyboard as an input device\n"
528 " -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
529 " 0 turns off SVDRP\n"
530 " -P OPT, --plugin=OPT load a plugin defined by the given options\n"
531 " -r CMD, --record=CMD call CMD before and after a recording, and after\n"
532 " a recording has been edited or deleted\n"
533 " --resdir=DIR read resource files from DIR (default: %s)\n"
534 " -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
535 " --split split edited files at the editing marks (only\n"
536 " useful in conjunction with --edit)\n"
537 " -t TTY, --terminal=TTY controlling tty\n"
538 " -u USER, --user=USER run as user USER; only applicable if started as\n"
540 " --userdump allow coredumps if -u is given (debugging)\n"
541 " -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
542 " -V, --version print version information and exit\n"
543 " --vfat for backwards compatibility (same as\n"
544 " --dirnames=250,40,1\n"
545 " -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
546 " seconds (default: %d); '0' disables the watchdog\n"
567 printf(
"Plugins: vdr -P\"name [OPTIONS]\"\n\n");
568 for (
int i = 0; ; i++) {
573 if (DisplayHelp && help) {
588 openlog(
"vdr", LOG_CONS, SysLogTarget);
593 fprintf(stderr,
"vdr: can't access video directory %s\n", VideoDirectory);
600 if (daemon(1, 0) == -1) {
601 fprintf(stderr,
"vdr: %m\n");
608 stdin = freopen(Terminal,
"r", stdin);
609 stdout = freopen(Terminal,
"w", stdout);
610 stderr = freopen(Terminal,
"w", stderr);
612 tcgetattr(STDIN_FILENO, &savedTm);
616 if (StartedAsRoot && VdrUser)
617 isyslog(
"switched to user '%s'", VdrUser);
624 char *CodeSet = NULL;
625 if (setlocale(LC_CTYPE,
""))
626 CodeSet = nl_langinfo(CODESET);
628 char *LangEnv = getenv(
"LANG");
630 CodeSet = strchr(LangEnv,
'.');
637 isyslog(
"codeset is '%s' - %s", CodeSet, known ?
"known" :
"unknown");
650 int LastTimerChannel = -1;
651 int PreviousChannel[2] = { 1, 1 };
652 int PreviousChannelIndex = 0;
653 time_t LastChannelChanged = time(NULL);
654 time_t LastInteract = 0;
655 int MaxLatencyTime = 0;
656 bool InhibitEpgScan =
false;
657 bool IsInfoMenu =
false;
658 cSkin *CurrentSkin = NULL;
668 if (!ConfigDirectory)
674 if (!ResourceDirectory)
696 const char *msg =
"no fonts available - OSD will not show any text!";
697 fprintf(stderr,
"vdr: %s\n", msg);
708 if (EpgDataFileName) {
709 const char *EpgDirectory = NULL;
711 EpgDirectory = EpgDataFileName;
714 else if (*EpgDataFileName !=
'/' && *EpgDataFileName !=
'.')
715 EpgDirectory = CacheDirectory;
720 EpgDataReader.
Start();
742 isyslog(
"trying device number %d instead", i + 1);
750 const char *msg =
"no primary device found - using first device!";
751 fprintf(stderr,
"vdr: %s\n", msg);
756 const char *msg =
"no primary device found - giving up!";
757 fprintf(stderr,
"vdr: %s\n", msg);
796 if (!DaemonMode && HasStdin && UseKbd)
827 if (signal(SIGHUP,
SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
828 if (signal(SIGINT,
SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN);
829 if (signal(SIGTERM,
SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
830 if (signal(SIGPIPE,
SignalHandler) == SIG_IGN) signal(SIGPIPE, SIG_IGN);
831 if (WatchdogTimeout > 0)
832 if (signal(SIGALRM,
Watchdog) == SIG_IGN) signal(SIGALRM, SIG_IGN);
836 if (WatchdogTimeout > 0) {
837 dsyslog(
"setting watchdog timer to %d seconds", WatchdogTimeout);
838 alarm(WatchdogTimeout);
843 #define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
846 #ifdef DEBUGRINGBUFFERS
847 cRingBufferLinear::PrintDebugRBL();
852 time_t Now = time(NULL);
856 static time_t lastTime = 0;
860 if (Channel && (Channel->
Vpid() || Channel->
Apid(0) || Channel->
Dpid(0))) {
863 else if (LastTimerChannel > 0) {
870 LastTimerChannel = -1;
878 static time_t lastOsdSizeUpdate = 0;
879 if (Now != lastOsdSizeUpdate) {
881 lastOsdSizeUpdate = Now;
885 if (WatchdogTimeout > 0) {
886 int LatencyTime = WatchdogTimeout - alarm(WatchdogTimeout);
887 if (LatencyTime > MaxLatencyTime) {
888 MaxLatencyTime = LatencyTime;
889 dsyslog(
"max. latency time %d seconds", MaxLatencyTime);
895 static time_t ChannelSaveTimeout = 0;
896 static int TimerState = 0;
900 ChannelSaveTimeout = 1;
901 else if (modified && !ChannelSaveTimeout)
903 bool timeout = ChannelSaveTimeout == 1 || ChannelSaveTimeout && Now > ChannelSaveTimeout && !
cRecordControls::Active();
904 if ((modified || timeout) &&
Channels.
Lock(
false, 100)) {
908 ChannelSaveTimeout = 0;
916 isyslog(
"retuning due to modification of channel %d", Channel->Number());
931 LastChannelChanged = Now;
933 if (Now - LastChannelChanged >=
Setup.
ZapTimeout && LastChannel != PreviousChannel[PreviousChannelIndex])
934 PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel;
951 static time_t LastTimerCheck = 0;
953 InhibitEpgScan =
false;
955 bool InVpsMargin =
false;
956 bool NeedsTransponder =
false;
963 else if (Timer->
Event()) {
972 InVpsMargin = !Schedule;
976 InhibitEpgScan |= InVpsMargin | NeedsTransponder;
981 if (NeedsTransponder || InVpsMargin) {
984 if (!Device && InVpsMargin)
1001 LastTimerCheck = Now;
1032 bool WasOpen = Interact != NULL;
1033 bool WasMenu = Interact && Interact->
IsMenu();
1071 #define DirectMainFunction(function)\
1073 if (cControl::Control())\
1074 cControl::Control()->Hide();\
1075 Menu = new cMenuMain(function);\
1076 key = kNone; } // nobody else needs to see this key
1097 esyslog(
"ERROR: unknown plugin '%s'", PluginName);
1184 isyslog(
"Power button pressed");
1218 if (state ==
osEnd) {
1261 case osEnd:
if (Interact == Menu)
1279 if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel || LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1])
1280 PreviousChannelIndex ^= 1;
1304 case kOk: LastChannel = -1;
break;
1318 if (!InhibitEpgScan)
1383 esyslog(
"emergency exit requested - shutting down");
1388 signal(SIGHUP, SIG_DFL);
1389 signal(SIGINT, SIG_DFL);
1390 signal(SIGTERM, SIG_DFL);
1391 signal(SIGPIPE, SIG_DFL);
1392 signal(SIGALRM, SIG_DFL);
1416 if (WatchdogTimeout > 0)
1417 dsyslog(
"max. latency time %d seconds", MaxLatencyTime);
1426 tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);