vdr.c 65.5 KB
Newer Older
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1 2 3
/*
 * vdr.c: Video Disk Recorder main program
 *
4
 * Copyright (C) 2000-2018 Klaus Schmidinger
Klaus Schmidinger's avatar
Klaus Schmidinger committed
5
 *
Klaus Schmidinger's avatar
Klaus Schmidinger committed
6 7 8 9
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
Klaus Schmidinger's avatar
Klaus Schmidinger committed
10
 *
Klaus Schmidinger's avatar
Klaus Schmidinger committed
11 12 13 14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
Klaus Schmidinger's avatar
Klaus Schmidinger committed
15
 *
Klaus Schmidinger's avatar
Klaus Schmidinger committed
16 17
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Klaus Schmidinger's avatar
Klaus Schmidinger committed
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Klaus Schmidinger's avatar
Klaus Schmidinger committed
19
 * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
Klaus Schmidinger's avatar
Klaus Schmidinger committed
20
 *
etobi's avatar
etobi committed
21
 * The author can be reached at vdr@tvdr.de
Klaus Schmidinger's avatar
Klaus Schmidinger committed
22
 *
Klaus Schmidinger's avatar
Klaus Schmidinger committed
23
 * The project's page is at http://www.tvdr.de
Klaus Schmidinger's avatar
Klaus Schmidinger committed
24
 *
25
 * $Id: vdr.c 4.25.1.5 2019/05/23 10:02:45 kls Exp $
Klaus Schmidinger's avatar
Klaus Schmidinger committed
26 27
 */

Klaus Schmidinger's avatar
Klaus Schmidinger committed
28
#include <getopt.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
29
#include <grp.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
30
#include <langinfo.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
31
#include <locale.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
32
#include <pwd.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
33
#include <signal.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
34
#include <stdlib.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
35 36
#include <sys/capability.h>
#include <sys/prctl.h>
etobi's avatar
etobi committed
37 38 39
#ifdef SDNOTIFY
#include <systemd/sd-daemon.h>
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
40
#include <termios.h>
Klaus Schmidinger's avatar
Klaus Schmidinger committed
41
#include <unistd.h>
etobi's avatar
etobi committed
42
#include "args.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
43
#include "audio.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
44
#include "channels.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
45
#include "config.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
46
#include "cutter.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
47
#include "device.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
48
#include "diseqc.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
49
#include "dvbdevice.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
50
#include "eitscan.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
51
#include "epg.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
52
#include "i18n.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
53
#include "interface.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
54
#include "keys.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
55
#include "libsi/si.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
56
#include "lirc.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
57
#include "menu.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
58
#include "osdbase.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
59
#include "plugin.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
60
#include "recording.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
61
#include "shutdown.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
62
#include "skinclassic.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
63
#include "skinlcars.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
64
#include "skinsttng.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
65
#include "sourceparams.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
66
#include "sources.h"
etobi's avatar
etobi committed
67
#include "status.h"
68
#include "svdrp.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
69
#include "themes.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
70
#include "timers.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
71
#include "tools.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
72
#include "transfer.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
73
#include "videodir.h"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
74

Klaus Schmidinger's avatar
Klaus Schmidinger committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
#define MINCHANNELWAIT        10 // seconds to wait between failed channel switchings
#define ACTIVITYTIMEOUT       60 // seconds before starting housekeeping
#define SHUTDOWNWAIT         300 // seconds to wait in user prompt before automatic shutdown
#define SHUTDOWNRETRY        360 // seconds before trying again to shut down
#define SHUTDOWNFORCEPROMPT    5 // seconds to wait in user prompt to allow forcing shutdown
#define SHUTDOWNCANCELPROMPT   5 // seconds to wait in user prompt to allow canceling shutdown
#define RESTARTCANCELPROMPT    5 // seconds to wait in user prompt before restarting on SIGHUP
#define MANUALSTART          600 // seconds the next timer must be in the future to assume manual start
#define CHANNELSAVEDELTA     600 // seconds before saving channels.conf after automatic modifications
#define DEVICEREADYTIMEOUT    30 // seconds to wait until all devices are ready
#define MENUTIMEOUT          120 // seconds of user inactivity after which an OSD display is closed
#define TIMERCHECKDELTA       10 // seconds between checks for timers that need to see their channel
#define TIMERDEVICETIMEOUT     8 // seconds before a device used for timer check may be reused
#define TIMERLOOKAHEADTIME    60 // seconds before a non-VPS timer starts and the channel is switched if possible
#define VPSLOOKAHEADTIME      24 // hours within which VPS timers will make sure their events are up to date
#define VPSUPTODATETIME     3600 // seconds before the event or schedule of a VPS timer needs to be refreshed
Klaus Schmidinger's avatar
Klaus Schmidinger committed
91

Klaus Schmidinger's avatar
Klaus Schmidinger committed
92
#define EXIT(v) { ShutdownHandler.Exit(v); goto Exit; }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
93

Klaus Schmidinger's avatar
Klaus Schmidinger committed
94
static int LastSignal = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
95

96
static bool SetUser(const char *User, bool UserDump)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
97
{
98 99
  if (User) {
     struct passwd *user = isnumber(User) ? getpwuid(atoi(User)) : getpwnam(User);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
100
     if (!user) {
101
        fprintf(stderr, "vdr: unknown user: '%s'\n", User);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115
        return false;
        }
     if (setgid(user->pw_gid) < 0) {
        fprintf(stderr, "vdr: cannot set group id %u: %s\n", (unsigned int)user->pw_gid, strerror(errno));
        return false;
        }
     if (initgroups(user->pw_name, user->pw_gid) < 0) {
        fprintf(stderr, "vdr: cannot set supplemental group ids for user %s: %s\n", user->pw_name, strerror(errno));
        return false;
        }
     if (setuid(user->pw_uid) < 0) {
        fprintf(stderr, "vdr: cannot set user id %u: %s\n", (unsigned int)user->pw_uid, strerror(errno));
        return false;
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
116
     if (UserDump && prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
117
        fprintf(stderr, "vdr: warning - cannot set dumpable: %s\n", strerror(errno));
etobi's avatar
etobi committed
118 119 120 121
     setenv("HOME", user->pw_dir, 1);
     setenv("USER", user->pw_name, 1);
     setenv("LOGNAME", user->pw_name, 1);
     setenv("SHELL", user->pw_shell, 1);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
122 123 124 125
     }
  return true;
}

Klaus Schmidinger's avatar
Klaus Schmidinger committed
126
static bool DropCaps(void)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
127
{
Klaus Schmidinger's avatar
Klaus Schmidinger committed
128
  // drop all capabilities except selected ones
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
  cap_t caps_all = cap_get_proc();
  if (!caps_all) {
     fprintf(stderr, "vdr: cap_get_proc failed: %s\n", strerror(errno));
     return false;
     }
  char *caps_text = cap_to_text(caps_all, NULL);
  if (!caps_text) {
     fprintf(stderr, "vdr: cap_to_text failed: %s\n", strerror(errno));
     return false;
     }
  if (cap_free(caps_all)) {
     fprintf(stderr, "vdr: cap_free failed: %s\n", strerror(errno));
     return false;
     }
  cap_t caps;
  if (strstr(caps_text,"cap_sys_time"))
     caps = cap_from_text("= cap_sys_nice,cap_sys_time,cap_net_raw=ep");
  else
     caps = cap_from_text("= cap_sys_nice,cap_net_raw=ep");
Klaus Schmidinger's avatar
Klaus Schmidinger committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  if (!caps) {
     fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno));
     return false;
     }
  if (cap_set_proc(caps) == -1) {
     fprintf(stderr, "vdr: cap_set_proc failed: %s\n", strerror(errno));
     cap_free(caps);
     return false;
     }
  cap_free(caps);
  return true;
}

static bool SetKeepCaps(bool On)
{
  // set keeping capabilities during setuid() on/off
  if (prctl(PR_SET_KEEPCAPS, On ? 1 : 0, 0, 0, 0) != 0) {
     fprintf(stderr, "vdr: prctl failed\n");
     return false;
     }
  return true;
}

Klaus Schmidinger's avatar
Klaus Schmidinger committed
171
static void SignalHandler(int signum)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
172
{
Klaus Schmidinger's avatar
Klaus Schmidinger committed
173 174 175 176 177 178 179 180 181 182 183
  switch (signum) {
    case SIGPIPE:
         break;
    case SIGHUP:
         LastSignal = signum;
         break;
    default:
         LastSignal = signum;
         Interface->Interrupt();
         ShutdownHandler.Exit(0);
    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
184
  signal(signum, SignalHandler);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
185 186
}

Klaus Schmidinger's avatar
Klaus Schmidinger committed
187 188 189 190
static void Watchdog(int signum)
{
  // Something terrible must have happened that prevented the 'alarm()' from
  // being called in time, so let's get out of here:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
191
  esyslog("PANIC: watchdog timer expired - exiting!");
192 193 194
#ifdef SDNOTIFY
  sd_notify(0, "STOPPING=1\nSTATUS=PANIC");
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
195 196 197
  exit(1);
}

Klaus Schmidinger's avatar
Klaus Schmidinger committed
198 199
int main(int argc, char *argv[])
{
Klaus Schmidinger's avatar
Klaus Schmidinger committed
200 201 202
  // Save terminal settings:

  struct termios savedTm;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
203
  bool HasStdin = (tcgetpgrp(STDIN_FILENO) == getpid() || getppid() != (pid_t)1) && tcgetattr(STDIN_FILENO, &savedTm) == 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
204

Klaus Schmidinger's avatar
Klaus Schmidinger committed
205 206 207 208
  // Initiate locale:

  setlocale(LC_ALL, "");

Klaus Schmidinger's avatar
Klaus Schmidinger committed
209 210
  // Command line options:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
211
#define dd(a, b) (*a ? a : b)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
212
#define DEFAULTSVDRPPORT 6419
Klaus Schmidinger's avatar
Klaus Schmidinger committed
213
#define DEFAULTWATCHDOG     0 // seconds
Klaus Schmidinger's avatar
Klaus Schmidinger committed
214 215
#define DEFAULTVIDEODIR VIDEODIR
#define DEFAULTCONFDIR dd(CONFDIR, VideoDirectory)
etobi's avatar
etobi committed
216
#define DEFAULTARGSDIR dd(ARGSDIR, "/etc/vdr/conf.d")
Klaus Schmidinger's avatar
Klaus Schmidinger committed
217 218
#define DEFAULTCACHEDIR dd(CACHEDIR, VideoDirectory)
#define DEFAULTRESDIR dd(RESDIR, ConfigDirectory)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
219
#define DEFAULTPLUGINDIR PLUGINDIR
Klaus Schmidinger's avatar
Klaus Schmidinger committed
220
#define DEFAULTLOCDIR LOCDIR
Klaus Schmidinger's avatar
Klaus Schmidinger committed
221
#define DEFAULTEPGDATAFILENAME "epg.data"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
222

Klaus Schmidinger's avatar
Klaus Schmidinger committed
223
  bool StartedAsRoot = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
224
  const char *VdrUser = NULL;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
225
  bool UserDump = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
226
  int SVDRPport = DEFAULTSVDRPPORT;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
227
  const char *AudioCommand = NULL;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
228
  const char *VideoDirectory = DEFAULTVIDEODIR;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
229
  const char *ConfigDirectory = NULL;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
230 231 232
  const char *CacheDirectory = NULL;
  const char *ResourceDirectory = NULL;
  const char *LocaleDirectory = DEFAULTLOCDIR;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
233
  const char *EpgDataFileName = DEFAULTEPGDATAFILENAME;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
234 235
  bool DisplayHelp = false;
  bool DisplayVersion = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
236
  bool DaemonMode = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
237
  int SysLogTarget = LOG_USER;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
238
  bool MuteAudio = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
239
  int WatchdogTimeout = DEFAULTWATCHDOG;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
240
  const char *Terminal = NULL;
etobi's avatar
etobi committed
241
  const char *OverrideCharacterTable = NULL;
242 243 244 245
#ifndef DEPRECATED_VDR_CHARSET_OVERRIDE
#define DEPRECATED_VDR_CHARSET_OVERRIDE 0
#endif
#if DEPRECATED_VDR_CHARSET_OVERRIDE
etobi's avatar
etobi committed
246 247 248
  OverrideCharacterTable = getenv("VDR_CHARSET_OVERRIDE");
  const char *DeprecatedVdrCharsetOverride = OverrideCharacterTable;
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
249 250 251 252 253 254 255 256 257

  bool UseKbd = true;
  const char *LircDevice = NULL;
#if !defined(REMOTE_KBD)
  UseKbd = false;
#endif
#if defined(REMOTE_LIRC)
  LircDevice = LIRC_DEVICE;
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
258 259 260
#if defined(VDR_USER)
  VdrUser = VDR_USER;
#endif
261
#ifdef SDNOTIFY
262
  time_t SdWatchdog = 0;
263 264
  int SdWatchdogTimeout = 0;
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
265

etobi's avatar
etobi committed
266 267 268 269 270 271 272 273 274 275
  cArgs *Args = NULL;
  if (argc == 1) {
     Args = new cArgs(argv[0]);
     if (Args->ReadDirectory(DEFAULTARGSDIR)) {
        argc = Args->GetArgc();
        argv = Args->GetArgv();
        }
     }

  cVideoDirectory::SetName(VideoDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
276
  cPluginManager PluginManager(DEFAULTPLUGINDIR);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
277 278

  static struct option long_options[] = {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
279
      { "audio",    required_argument, NULL, 'a' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
280
      { "cachedir", required_argument, NULL, 'c' | 0x100 },
etobi's avatar
etobi committed
281
      { "chartab",  required_argument, NULL, 'c' | 0x200 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
282 283 284
      { "config",   required_argument, NULL, 'c' },
      { "daemon",   no_argument,       NULL, 'd' },
      { "device",   required_argument, NULL, 'D' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
285
      { "dirnames", required_argument, NULL, 'd' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
286
      { "edit",     required_argument, NULL, 'e' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
287
      { "epgfile",  required_argument, NULL, 'E' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
288
      { "filesize", required_argument, NULL, 'f' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
289
      { "genindex", required_argument, NULL, 'g' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
290
      { "grab",     required_argument, NULL, 'g' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
291
      { "help",     no_argument,       NULL, 'h' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
292
      { "instance", required_argument, NULL, 'i' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
293
      { "lib",      required_argument, NULL, 'L' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
294
      { "lirc",     optional_argument, NULL, 'l' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
295
      { "localedir",required_argument, NULL, 'l' | 0x200 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
296
      { "log",      required_argument, NULL, 'l' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
297
      { "mute",     no_argument,       NULL, 'm' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
298
      { "no-kbd",   no_argument,       NULL, 'n' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
299
      { "plugin",   required_argument, NULL, 'P' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
300
      { "port",     required_argument, NULL, 'p' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
301
      { "record",   required_argument, NULL, 'r' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
302
      { "resdir",   required_argument, NULL, 'r' | 0x100 },
etobi's avatar
etobi committed
303
      { "showargs", optional_argument, NULL, 's' | 0x200 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
304
      { "shutdown", required_argument, NULL, 's' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
305
      { "split",    no_argument,       NULL, 's' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
306
      { "terminal", required_argument, NULL, 't' },
etobi's avatar
etobi committed
307
      { "updindex", required_argument, NULL, 'u' | 0x200 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
308
      { "user",     required_argument, NULL, 'u' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
309
      { "userdump", no_argument,       NULL, 'u' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
310
      { "version",  no_argument,       NULL, 'V' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
311
      { "vfat",     no_argument,       NULL, 'v' | 0x100 },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
312 313
      { "video",    required_argument, NULL, 'v' },
      { "watchdog", required_argument, NULL, 'w' },
Klaus Schmidinger's avatar
Klaus Schmidinger committed
314
      { NULL,       no_argument,       NULL,  0  }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
315
    };
Klaus Schmidinger's avatar
Klaus Schmidinger committed
316

Klaus Schmidinger's avatar
Klaus Schmidinger committed
317
  int c;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
318
  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) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
319
        switch (c) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
320
          case 'a': AudioCommand = optarg;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
321
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
322 323 324
          case 'c' | 0x100:
                    CacheDirectory = optarg;
                    break;
etobi's avatar
etobi committed
325 326 327
          case 'c' | 0x200:
                    OverrideCharacterTable = optarg;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
328 329
          case 'c': ConfigDirectory = optarg;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
330 331
          case 'd': DaemonMode = true;
                    break;
332 333 334 335 336
          case 'D': if (*optarg == '-') {
                       cDvbDevice::useDvbDevices = false;
                       break;
                       }
                    if (isnumber(optarg)) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
337
                       int n = atoi(optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
338 339
                       if (0 <= n && n < MAXDEVICES) {
                          cDevice::SetUseDevice(n);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
340 341 342 343
                          break;
                          }
                       }
                    fprintf(stderr, "vdr: invalid DVB device number: %s\n", optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
344
                    return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
345 346
          case 'd' | 0x100: {
                    char *s = optarg;
etobi's avatar
etobi committed
347 348 349 350 351 352 353 354 355 356 357 358 359
                    if (*s != ',') {
                       int n = strtol(s, &s, 10);
                       if (n <= 0 || n >= PATH_MAX) { // PATH_MAX includes the terminating 0
                          fprintf(stderr, "vdr: invalid directory path length: %s\n", optarg);
                          return 2;
                          }
                       DirectoryPathMax = n;
                       if (!*s)
                          break;
                       if (*s != ',') {
                          fprintf(stderr, "vdr: invalid delimiter: %s\n", optarg);
                          return 2;
                          }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
360
                       }
etobi's avatar
etobi committed
361
                    s++;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
362 363
                    if (!*s)
                       break;
etobi's avatar
etobi committed
364 365 366 367 368 369 370 371 372 373 374 375 376
                    if (*s != ',') {
                       int n = strtol(s, &s, 10);
                       if (n <= 0 || n > NAME_MAX) { // NAME_MAX excludes the terminating 0
                          fprintf(stderr, "vdr: invalid directory name length: %s\n", optarg);
                          return 2;
                          }
                       DirectoryNameMax = n;
                       if (!*s)
                          break;
                       if (*s != ',') {
                          fprintf(stderr, "vdr: invalid delimiter: %s\n", optarg);
                          return 2;
                          }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
377
                       }
etobi's avatar
etobi committed
378
                    s++;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
379 380
                    if (!*s)
                       break;
etobi's avatar
etobi committed
381
                    int n = strtol(s, &s, 10);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
382 383 384 385 386 387 388 389 390 391
                    if (n != 0 && n != 1) {
                       fprintf(stderr, "vdr: invalid directory encoding: %s\n", optarg);
                       return 2;
                       }
                    DirectoryEncoding = n;
                    if (*s) {
                       fprintf(stderr, "vdr: unexpected data: %s\n", optarg);
                       return 2;
                       }
                    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
392
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
393 394
          case 'e' | 0x100:
                    return CutRecording(optarg) ? 0 : 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
395
          case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
396
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
397 398 399 400 401 402 403
          case 'f' | 0x100:
                    Setup.MaxVideoFileSize = StrToNum(optarg) / MEGABYTE(1);
                    if (Setup.MaxVideoFileSize < MINVIDEOFILESIZE)
                       Setup.MaxVideoFileSize = MINVIDEOFILESIZE;
                    if (Setup.MaxVideoFileSize > MAXVIDEOFILESIZETS)
                       Setup.MaxVideoFileSize = MAXVIDEOFILESIZETS;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
404 405
          case 'g' | 0x100:
                    return GenerateIndex(optarg) ? 0 : 2;
406
          case 'g': SetSVDRPGrabImageDir(*optarg != '-' ? optarg : NULL);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
407
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
408
          case 'h': DisplayHelp = true;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
409
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
410 411 412 413 414 415 416
          case 'i': if (isnumber(optarg)) {
                       InstanceId = atoi(optarg);
                       if (InstanceId >= 0)
                          break;
                       }
                    fprintf(stderr, "vdr: invalid instance id: %s\n", optarg);
                    return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
417
          case 'l': {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
                    char *p = strchr(optarg, '.');
                    if (p)
                       *p = 0;
                    if (isnumber(optarg)) {
                       int l = atoi(optarg);
                       if (0 <= l && l <= 3) {
                          SysLogLevel = l;
                          if (!p)
                             break;
                          if (isnumber(p + 1)) {
                             int l = atoi(p + 1);
                             if (0 <= l && l <= 7) {
                                int targets[] = { LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7 };
                                SysLogTarget = targets[l];
                                break;
                                }
                             }
                          }
                       }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
437 438
                    if (p)
                       *p = '.';
Klaus Schmidinger's avatar
Klaus Schmidinger committed
439
                    fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
440
                    return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
441
                    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
442 443 444 445 446 447
          case 'L': if (access(optarg, R_OK | X_OK) == 0)
                       PluginManager.SetDirectory(optarg);
                    else {
                       fprintf(stderr, "vdr: can't access plugin directory: %s\n", optarg);
                       return 2;
                       }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
448
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
449
          case 'l' | 0x100:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
450
                    LircDevice = optarg ? optarg : LIRC_DEVICE;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
451
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
452 453
          case 'l' | 0x200:
                    if (access(optarg, R_OK | X_OK) == 0)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
454
                       LocaleDirectory = optarg;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
455 456 457 458 459
                    else {
                       fprintf(stderr, "vdr: can't access locale directory: %s\n", optarg);
                       return 2;
                       }
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
460 461
          case 'm': MuteAudio = true;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
462 463 464
          case 'n' | 0x100:
                    UseKbd = false;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
465
          case 'p': if (isnumber(optarg))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
466
                       SVDRPport = atoi(optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
467 468
                    else {
                       fprintf(stderr, "vdr: invalid port number: %s\n", optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
469
                       return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
470 471
                       }
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
472 473
          case 'P': PluginManager.AddPlugin(optarg);
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
474 475
          case 'r': cRecordingUserCommand::SetCommand(optarg);
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
476 477 478
          case 'r' | 0x100:
                    ResourceDirectory = optarg;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
479
          case 's': ShutdownHandler.SetShutdownCommand(optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
480
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
481 482 483
          case 's' | 0x100:
                    Setup.SplitEditedFiles = 1;
                    break;
etobi's avatar
etobi committed
484 485 486 487 488 489 490 491 492 493 494 495 496
          case 's' | 0x200: {
                    const char *ArgsDir = optarg ? optarg : DEFAULTARGSDIR;
                    cArgs Args(argv[0]);
                    if (!Args.ReadDirectory(ArgsDir)) {
                       fprintf(stderr, "vdr: can't read arguments from directory: %s\n", ArgsDir);
                       return 2;
                       }
                    int c = Args.GetArgc();
                    char **v = Args.GetArgv();
                    for (int i = 1; i < c; i++)
                        printf("%s\n", v[i]);
                    return 0;
                    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
497
          case 't': Terminal = optarg;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
498 499 500 501
                    if (access(Terminal, R_OK | W_OK) < 0) {
                       fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal);
                       return 2;
                       }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
502
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
503 504 505
          case 'u': if (*optarg)
                       VdrUser = optarg;
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
506 507 508
          case 'u' | 0x100:
                    UserDump = true;
                    break;
etobi's avatar
etobi committed
509 510
          case 'u' | 0x200:
                    return GenerateIndex(optarg, true) ? 0 : 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
511
          case 'V': DisplayVersion = true;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
512
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
513
          case 'v' | 0x100:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
514 515 516
                    DirectoryPathMax = 250;
                    DirectoryNameMax = 40;
                    DirectoryEncoding = true;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
517
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
518
          case 'v': VideoDirectory = optarg;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
519 520
                    while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
                          optarg[strlen(optarg) - 1] = 0;
etobi's avatar
etobi committed
521
                    cVideoDirectory::SetName(VideoDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
522
                    break;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
523 524
          case 'w': if (isnumber(optarg)) {
                       int t = atoi(optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
525 526 527 528 529 530
                       if (t >= 0) {
                          WatchdogTimeout = t;
                          break;
                          }
                       }
                    fprintf(stderr, "vdr: invalid watchdog timeout: %s\n", optarg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
531 532
                    return 2;
          default:  return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
533 534 535
          }
        }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
536 537
  // Set user id in case we were started as root:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
538
  if (VdrUser && geteuid() == 0) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
539
     StartedAsRoot = true;
540
     if (strcmp(VdrUser, "root") && strcmp(VdrUser, "0")) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
541 542
        if (!SetKeepCaps(true))
           return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
543
        if (!SetUser(VdrUser, UserDump))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
544 545 546
           return 2;
        if (!SetKeepCaps(false))
           return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
547
        if (!DropCaps())
Klaus Schmidinger's avatar
Klaus Schmidinger committed
548 549
           return 2;
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
550 551
     }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
552 553 554 555 556 557 558 559 560
  // Help and version info:

  if (DisplayHelp || DisplayVersion) {
     if (!PluginManager.HasPlugins())
        PluginManager.AddPlugin("*"); // adds all available plugins
     PluginManager.LoadPlugins();
     if (DisplayHelp) {
        printf("Usage: vdr [OPTIONS]\n\n"          // for easier orientation, this is column 80|
               "  -a CMD,   --audio=CMD    send Dolby Digital audio to stdin of command CMD\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
561
               "            --cachedir=DIR save cache files in DIR (default: %s)\n"
etobi's avatar
etobi committed
562 563 564 565 566
               "            --chartab=CHARACTER_TABLE\n"
               "                           set the character table to use for strings in the\n"
               "                           DVB data stream that don't begin with a character\n"
               "                           table indicator, but don't use the standard default\n"
               "                           character table (for instance ISO-8859-9)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
567
               "  -c DIR,   --config=DIR   read config files from DIR (default: %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
568 569 570
               "  -d,       --daemon       run in daemon mode\n"
               "  -D NUM,   --device=NUM   use only the given DVB device (NUM = 0, 1, 2...)\n"
               "                           there may be several -D options (default: all DVB\n"
571 572 573
               "                           devices will be used); if -D- is given, no DVB\n"
               "                           devices will be used at all, independent of any\n"
               "                           other -D options\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
574 575 576 577 578 579
               "            --dirnames=PATH[,NAME[,ENC]]\n"
               "                           set the maximum directory path length to PATH\n"
               "                           (default: %d); if NAME is also given, it defines\n"
               "                           the maximum directory name length (default: %d);\n"
               "                           the optional ENC can be 0 or 1, and controls whether\n"
               "                           special characters in directory names are encoded as\n"
etobi's avatar
etobi committed
580 581 582
               "                           hex values (default: 0); if PATH or NAME are left\n"
               "                           empty (as in \",,1\" to only set ENC), the defaults\n"
               "                           apply\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
583
               "            --edit=REC     cut recording REC and exit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
584
               "  -E FILE,  --epgfile=FILE write the EPG data into the given FILE (default is\n"
etobi's avatar
etobi committed
585
               "                           '%s' in the cache directory)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
586
               "                           '-E-' disables this\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
587 588
               "                           if FILE is a directory, the default EPG file will be\n"
               "                           created in that directory\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
589 590
               "            --filesize=SIZE limit video files to SIZE bytes (default is %dM)\n"
               "                           only useful in conjunction with --edit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
591
               "            --genindex=REC generate index for recording REC and exit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
592 593 594 595
               "  -g DIR,   --grab=DIR     write images from the SVDRP command GRAB into the\n"
               "                           given DIR; DIR must be the full path name of an\n"
               "                           existing directory, without any \"..\", double '/'\n"
               "                           or symlinks (default: none, same as -g-)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
596
               "  -h,       --help         print this help and exit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
597
               "  -i ID,    --instance=ID  use ID as the id of this VDR instance (default: 0)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
598 599 600
               "  -l LEVEL, --log=LEVEL    set log level (default: 3)\n"
               "                           0 = no logging, 1 = errors only,\n"
               "                           2 = errors and info, 3 = errors, info and debug\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
601 602
               "                           if logging should be done to LOG_LOCALn instead of\n"
               "                           LOG_USER, add '.n' to LEVEL, as in 3.7 (n=0..7)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
603
               "  -L DIR,   --lib=DIR      search for plugins in DIR (default is %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
604 605
               "            --lirc[=PATH]  use a LIRC remote control device, attached to PATH\n"
               "                           (default: %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
606 607
               "            --localedir=DIR search for locale files in DIR (default is\n"
               "                           %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
608
               "  -m,       --mute         mute audio of the primary DVB device at startup\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
609
               "            --no-kbd       don't use the keyboard as an input device\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
610 611 612
               "  -p PORT,  --port=PORT    use PORT for SVDRP (default: %d)\n"
               "                           0 turns off SVDRP\n"
               "  -P OPT,   --plugin=OPT   load a plugin defined by the given options\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
613 614
               "  -r CMD,   --record=CMD   call CMD before and after a recording, and after\n"
               "                           a recording has been edited or deleted\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
615
               "            --resdir=DIR   read resource files from DIR (default: %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
616
               "  -s CMD,   --shutdown=CMD call CMD to shutdown the computer\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
617 618
               "            --split        split edited files at the editing marks (only\n"
               "                           useful in conjunction with --edit)\n"
etobi's avatar
etobi committed
619 620
               "            --showargs[=DIR] print the arguments read from DIR and exit\n"
               "                           (default: %s)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
621
               "  -t TTY,   --terminal=TTY controlling tty\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
622
               "  -u USER,  --user=USER    run as user USER; only applicable if started as\n"
623
               "                           root; USER can be a user name or a numerical id\n"
etobi's avatar
etobi committed
624
               "            --updindex=REC update index for recording REC and exit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
625
               "            --userdump     allow coredumps if -u is given (debugging)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
626 627
               "  -v DIR,   --video=DIR    use DIR as video directory (default: %s)\n"
               "  -V,       --version      print version information and exit\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
628
               "            --vfat         for backwards compatibility (same as\n"
etobi's avatar
etobi committed
629
               "                           --dirnames=250,40,1)\n"
Klaus Schmidinger's avatar
Klaus Schmidinger committed
630 631 632
               "  -w SEC,   --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
               "                           seconds (default: %d); '0' disables the watchdog\n"
               "\n",
Klaus Schmidinger's avatar
Klaus Schmidinger committed
633
               DEFAULTCACHEDIR,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
634
               DEFAULTCONFDIR,
etobi's avatar
etobi committed
635
               PATH_MAX - 1,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
636
               NAME_MAX,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
637
               DEFAULTEPGDATAFILENAME,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
638
               MAXVIDEOFILESIZEDEFAULT,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
639
               DEFAULTPLUGINDIR,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
640
               LIRC_DEVICE,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
641
               DEFAULTLOCDIR,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
642
               DEFAULTSVDRPPORT,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
643
               DEFAULTRESDIR,
etobi's avatar
etobi committed
644
               DEFAULTARGSDIR,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
645
               DEFAULTVIDEODIR,
Klaus Schmidinger's avatar
Klaus Schmidinger committed
646 647 648 649
               DEFAULTWATCHDOG
               );
        }
     if (DisplayVersion)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
650
        printf("vdr (%s/%s) - The Video Disk Recorder\n", VDRVERSION, APIVERSION);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
     if (PluginManager.HasPlugins()) {
        if (DisplayHelp)
           printf("Plugins: vdr -P\"name [OPTIONS]\"\n\n");
        for (int i = 0; ; i++) {
            cPlugin *p = PluginManager.GetPlugin(i);
            if (p) {
               const char *help = p->CommandLineHelp();
               printf("%s (%s) - %s\n", p->Name(), p->Version(), p->Description());
               if (DisplayHelp && help) {
                  printf("\n");
                  puts(help);
                  }
               }
            else
               break;
            }
        }
     return 0;
     }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
671
  // Log file:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
672

Klaus Schmidinger's avatar
Klaus Schmidinger committed
673
  if (SysLogLevel > 0)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
674
     openlog("vdr", LOG_CONS, SysLogTarget); // LOG_PID doesn't work as expected under NPTL
Klaus Schmidinger's avatar
Klaus Schmidinger committed
675 676 677 678 679

  // Check the video directory:

  if (!DirectoryOk(VideoDirectory, true)) {
     fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
680
     return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
681
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
682 683 684 685

  // Daemon mode:

  if (DaemonMode) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
686
     if (daemon(1, 0) == -1) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
687
        fprintf(stderr, "vdr: %m\n");
Klaus Schmidinger's avatar
Klaus Schmidinger committed
688
        esyslog("ERROR: %m");
Klaus Schmidinger's avatar
Klaus Schmidinger committed
689
        return 2;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
690 691
        }
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
692 693 694 695 696
  else if (Terminal) {
     // Claim new controlling terminal
     stdin  = freopen(Terminal, "r", stdin);
     stdout = freopen(Terminal, "w", stdout);
     stderr = freopen(Terminal, "w", stderr);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
697
     HasStdin = true;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
698
     tcgetattr(STDIN_FILENO, &savedTm);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
699 700
     }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
701
  isyslog("VDR version %s started", VDRVERSION);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
702
  if (StartedAsRoot && VdrUser)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
703 704 705 706
     isyslog("switched to user '%s'", VdrUser);
  if (DaemonMode)
     dsyslog("running as daemon (tid=%d)", cThread::ThreadId());
  cThread::SetMainThreadId();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
707

Klaus Schmidinger's avatar
Klaus Schmidinger committed
708 709
  // Set the system character table:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
710 711 712 713 714 715 716 717 718
  char *CodeSet = NULL;
  if (setlocale(LC_CTYPE, ""))
     CodeSet = nl_langinfo(CODESET);
  else {
     char *LangEnv = getenv("LANG"); // last resort in case locale stuff isn't installed
     if (LangEnv) {
        CodeSet = strchr(LangEnv, '.');
        if (CodeSet)
           CodeSet++; // skip the dot
Klaus Schmidinger's avatar
Klaus Schmidinger committed
719 720
        }
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
721 722 723 724 725
  if (CodeSet) {
     bool known = SI::SetSystemCharacterTable(CodeSet);
     isyslog("codeset is '%s' - %s", CodeSet, known ? "known" : "unknown");
     cCharSetConv::SetSystemCharacterTable(CodeSet);
     }
726
#if DEPRECATED_VDR_CHARSET_OVERRIDE
etobi's avatar
etobi committed
727 728 729 730 731 732 733
  if (DeprecatedVdrCharsetOverride)
     isyslog("use of environment variable VDR_CHARSET_OVERRIDE (%s) is deprecated!", DeprecatedVdrCharsetOverride);
#endif
  if (OverrideCharacterTable) {
     isyslog("override character table is '%s'", OverrideCharacterTable);
     SI::SetOverrideCharacterTable(OverrideCharacterTable);
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
734

Klaus Schmidinger's avatar
Klaus Schmidinger committed
735 736
  // Initialize internationalization:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
737
  I18nInitialize(LocaleDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
738

Klaus Schmidinger's avatar
Klaus Schmidinger committed
739 740
  // Main program loop variables - need to be here to have them initialized before any EXIT():

Klaus Schmidinger's avatar
Klaus Schmidinger committed
741
  cEpgDataReader EpgDataReader;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
742
  cOsdObject *Menu = NULL;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
743
  int LastChannel = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
744 745 746 747
  int LastTimerChannel = -1;
  int PreviousChannel[2] = { 1, 1 };
  int PreviousChannelIndex = 0;
  time_t LastChannelChanged = time(NULL);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
748
  time_t LastInteract = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
749
  int MaxLatencyTime = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
750
  bool InhibitEpgScan = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
751
  bool IsInfoMenu = false;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
752
  cSkin *CurrentSkin = NULL;
753
  int OldPrimaryDVB = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
754

Klaus Schmidinger's avatar
Klaus Schmidinger committed
755 756 757
  // Load plugins:

  if (!PluginManager.LoadPlugins(true))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
758
     EXIT(2);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
759

Klaus Schmidinger's avatar
Klaus Schmidinger committed
760
  // Directories:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
761

Klaus Schmidinger's avatar
Klaus Schmidinger committed
762
  if (!ConfigDirectory)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
763
     ConfigDirectory = DEFAULTCONFDIR;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
764
  cPlugin::SetConfigDirectory(ConfigDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
765 766 767 768 769 770
  if (!CacheDirectory)
     CacheDirectory = DEFAULTCACHEDIR;
  cPlugin::SetCacheDirectory(CacheDirectory);
  if (!ResourceDirectory)
     ResourceDirectory = DEFAULTRESDIR;
  cPlugin::SetResourceDirectory(ResourceDirectory);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
771
  cThemes::SetThemesDirectory(AddDirectory(ConfigDirectory, "themes"));
Klaus Schmidinger's avatar
Klaus Schmidinger committed
772

Klaus Schmidinger's avatar
Klaus Schmidinger committed
773 774
  // Configuration data:

775
  Setup.Load(AddDirectory(ConfigDirectory, "setup.conf"));
Klaus Schmidinger's avatar
Klaus Schmidinger committed
776 777
  Sources.Load(AddDirectory(ConfigDirectory, "sources.conf"), true, true);
  Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
778
  Scrs.Load(AddDirectory(ConfigDirectory, "scr.conf"), true);
779 780
  cChannels::Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true);
  cTimers::Load(AddDirectory(ConfigDirectory, "timers.conf"));
Klaus Schmidinger's avatar
Klaus Schmidinger committed
781 782
  Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"));
  RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"));
Klaus Schmidinger's avatar
Klaus Schmidinger committed
783 784 785
  SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true);
  Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
  KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
786
  Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
787
  CamResponsesLoad(AddDirectory(ConfigDirectory, "camresponses.conf"), true);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
788

Klaus Schmidinger's avatar
Klaus Schmidinger committed
789 790 791 792 793 794
  if (!*cFont::GetFontFileName(Setup.FontOsd)) {
     const char *msg = "no fonts available - OSD will not show any text!";
     fprintf(stderr, "vdr: %s\n", msg);
     esyslog("ERROR: %s", msg);
     }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
795 796
  // Recordings:

797
  cRecordings::Update();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
798

Klaus Schmidinger's avatar
Klaus Schmidinger committed
799 800 801
  // EPG data:

  if (EpgDataFileName) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
802 803 804 805 806
     const char *EpgDirectory = NULL;
     if (DirectoryOk(EpgDataFileName)) {
        EpgDirectory = EpgDataFileName;
        EpgDataFileName = DEFAULTEPGDATAFILENAME;
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
807
     else if (*EpgDataFileName != '/' && *EpgDataFileName != '.')
Klaus Schmidinger's avatar
Klaus Schmidinger committed
808
        EpgDirectory = CacheDirectory;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
809 810 811 812
     if (EpgDirectory)
        cSchedules::SetEpgDataFileName(AddDirectory(EpgDirectory, EpgDataFileName));
     else
        cSchedules::SetEpgDataFileName(EpgDataFileName);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
813
     EpgDataReader.Start();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
814 815
     }

Klaus Schmidinger's avatar
Klaus Schmidinger committed
816 817
  // DVB interfaces:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
818
  cDvbDevice::Initialize();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
819
  cDvbDevice::BondDevices(Setup.DeviceBondings);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
820

Klaus Schmidinger's avatar
Klaus Schmidinger committed
821
  // Initialize plugins:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
822

Klaus Schmidinger's avatar
Klaus Schmidinger committed
823
  if (!PluginManager.InitializePlugins())
Klaus Schmidinger's avatar
Klaus Schmidinger committed
824
     EXIT(2);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
825

Klaus Schmidinger's avatar
Klaus Schmidinger committed
826 827 828
  // Primary device:

  cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
829 830 831
  if (!cDevice::PrimaryDevice() || !cDevice::PrimaryDevice()->HasDecoder()) {
     if (cDevice::PrimaryDevice() && !cDevice::PrimaryDevice()->HasDecoder())
        isyslog("device %d has no MPEG decoder", cDevice::PrimaryDevice()->DeviceNumber() + 1);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
832 833 834 835
     for (int i = 0; i < cDevice::NumDevices(); i++) {
         cDevice *d = cDevice::GetDevice(i);
         if (d && d->HasDecoder()) {
            isyslog("trying device number %d instead", i + 1);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
836
            if (cDevice::SetPrimaryDevice(i + 1)) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
837
               Setup.PrimaryDVB = i + 1;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
838 839
               break;
               }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
840 841
            }
         }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
842 843 844 845
     if (!cDevice::PrimaryDevice()) {
        const char *msg = "no primary device found - using first device!";
        fprintf(stderr, "vdr: %s\n", msg);
        esyslog("ERROR: %s", msg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
846
        if (!cDevice::SetPrimaryDevice(1))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
847
           EXIT(2);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
848 849 850 851
        if (!cDevice::PrimaryDevice()) {
           const char *msg = "no primary device found - giving up!";
           fprintf(stderr, "vdr: %s\n", msg);
           esyslog("ERROR: %s", msg);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
852
           EXIT(2);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
853 854
           }
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
855
     }
856
  OldPrimaryDVB = Setup.PrimaryDVB;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
857

Klaus Schmidinger's avatar
Klaus Schmidinger committed
858 859 860 861
  // Check for timers in automatic start time window:

  ShutdownHandler.CheckManualStart(MANUALSTART);

Klaus Schmidinger's avatar
Klaus Schmidinger committed
862 863
  // User interface:

864
  Interface = new cInterface;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
865

Klaus Schmidinger's avatar
Klaus Schmidinger committed
866 867
  // Default skins:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
868
  new cSkinLCARS;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
869
  new cSkinSTTNG;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
870
  new cSkinClassic;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
871 872
  Skins.SetCurrent(Setup.OSDSkin);
  cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme());
Klaus Schmidinger's avatar
Klaus Schmidinger committed
873
  CurrentSkin = Skins.Current();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
874

Klaus Schmidinger's avatar
Klaus Schmidinger committed
875 876 877
  // Start plugins:

  if (!PluginManager.StartPlugins())
Klaus Schmidinger's avatar
Klaus Schmidinger committed
878
     EXIT(2);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
879

Klaus Schmidinger's avatar
Klaus Schmidinger committed
880
  // Set skin and theme in case they're implemented by a plugin:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
881

Klaus Schmidinger's avatar
Klaus Schmidinger committed
882 883 884 885
  if (!CurrentSkin || CurrentSkin == Skins.Current() && strcmp(Skins.Current()->Name(), Setup.OSDSkin) != 0) {
     Skins.SetCurrent(Setup.OSDSkin);
     cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme());
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
886

Klaus Schmidinger's avatar
Klaus Schmidinger committed
887
  // Remote Controls:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
888 889 890
  if (LircDevice)
     new cLircRemote(LircDevice);
  if (!DaemonMode && HasStdin && UseKbd)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
891
     new cKbdRemote;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
892 893
  Interface->LearnKeys();

Klaus Schmidinger's avatar
Klaus Schmidinger committed
894 895 896 897 898
  // External audio:

  if (AudioCommand)
     new cExternalAudio(AudioCommand);

etobi's avatar
etobi committed
899 900 901 902 903
  // Positioner:

  if (!cPositioner::GetPositioner()) // no plugin has created a positioner
     new cDiseqcPositioner;

904 905 906 907
  // CAM data:

  ChannelCamRelations.Load(AddDirectory(CacheDirectory, "cam.data"));

Klaus Schmidinger's avatar
Klaus Schmidinger committed
908 909
  // Channel:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
910 911
  if (!cDevice::WaitForAllDevicesReady(DEVICEREADYTIMEOUT))
     dsyslog("not all devices ready after %d seconds", DEVICEREADYTIMEOUT);
etobi's avatar
etobi committed
912 913
  if (!CamSlots.WaitForAllCamSlotsReady(DEVICEREADYTIMEOUT))
     dsyslog("not all CAM slots ready after %d seconds", DEVICEREADYTIMEOUT);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
914
  if (*Setup.InitialChannel) {
915
     LOCK_CHANNELS_READ;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
916
     if (isnumber(Setup.InitialChannel)) { // for compatibility with old setup.conf files
917
        if (const cChannel *Channel = Channels->GetByNumber(atoi(Setup.InitialChannel)))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
918 919
           Setup.InitialChannel = Channel->GetChannelID().ToString();
        }
920
     if (const cChannel *Channel = Channels->GetByChannelID(tChannelID::FromString(Setup.InitialChannel)))
Klaus Schmidinger's avatar
Klaus Schmidinger committed
921
        Setup.CurrentChannel = Channel->Number();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
922
     }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
923 924
  if (Setup.InitialVolume >= 0)
     Setup.CurrentVolume = Setup.InitialVolume;
925 926 927 928
  {
    LOCK_CHANNELS_READ;
    Channels->SwitchTo(Setup.CurrentChannel);
  }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
929
  if (MuteAudio)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
930
     cDevice::PrimaryDevice()->ToggleMute();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
931
  else
Klaus Schmidinger's avatar
Klaus Schmidinger committed
932
     cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
933

Klaus Schmidinger's avatar
Klaus Schmidinger committed
934 935
  // Signal handlers:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
936 937 938
  if (signal(SIGHUP,  SignalHandler) == SIG_IGN) signal(SIGHUP,  SIG_IGN);
  if (signal(SIGINT,  SignalHandler) == SIG_IGN) signal(SIGINT,  SIG_IGN);
  if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
939
  if (signal(SIGPIPE, SignalHandler) == SIG_IGN) signal(SIGPIPE, SIG_IGN);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
940 941
  if (WatchdogTimeout > 0)
     if (signal(SIGALRM, Watchdog)   == SIG_IGN) signal(SIGALRM, SIG_IGN);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
942

Klaus Schmidinger's avatar
Klaus Schmidinger committed
943 944 945 946 947 948 949
  // Watchdog:

  if (WatchdogTimeout > 0) {
     dsyslog("setting watchdog timer to %d seconds", WatchdogTimeout);
     alarm(WatchdogTimeout); // Initial watchdog timer start
     }

etobi's avatar
etobi committed
950
#ifdef SDNOTIFY
951 952 953 954 955 956 957 958 959 960
  if (sd_watchdog_enabled(0, NULL) > 0) {
     uint64_t timeout;
     SdWatchdog = time(NULL);
     sd_watchdog_enabled(0, &timeout);
     SdWatchdogTimeout = (int)timeout/1000000;
     dsyslog("SD_WATCHDOG enabled with timeout set to %d seconds", SdWatchdogTimeout);
     }

  // Startup notification:

etobi's avatar
etobi committed
961 962 963
  sd_notify(0, "READY=1\nSTATUS=Ready");
#endif

964 965 966
  // SVDRP:

  SetSVDRPPorts(SVDRPport, DEFAULTSVDRPPORT);
967
  StartSVDRPHandler();
968

Klaus Schmidinger's avatar
Klaus Schmidinger committed
969 970
  // Main program loop:

Klaus Schmidinger's avatar
Klaus Schmidinger committed
971 972
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)

Klaus Schmidinger's avatar
Klaus Schmidinger committed
973
  while (!ShutdownHandler.DoExit()) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
974 975 976
#ifdef DEBUGRINGBUFFERS
        cRingBufferLinear::PrintDebugRBL();
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
977 978
        // Attach launched player control:
        cControl::Attach();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
979 980 981

        time_t Now = time(NULL);

Klaus Schmidinger's avatar
Klaus Schmidinger committed
982
        // Make sure we have a visible programme in case device usage has changed:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
983
        if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder()) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
984
           static time_t lastTime = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
985 986
           if (!cDevice::PrimaryDevice()->HasProgramme()) {
              if (!CamMenuActive() && Now - lastTime > MINCHANNELWAIT) { // !CamMenuActive() to avoid interfering with the CAM if a CAM menu is open
987 988
                 LOCK_CHANNELS_READ;
                 const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel());
Klaus Schmidinger's avatar
Klaus Schmidinger committed
989
                 if (Channel && (Channel->Vpid() || Channel->Apid(0) || Channel->Dpid(0))) {
990
                    if (cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels->SwitchTo(Channel->Number())) // try to switch to the original channel...
Klaus Schmidinger's avatar
Klaus Schmidinger committed
991
                       ;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
992
                    else if (LastTimerChannel > 0) {
993 994
                       Channel = Channels->GetByNumber(LastTimerChannel);
                       if (Channel && cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels->SwitchTo(LastTimerChannel)) // ...or the one used by the last timer
Klaus Schmidinger's avatar
Klaus Schmidinger committed
995 996
                          ;
                       }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
997
                    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
998 999
                 lastTime = Now; // don't do this too often
                 LastTimerChannel = -1;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1000
                 }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1001
              }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1002 1003
           else
              lastTime = 0; // makes sure we immediately try again next time
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1004
           }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1005 1006 1007
        // Update the OSD size:
        {
          static time_t lastOsdSizeUpdate = 0;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1008
          if (Now != lastOsdSizeUpdate) { // once per second
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1009
             cOsdProvider::UpdateOsdSize();
1010 1011 1012 1013 1014
             static int OsdState = 0;
             if (cOsdProvider::OsdSizeChanged(OsdState)) {
                if (cOsdMenu *OsdMenu = dynamic_cast<cOsdMenu *>(Menu))
                   OsdMenu->Display();
                }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1015 1016
             lastOsdSizeUpdate = Now;
             }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1017
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1018 1019 1020 1021 1022
        // Restart the Watchdog timer:
        if (WatchdogTimeout > 0) {
           int LatencyTime = WatchdogTimeout - alarm(WatchdogTimeout);
           if (LatencyTime > MaxLatencyTime) {
              MaxLatencyTime = LatencyTime;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1023
              dsyslog("max. latency time %d seconds", MaxLatencyTime);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1024 1025
              }
           }
1026 1027 1028 1029 1030 1031 1032 1033
#ifdef SDNOTIFY
        // Ping systemd watchdog when half the timeout is elapsed:
        if (SdWatchdogTimeout && (Now - SdWatchdog) * 2 > SdWatchdogTimeout) {
           sd_notify(0, "WATCHDOG=1");
           SdWatchdog = Now;
           dsyslog("SD_WATCHDOG ping");
           }
#endif
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1034
        // Handle channel and timer modifications:
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
        {
          // Channels and timers need to be stored in a consistent manner,
          // therefore if one of them is changed, we save both.
          static time_t ChannelSaveTimeout = 0;
          static cStateKey TimersStateKey(true);
          static cStateKey ChannelsStateKey(true);
          static int ChannelsModifiedByUser = 0;
          const cTimers *Timers = cTimers::GetTimersRead(TimersStateKey);
          const cChannels *Channels = cChannels::GetChannelsRead(ChannelsStateKey);
          if (ChannelSaveTimeout != 1) {
             if (Channels) {
                if (Channels->ModifiedByUser(ChannelsModifiedByUser))
                   ChannelSaveTimeout = 1; // triggers an immediate save
                else if (!ChannelSaveTimeout)
                   ChannelSaveTimeout = Now + CHANNELSAVEDELTA;
                }
             if (Timers)
                ChannelSaveTimeout = 1; // triggers an immediate save
             }
          if (ChannelSaveTimeout && Now > ChannelSaveTimeout && !cRecordControls::Active())
             ChannelSaveTimeout = 1; // triggers an immediate save
          if (Timers && Channels) {
             Channels->Save();
             Timers->Save();
             ChannelSaveTimeout = 0;
             }
          if (Channels) {
             for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
                 if (Channel->Modification(CHANNELMOD_RETUNE)) {
                    cRecordControls::ChannelDataModified(Channel);
                    if (Channel->Number() == cDevice::CurrentChannel() && cDevice::PrimaryDevice()->HasDecoder()) {
                       if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) {
                          if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
                             isyslog("retuning due to modification of channel %d (%s)", Channel->Number(), Channel->Name());
                             Channels->SwitchTo(Channel->Number());
                             }
                          }
                       }
                    cStatus::MsgChannelChange(Channel);
                    }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1075
                 }
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
             }
          // State keys are removed in reverse order!
          if (Channels)
             ChannelsStateKey.Remove();
          if (Timers)
             TimersStateKey.Remove();
          if (ChannelSaveTimeout == 1) {
             // Only one of them was modified, so we reset the state keys to handle them both in the next turn:
             ChannelsStateKey.Reset();
             TimersStateKey.Reset();
             }
        }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1088
        // Channel display:
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1089
        if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1090
           if (!Menu)
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1091
              Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0);
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1092
           LastChannel = cDevice::CurrentChannel();
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1093
           LastChannelChanged = Now;
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1094
           }
Klaus Schmidinger's avatar
Klaus Schmidinger committed
1095
        if (Now - LastChannelChanged >= Setup.ZapTimeout && LastChannel != PreviousChannel[PreviousChannelIndex])
1096
           PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel;
1097 1098
        {
          // Timers and Recordings:
1099
          static cStateKey TimersStateKey;
1100
          cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey);
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
          {
            // Assign events to timers:
            static cStateKey SchedulesStateKey;
            if (TimersStateKey.StateChanged())
               SchedulesStateKey.Reset(); // we assign events if either the Timers or the Schedules have changed
            bool TimersModified = false;
            if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) {
               Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
               if (Timers->SetEvents(Schedules))
                  TimersModified = true;
               SchedulesStateKey.Remove();
               }
            TimersStateKey.Remove(TimersModified); // we need to remove the key here, so that syncing StateKeySVDRPRemoteTimersPoll takes effect!
          }
1115 1116
          // Must do all following calls with the exact same time!
          // Process ongoing recordings:
1117 1118 1119
          Timers = cTimers::GetTimersWrite(TimersStateKey);
          bool TimersModified = false;
          if (cRecordControls::Process(Timers, Now))
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
             TimersModified = true;
          // Start new recordings:
          if (cTimer *Timer = Timers->GetMatch(Now)) {
             if (!cRecordControls::Start(Timers, Timer))
                Timer->SetPending(true);
             else
                LastTimerChannel = Timer->Channel()->Number();
             TimersModified = true;
             }
          // Make sure timers "see" their channel early enough:
          static time_t LastTimerCheck = 0;
          if (Now - LastTimerCheck > TIMERCHECKDELTA) { // don't do this too often
             InhibitEpgScan = false;
             for (cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
                 if (Timer->Remote())
                    continue;
                 bool InVpsMargin = false;
                 bool NeedsTransponder = false;
                 if (Timer->HasFlags(tfActive) && !Timer->Recording()) {
                    if (Timer->HasFlags(tfVps)) {
                       if (Timer->Matches(Now, true, Setup.VpsMargin)) {
                          InVpsMargin = true;
                          Timer->SetInVpsMargin(InVpsMargin);
                          }
                       else if (Timer->Event()) {
                          InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime();
                          NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME);
                          }
                       else {
                          LOCK_SCHEDULES_READ;
                          const cSchedule *Schedule = Schedules->GetSchedule(Timer->Channel());
                          InVpsMargin = !Schedule; // we must make sure we have the schedule
                          NeedsTransponder = Schedule && !Schedule->PresentSeenWithin(VPSUPTODATETIME);
                          }
                       InhibitEpgScan |= InVpsMargin | NeedsTransponder;
                       }
                    else
                       NeedsTransponder = Timer->Matches(Now, true, TIMERLOOKAHEADTIME);
                    }
                 if (NeedsTransponder || InVpsMargin) {
                    // Find a device that provides the required transponder:
                    cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY);
                    if (!Device && InVpsMargin)
                       Device = cDevice::GetDeviceForTransponder(Timer->Channel(), LIVEPRIORITY);
                    // Switch the device to the transponder:
                    if (Device) {
                       bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme();
                       if (!Device->IsTunedToTransponder(Timer->Channel())) {
                          if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
                             cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
1170
                          dsyslog("switching device %d to channel %d %s (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), *Timer->Channel()->GetChannelID().ToString(), Timer->Channel()->Name());
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
                          if (Device->SwitchChannel(Timer->Channel(), false))
                             Device->SetOccupied(TIMERDEVICETIMEOUT);
                          }
                       if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme())
                          Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel
                       }
                    }
                 }
             LastTimerCheck = Now;
             }
          // Delete expired timers:
1182
          if (Timers->DeleteExpired())
1183
             TimersModified = true;
1184
          // Make sure there is enough free disk space for ongoing recordings:
1185 1186 1187
          int MaxPriority = Timers->GetMaxPriority();
          if (MaxPriority >= 0)
             AssertFreeDiskSpace(MaxPriority);
1188 1189 1190 1191 1192 1193
          TimersStateKey.Remove(TimersModified);
        }
        // Recordings:
        if (!Menu) {
           if (cRecordings::NeedsUpdate())
              cRecordings::Update();