2049 lines
53 KiB
C
Executable File
2049 lines
53 KiB
C
Executable File
#define MODULE_LOG_PREFIX "main"
|
|
|
|
#include "globals.h"
|
|
#include <getopt.h>
|
|
|
|
#include "csctapi/cardreaders.h"
|
|
#include "modules.h"
|
|
#include "readers.h"
|
|
|
|
#include "extapi/coolapi.h"
|
|
#include "module-anticasc.h"
|
|
#include "module-cacheex.h"
|
|
#include "module-cccam.h"
|
|
#include "module-dvbapi.h"
|
|
#include "module-dvbapi-azbox.h"
|
|
#include "module-dvbapi-mca.h"
|
|
#include "module-dvbapi-chancache.h"
|
|
#include "module-gbox-sms.h"
|
|
#include "module-lcd.h"
|
|
#include "module-led.h"
|
|
#include "module-stat.h"
|
|
#include "module-webif.h"
|
|
#include "module-webif-tpl.h"
|
|
#ifdef WEBIF_WIKI
|
|
#include "webif/pages_wiki.h"
|
|
#endif
|
|
#include "module-cw-cycle-check.h"
|
|
#include "module-streamrelay.h"
|
|
#include "oscam-chk.h"
|
|
#include "oscam-cache.h"
|
|
#include "oscam-client.h"
|
|
#include "oscam-config.h"
|
|
#include "oscam-ecm.h"
|
|
#include "oscam-emm.h"
|
|
#include "oscam-emm-cache.h"
|
|
#include "oscam-files.h"
|
|
#include "oscam-garbage.h"
|
|
#include "oscam-lock.h"
|
|
#include "oscam-net.h"
|
|
#include "oscam-reader.h"
|
|
#include "oscam-string.h"
|
|
#include "oscam-time.h"
|
|
#include "oscam-work.h"
|
|
#include "reader-common.h"
|
|
#include "module-gbox.h"
|
|
|
|
#ifdef WITH_EMU
|
|
void add_emu_reader(void);
|
|
#endif
|
|
|
|
#ifdef WITH_SSL
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
|
|
static void ssl_init(void)
|
|
{
|
|
SSL_load_error_strings();
|
|
ERR_load_BIO_strings();
|
|
ERR_load_SSL_strings();
|
|
SSL_library_init();
|
|
}
|
|
|
|
static void ssl_done(void)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER < 0x1010005fL
|
|
ERR_remove_state(0);
|
|
#endif
|
|
ERR_free_strings();
|
|
EVP_cleanup();
|
|
CRYPTO_cleanup_all_ex_data();
|
|
}
|
|
|
|
#else
|
|
static void ssl_init(void) { }
|
|
static void ssl_done(void) { }
|
|
#endif
|
|
|
|
#ifdef WITH_SIGNING
|
|
#include "oscam-signing.h"
|
|
#endif
|
|
|
|
extern char *config_ssl;
|
|
extern char *config_mak;
|
|
|
|
/*****************************************************************************
|
|
Globals
|
|
*****************************************************************************/
|
|
const char *syslog_ident = "oscam";
|
|
static char *oscam_pidfile;
|
|
static char default_pidfile[64];
|
|
|
|
int32_t exit_oscam = 0;
|
|
static struct s_module modules[CS_MAX_MOD];
|
|
|
|
struct s_client *first_client = NULL; // Pointer to clients list, first client is master
|
|
struct s_reader *first_active_reader = NULL; // list of active readers (enable=1 deleted = 0)
|
|
LLIST *configured_readers = NULL; // list of all (configured) readers
|
|
|
|
char cs_confdir[128];
|
|
uint16_t cs_dblevel = 0; // Debug Level
|
|
int32_t thread_pipe[2] = {0, 0};
|
|
static int8_t cs_restart_mode = 1; // Restartmode: 0=off, no restart fork, 1=(default)restart fork, restart by webif, 2=like=1, but also restart on segfaults
|
|
static int8_t cs_capture_SEGV;
|
|
static int8_t cs_dump_stack;
|
|
static uint16_t cs_waittime = 60;
|
|
char cs_tmpdir[200];
|
|
CS_MUTEX_LOCK system_lock;
|
|
CS_MUTEX_LOCK config_lock;
|
|
CS_MUTEX_LOCK gethostbyname_lock;
|
|
CS_MUTEX_LOCK clientlist_lock;
|
|
CS_MUTEX_LOCK readerlist_lock;
|
|
CS_MUTEX_LOCK fakeuser_lock;
|
|
CS_MUTEX_LOCK cwcycle_lock;
|
|
pthread_key_t getclient;
|
|
static int32_t bg;
|
|
static int32_t gbdb;
|
|
static int32_t max_pending = 32;
|
|
|
|
// ecms list
|
|
CS_MUTEX_LOCK ecmcache_lock;
|
|
struct ecm_request_t *ecmcwcache = NULL;
|
|
uint32_t ecmcwcache_size = 0;
|
|
|
|
// pushout deleted list
|
|
CS_MUTEX_LOCK ecm_pushed_deleted_lock;
|
|
struct ecm_request_t *ecm_pushed_deleted = NULL;
|
|
|
|
struct s_config cfg;
|
|
|
|
int log_remove_sensitive = 1;
|
|
|
|
static char *prog_name;
|
|
static char *stb_boxtype;
|
|
static char *stb_boxname;
|
|
|
|
static int32_t oscam_stacksize = 0;
|
|
|
|
/*****************************************************************************
|
|
Statics
|
|
*****************************************************************************/
|
|
/* Prints usage information and information about the built-in modules. */
|
|
static void show_usage(void)
|
|
{
|
|
printf("%s",
|
|
" ___ ____ ___\n"
|
|
" / _ \\/ ___| / __|__ _ _ __ ___\n"
|
|
"| | | \\___ \\| | / _` | '_ ` _ \\\n"
|
|
"| |_| |___) | |_| (_| | | | | | |\n"
|
|
" \\___/|____/ \\___\\__,_|_| |_| |_|\n\n");
|
|
printf("OSCam Cardserver v%s@%s (%s)\n", CS_VERSION, CS_GIT_COMMIT, CS_TARGET);
|
|
printf("Copyright (C) 2009-2026 OSCam developers.\n");
|
|
printf("This program is distributed under GPLv3.\n");
|
|
printf("OSCam is based on Streamboard mp-cardserver v0.9d written by dukat\n");
|
|
printf("Visit %s for more details.\n\n", BOARD_URL);
|
|
|
|
printf(" ConfigDir : %s\n", CS_CONFDIR);
|
|
printf("\n");
|
|
printf(" Usage: oscam [parameters]\n");
|
|
printf("\n Directories:\n");
|
|
printf(" -c, --config-dir <dir> | Read configuration files from <dir>.\n");
|
|
printf(" . Default: %s\n", CS_CONFDIR);
|
|
printf(" -t, --temp-dir <dir> | Set temporary directory to <dir>.\n");
|
|
#if defined(__CYGWIN__)
|
|
printf(" . Default: (OS-TMP)\n");
|
|
#else
|
|
printf(" . Default: /tmp/.oscam\n");
|
|
#endif
|
|
printf("\n Startup:\n");
|
|
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
|
|
printf(" -f, --foreground | Start in the foreground mode.\n");
|
|
#else
|
|
printf(" -b, --daemon | Start in the background as daemon.\n");
|
|
#endif
|
|
printf(" -B, --pidfile <pidfile> | Create pidfile when starting.\n");
|
|
if(config_enabled(WEBIF))
|
|
{
|
|
printf(" -r, --restart <level> | Set restart level:\n");
|
|
printf(" . 0 - Restart disabled (exit on restart request).\n");
|
|
printf(" . 1 - WebIf restart is active (default).\n");
|
|
printf(" . 2 - Like 1, but also restart on segfaults.\n");
|
|
}
|
|
printf(" -w, --wait <secs> | Set how much seconds to wait at startup for the\n");
|
|
printf(" . system clock to be set correctly. Default: 60\n");
|
|
printf("\n Logging:\n");
|
|
printf(" -I, --syslog-ident <ident> | Set syslog ident. Default: oscam\n");
|
|
printf(" -S, --show-sensitive | Do not filter sensitive info (card serials, boxids)\n");
|
|
printf(" . from the logs.\n");
|
|
printf(" -d, --debug <level> | Set debug level mask used for logging:\n");
|
|
printf(" . 0 - No extra debugging (default).\n");
|
|
printf(" . 1 - Detailed error messages.\n");
|
|
printf(" . 2 - ATR parsing info, ECM, EMM and CW dumps.\n");
|
|
printf(" . 4 - Traffic from/to the reader.\n");
|
|
printf(" . 8 - Traffic from/to the clients.\n");
|
|
printf(" . 16 - Traffic to the reader-device on IFD layer.\n");
|
|
printf(" . 32 - Traffic to the reader-device on I/O layer.\n");
|
|
printf(" . 64 - EMM logging.\n");
|
|
printf(" . 128 - DVBAPI logging.\n");
|
|
printf(" . 256 - Loadbalancer logging.\n");
|
|
printf(" . 512 - CACHEEX logging.\n");
|
|
printf(" . 1024 - Client ECM logging.\n");
|
|
printf(" . 2048 - CSP logging.\n");
|
|
printf(" . 4096 - CWC logging.\n");
|
|
#ifdef CS_CACHEEX_AIO
|
|
printf(" . 8192 - CW Cache logging.\n");
|
|
#endif
|
|
printf(" . 65535 - Debug all.\n");
|
|
printf("\n Settings:\n");
|
|
printf(" -p, --pending-ecm <num> | Set the maximum number of pending ECM packets.\n");
|
|
printf(" . Default: 32 Max: 4096\n");
|
|
printf("\n Debug parameters:\n");
|
|
printf(" -a, --crash-dump | Write oscam.crash file on segfault. This option\n");
|
|
printf(" . needs GDB to be installed and OSCam executable to\n");
|
|
printf(" . contain the debug information (run oscam-XXXX.debug)\n");
|
|
printf(" -s, --capture-segfaults | Capture segmentation faults.\n");
|
|
printf(" -g, --gcollect <mode> | Garbage collector debug mode:\n");
|
|
printf(" . 1 - Immediate free.\n");
|
|
printf(" . 2 - Check for double frees.\n");
|
|
printf("\n Information:\n");
|
|
printf(" -h, --help | Show command line help text.\n");
|
|
printf(" -V, --build-info | Show OSCam binary configuration and version.\n");
|
|
}
|
|
|
|
/* Keep the options sorted */
|
|
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
|
|
static const char short_options[] = "aB:fc:d:g:hI:p:r:Sst:uVw:";
|
|
#else
|
|
static const char short_options[] = "aB:bc:d:g:hI:p:r:Sst:uVw:";
|
|
#endif
|
|
|
|
/* Keep the options sorted by short option */
|
|
static const struct option long_options[] =
|
|
{
|
|
{ "crash-dump", no_argument, NULL, 'a' },
|
|
{ "pidfile", required_argument, NULL, 'B' },
|
|
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
|
|
{ "foreground", no_argument, NULL, 'f' },
|
|
#else
|
|
{ "daemon", no_argument, NULL, 'b' },
|
|
#endif
|
|
{ "config-dir", required_argument, NULL, 'c' },
|
|
{ "debug", required_argument, NULL, 'd' },
|
|
{ "gcollect", required_argument, NULL, 'g' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "syslog-ident", required_argument, NULL, 'I' },
|
|
{ "pending-ecm", required_argument, NULL, 'p' },
|
|
{ "restart", required_argument, NULL, 'r' },
|
|
{ "show-sensitive", no_argument, NULL, 'S' },
|
|
{ "capture-segfaults", no_argument, NULL, 's' },
|
|
{ "temp-dir", required_argument, NULL, 't' },
|
|
{ "build-info", no_argument, NULL, 'V' },
|
|
{ "wait", required_argument, NULL, 'w' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static void set_default_dirs_first(void)
|
|
{
|
|
snprintf(cs_confdir, sizeof(cs_confdir), "%s", CS_CONFDIR);
|
|
memset(cs_tmpdir, 0, sizeof(cs_tmpdir)); // will get further procesed trought oscam_files.c !!
|
|
}
|
|
|
|
static void write_versionfile(bool use_stdout);
|
|
|
|
static void parse_cmdline_params(int argc, char **argv)
|
|
{
|
|
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
|
|
bg = 1;
|
|
#endif
|
|
|
|
int i;
|
|
while((i = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF)
|
|
{
|
|
if(i == '?')
|
|
{ fprintf(stderr, "ERROR: Unknown command line parameter: %s\n", argv[optind - 1]); }
|
|
switch(i)
|
|
{
|
|
case 'a': // --crash-dump
|
|
cs_dump_stack = 1;
|
|
break;
|
|
case 'B': // --pidfile
|
|
oscam_pidfile = optarg;
|
|
break;
|
|
case 'f': // --foreground
|
|
bg = 0;
|
|
break;
|
|
case 'b': // --daemon
|
|
bg = 1;
|
|
break;
|
|
case 'c': // --config-dir
|
|
cs_strncpy(cs_confdir, optarg, sizeof(cs_confdir));
|
|
break;
|
|
case 'd': // --debug
|
|
cs_dblevel = atoi(optarg);
|
|
break;
|
|
case 'g': // --gcollect
|
|
gbdb = atoi(optarg);
|
|
break;
|
|
case 'h': // --help
|
|
show_usage();
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
case 'I': // --syslog-ident
|
|
syslog_ident = optarg;
|
|
break;
|
|
case 'p': // --pending-ecm
|
|
max_pending = atoi(optarg) <= 0 ? 32 : MIN(atoi(optarg), 4096);
|
|
break;
|
|
case 'r': // --restart
|
|
if(config_enabled(WEBIF))
|
|
{
|
|
cs_restart_mode = atoi(optarg);
|
|
}
|
|
break;
|
|
case 'S': // --show-sensitive
|
|
log_remove_sensitive = !log_remove_sensitive;
|
|
break;
|
|
case 's': // --capture-segfaults
|
|
cs_capture_SEGV = 1;
|
|
break;
|
|
case 't': // --temp-dir
|
|
{
|
|
mkdir(optarg, S_IRWXU);
|
|
int j = open(optarg, O_RDONLY);
|
|
if(j >= 0)
|
|
{
|
|
close(j);
|
|
cs_strncpy(cs_tmpdir, optarg, sizeof(cs_tmpdir));
|
|
}
|
|
else
|
|
{
|
|
printf("WARNING: Temp dir does not exist. Using default value.\n");
|
|
}
|
|
break;
|
|
}
|
|
case 'V': // --build-info
|
|
write_versionfile(true);
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
case 'w': // --wait
|
|
cs_waittime = strtoul(optarg, NULL, 10);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define write_conf(CONFIG_VAR, text) \
|
|
fprintf(fp, "%-40s %s\n", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no")
|
|
|
|
#define write_readerconf(CONFIG_VAR, text) \
|
|
fprintf(fp, "%-40s %s\n", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no - no EMM support!")
|
|
|
|
#define write_cardreaderconf(CONFIG_VAR, text) \
|
|
fprintf(fp, "%s%-29s %s\n", "cardreader_", text ":", config_enabled(CONFIG_VAR) ? "yes" : "no")
|
|
|
|
static void write_versionfile(bool use_stdout)
|
|
{
|
|
#ifdef WITH_SIGNING
|
|
if(!init_signing_info(prog_name)) cs_exit_oscam();
|
|
#endif
|
|
FILE *fp = stdout;
|
|
if(!use_stdout)
|
|
{
|
|
char targetfile[256];
|
|
fp = fopen(get_tmp_dir_filename(targetfile, sizeof(targetfile), "oscam.version"), "w");
|
|
if(!fp)
|
|
{
|
|
cs_log("Cannot open %s (errno=%d %s)", targetfile, errno, strerror(errno));
|
|
return;
|
|
}
|
|
struct tm st;
|
|
time_t walltime = cs_time();
|
|
localtime_r(&walltime, &st);
|
|
fprintf(fp, "Unix Starttime: %" PRId64 "\n", (int64_t)walltime);
|
|
fprintf(fp, "Starttime: %02d.%02d.%04d %02d:%02d:%02d\n",
|
|
st.tm_mday, st.tm_mon + 1, st.tm_year + 1900,
|
|
st.tm_hour, st.tm_min, st.tm_sec);
|
|
}
|
|
|
|
fprintf(fp, "Build Date: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
|
|
fprintf(fp, "Version: %s@%s\n", "UNKNOWN", "UNKNOWN"); // Placeholder if not defined by build system
|
|
fprintf(fp, "Compiler: %s\n", "UNKNOWN"); // Placeholder if not defined by build system
|
|
#ifdef USE_COMPRESS
|
|
fprintf(fp, "Compression: %s, level %s\n", COMP_VERSION, COMP_LEVEL);
|
|
#endif
|
|
fprintf(fp, "Box Type: %s (%s)\n", boxtype_get(), boxname_get());
|
|
fprintf(fp, "PID: %d\n", getppid());
|
|
fprintf(fp, "TempDir: %s\n", cs_tmpdir);
|
|
#ifdef MODULE_GBOX
|
|
if(cfg.gbox_tmp_dir == NULL)
|
|
{
|
|
fprintf(fp, "GBox tmp_dir: not defined using: %s\n", cs_tmpdir);
|
|
}
|
|
else
|
|
{
|
|
fprintf(fp, "GBox tmp_dir: %s\n", cfg.gbox_tmp_dir);
|
|
}
|
|
#endif
|
|
|
|
fprintf(fp, "ConfigDir: %s\n", cs_confdir);
|
|
|
|
#ifdef WEBIF
|
|
fprintf(fp, "WebifPort: %d\n", cfg.http_port);
|
|
#endif
|
|
|
|
#ifdef WITH_SIGNING
|
|
fprintf(fp, "\n");
|
|
fprintf(fp, "Signature: %s\n", (osi.is_verified ? "Valid - successfully verified using built-in Public Key" : "Invalid - wrong signature or internal error occured!"));
|
|
fprintf(fp, " Binary: %s%s\n", osi.resolved_binfile, osi.binfile_exists ? "" : " is inaccessible!");
|
|
fprintf(fp, " Signer: %s\n", config_ssl);
|
|
fprintf(fp, " SHA256: %s\n", osi.hash_sha256 ? osi.hash_sha256 : "n/a");
|
|
fprintf(fp, "Certificate: %s %s Certificate\n", ((osi.cert_is_valid_self || osi.cert_is_valid_system) ? "Trusted" : "Untrusted"), (osi.cert_is_cacert ? "CA" : "self signed"));
|
|
fprintf(fp, " Subject: %s\n", osi.cert_subject);
|
|
fprintf(fp, " Issuer: %s\n", osi.cert_issuer);
|
|
fprintf(fp, " Version: %d\n", osi.cert_version);
|
|
fprintf(fp, " Serial: %s\n", osi.cert_serial);
|
|
fprintf(fp, " Fingerprint: %s\n", osi.cert_fingerprint);
|
|
fprintf(fp, " Valid from: %s\n", osi.cert_valid_from);
|
|
fprintf(fp, " Valid to: %s\n", osi.cert_valid_to);
|
|
fprintf(fp, " Status: %s\n", (osi.cert_is_expired ? "Expired" : "Valid"));
|
|
fprintf(fp, " Type: %s\n", osi.pkey_type);
|
|
if (osi.system_ca_file) fprintf(fp, "System CA: %s\n", osi.system_ca_file);
|
|
#endif
|
|
|
|
fprintf(fp, "\n");
|
|
write_conf(WEBIF, "Web interface support");
|
|
write_conf(WEBIF_LIVELOG, "LiveLog support");
|
|
write_conf(WEBIF_JQUERY, "jQuery support intern");
|
|
write_conf(WEBIF_WIKI, "Embedded wiki help system");
|
|
write_conf(WITH_COMPRESS_WEBIF, "Compressed pages");
|
|
write_conf(WITH_SSL, "SSL support");
|
|
write_conf(HAVE_DVBAPI, "DVB API support");
|
|
if(config_enabled(HAVE_DVBAPI))
|
|
{
|
|
write_conf(WITH_EXTENDED_CW, "DVB API with extended CW API support");
|
|
write_conf(MODULE_STREAMRELAY, "DVB API with Stream Relay support");
|
|
write_conf(WITH_AZBOX, "DVB API with AZBOX support");
|
|
write_conf(WITH_MCA, "DVB API with MCA support");
|
|
write_conf(WITH_COOLAPI, "DVB API with COOLAPI support");
|
|
write_conf(WITH_COOLAPI2, "DVB API with COOLAPI2 support");
|
|
write_conf(WITH_STAPI, "DVB API with STAPI support");
|
|
write_conf(WITH_STAPI5, "DVB API with STAPI5 support");
|
|
write_conf(WITH_NEUTRINO, "DVB API with NEUTRINO support");
|
|
write_conf(READ_SDT_CHARSETS, "DVB API read-sdt charsets");
|
|
}
|
|
write_conf(CS_ANTICASC, "Anti-cascading support");
|
|
write_conf(WITH_DEBUG, "Debug mode");
|
|
write_conf(MODULE_MONITOR, "Monitor");
|
|
write_conf(WITH_LB, "Loadbalancing support");
|
|
write_conf(CS_CACHEEX, "Cache exchange support");
|
|
#ifdef CS_CACHEEX_AIO
|
|
write_conf(CS_CACHEEX_AIO, "Cache exchange AIO support");
|
|
#endif
|
|
write_conf(CW_CYCLE_CHECK, "CW Cycle Check support");
|
|
write_conf(LCDSUPPORT, "LCD support");
|
|
write_conf(LEDSUPPORT, "LED support");
|
|
write_conf(CLOCKFIX, "Clockfix with realtime clock");
|
|
write_conf(IPV6SUPPORT, "IPv6 support");
|
|
#if defined(__arm__) || defined(__aarch64__)
|
|
write_conf(WITH_ARM_NEON, "ARM NEON (SIMD/MPE) support");
|
|
#endif
|
|
#ifdef WITH_SIGNING
|
|
write_conf(WITH_SIGNING, "Binary signing support");
|
|
#endif
|
|
write_conf(WITH_EMU, "Emulator support");
|
|
write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key");
|
|
|
|
fprintf(fp, "\n");
|
|
write_conf(MODULE_CAMD33, "camd 3.3x");
|
|
write_conf(MODULE_CAMD35, "camd 3.5 UDP");
|
|
write_conf(MODULE_CAMD35_TCP, "camd 3.5 TCP");
|
|
write_conf(MODULE_NEWCAMD, "newcamd");
|
|
write_conf(MODULE_CCCAM, "CCcam");
|
|
write_conf(MODULE_CCCSHARE, "CCcam share");
|
|
write_conf(MODULE_GBOX, "gbox");
|
|
write_conf(MODULE_RADEGAST, "radegast");
|
|
write_conf(MODULE_SCAM, "scam");
|
|
write_conf(MODULE_SERIAL, "serial");
|
|
write_conf(MODULE_CONSTCW, "constant CW");
|
|
write_conf(MODULE_PANDORA, "Pandora");
|
|
write_conf(MODULE_GHTTP, "ghttp");
|
|
write_conf(MODULE_STREAMRELAY, "Streamrelay");
|
|
|
|
fprintf(fp, "\n");
|
|
write_conf(WITH_CARDREADER, "Reader support");
|
|
if(config_enabled(WITH_CARDREADER))
|
|
{
|
|
fprintf(fp, "\n");
|
|
write_readerconf(READER_NAGRA, "Nagra");
|
|
write_readerconf(READER_NAGRA_MERLIN, "Nagra Merlin");
|
|
write_readerconf(READER_IRDETO, "Irdeto");
|
|
write_readerconf(READER_CONAX, "Conax");
|
|
write_readerconf(READER_CRYPTOWORKS, "Cryptoworks");
|
|
write_readerconf(READER_SECA, "Seca");
|
|
write_readerconf(READER_VIACCESS, "Viaccess");
|
|
write_readerconf(READER_VIDEOGUARD, "NDS Videoguard");
|
|
write_readerconf(READER_DRE, "DRE Crypt");
|
|
write_readerconf(READER_TONGFANG, "TONGFANG");
|
|
write_readerconf(READER_BULCRYPT, "Bulcrypt");
|
|
write_readerconf(READER_GRIFFIN, "Griffin");
|
|
write_readerconf(READER_DGCRYPT, "DGCrypt");
|
|
fprintf(fp, "\n");
|
|
write_cardreaderconf(CARDREADER_PHOENIX, "phoenix");
|
|
write_cardreaderconf(CARDREADER_DRECAS, "drecas");
|
|
write_cardreaderconf(CARDREADER_INTERNAL_AZBOX, "internal_azbox");
|
|
write_cardreaderconf(CARDREADER_INTERNAL_COOLAPI, "internal_coolapi");
|
|
write_cardreaderconf(CARDREADER_INTERNAL_COOLAPI2, "internal_coolapi2");
|
|
write_cardreaderconf(CARDREADER_INTERNAL_AMSMC, "internal_amsmc");
|
|
write_cardreaderconf(CARDREADER_INTERNAL_SCI, "internal_sci");
|
|
write_cardreaderconf(CARDREADER_SC8IN1, "sc8in1");
|
|
write_cardreaderconf(CARDREADER_MP35, "mp35");
|
|
write_cardreaderconf(CARDREADER_SMARGO, "smargo");
|
|
write_cardreaderconf(CARDREADER_PCSC, "pcsc");
|
|
write_cardreaderconf(CARDREADER_SMART, "smartreader");
|
|
write_cardreaderconf(CARDREADER_DB2COM, "db2com");
|
|
write_cardreaderconf(CARDREADER_STAPI, "stapi");
|
|
write_cardreaderconf(CARDREADER_STAPI5, "stapi5");
|
|
write_cardreaderconf(CARDREADER_STINGER, "stinger");
|
|
}
|
|
else
|
|
{
|
|
write_readerconf(WITH_CARDREADER, "Reader Support");
|
|
}
|
|
if(!use_stdout)
|
|
{ fclose(fp); }
|
|
}
|
|
#undef write_conf
|
|
#undef write_readerconf
|
|
#undef write_cardreaderconf
|
|
|
|
static void remove_versionfile(void)
|
|
{
|
|
char targetfile[256];
|
|
unlink(get_tmp_dir_filename(targetfile, sizeof(targetfile), "oscam.version"));
|
|
}
|
|
|
|
#define report_emm_support(CONFIG_VAR, text) \
|
|
do { \
|
|
if (!config_enabled(CONFIG_VAR)) \
|
|
cs_log_dbg(D_TRACE, "Binary without %s module - no EMM processing for %s possible!", text, text); \
|
|
} while(0)
|
|
|
|
static void do_report_emm_support(void)
|
|
{
|
|
if(!config_enabled(WITH_CARDREADER))
|
|
{
|
|
cs_log("Binary without Cardreader Support! No EMM processing possible!");
|
|
}
|
|
else
|
|
{
|
|
report_emm_support(READER_NAGRA, "Nagra");
|
|
report_emm_support(READER_NAGRA_MERLIN, "Nagra Merlin");
|
|
report_emm_support(READER_IRDETO, "Irdeto");
|
|
report_emm_support(READER_CONAX, "Conax");
|
|
report_emm_support(READER_CRYPTOWORKS, "Cryptoworks");
|
|
report_emm_support(READER_SECA, "Seca");
|
|
report_emm_support(READER_VIACCESS, "Viaccess");
|
|
report_emm_support(READER_VIDEOGUARD, "NDS Videoguard");
|
|
report_emm_support(READER_DRE, "DRE Crypt");
|
|
report_emm_support(READER_TONGFANG, "TONGFANG");
|
|
report_emm_support(READER_BULCRYPT, "Bulcrypt");
|
|
report_emm_support(READER_GRIFFIN, "Griffin");
|
|
report_emm_support(READER_DGCRYPT, "DGCrypt");
|
|
}
|
|
}
|
|
#undef report_emm_support
|
|
|
|
#ifdef NEED_DAEMON
|
|
// The compat function is not called daemon() because this may cause problems.
|
|
static int32_t do_daemon(int32_t nochdir, int32_t noclose)
|
|
{
|
|
int32_t fd;
|
|
|
|
switch(fork())
|
|
{
|
|
case -1:
|
|
return (-1);
|
|
case 0:
|
|
break;
|
|
default:
|
|
_exit(0);
|
|
}
|
|
|
|
if(setsid() == (-1))
|
|
{ return (-1); }
|
|
|
|
if(!nochdir)
|
|
{ (void)chdir("/"); }
|
|
|
|
if(!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1)
|
|
{
|
|
(void)dup2(fd, STDIN_FILENO);
|
|
(void)dup2(fd, STDOUT_FILENO);
|
|
(void)dup2(fd, STDERR_FILENO);
|
|
if(fd > 2)
|
|
{ (void)close(fd); }
|
|
}
|
|
return (0);
|
|
}
|
|
#else
|
|
#define do_daemon daemon
|
|
#endif
|
|
|
|
/*
|
|
* flags: 1 = restart, 2 = don't modify if SIG_IGN, may be combined
|
|
*/
|
|
static void set_signal_handler(int32_t sig, int32_t flags, void (*sighandler))
|
|
{
|
|
struct sigaction sa;
|
|
sigaction(sig, (struct sigaction *) 0, &sa);
|
|
if(!((flags & 2) && (sa.sa_handler == SIG_IGN)))
|
|
{
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = (flags & 1) ? SA_RESTART : 0;
|
|
sa.sa_handler = sighandler;
|
|
sigaction(sig, &sa, (struct sigaction *) 0);
|
|
}
|
|
}
|
|
|
|
static void cs_master_alarm(void)
|
|
{
|
|
cs_log("PANIC: master deadlock!");
|
|
fprintf(stderr, "PANIC: master deadlock!");
|
|
fflush(stderr);
|
|
}
|
|
|
|
static void cs_sigpipe(void)
|
|
{
|
|
if(cs_dblevel & D_ALL_DUMP)
|
|
{ cs_log("Got sigpipe signal -> captured"); }
|
|
}
|
|
|
|
static void cs_dummy(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Switch debuglevel forward one step (called when receiving SIGUSR1). */
|
|
static void cs_debug_level(void)
|
|
{
|
|
switch(cs_dblevel)
|
|
{
|
|
case 0:
|
|
cs_dblevel = 1;
|
|
break;
|
|
case 128:
|
|
cs_dblevel = 255;
|
|
break;
|
|
case 255:
|
|
cs_dblevel = 0;
|
|
break;
|
|
default:
|
|
cs_dblevel <<= 1;
|
|
}
|
|
|
|
cs_log("debug_level=%d", cs_dblevel);
|
|
}
|
|
|
|
/**
|
|
* write stacktrace to oscam.crash. file is always appended
|
|
* Usage:
|
|
* 1. compile oscam with debug parameters (Makefile: DS_OPTS="-ggdb")
|
|
* 2. you need gdb installed and working on the local machine
|
|
* 3. start oscam with parameter: -a
|
|
*/
|
|
static void cs_dumpstack(int32_t sig)
|
|
{
|
|
FILE *fp = fopen("oscam.crash", "a+");
|
|
|
|
time_t timep;
|
|
char buf[200];
|
|
|
|
time(&timep);
|
|
cs_ctime_r(&timep, buf);
|
|
|
|
fprintf(stderr, "crashed with signal %d on %swriting oscam.crash\n", sig, buf);
|
|
|
|
if (fp)
|
|
{
|
|
fprintf(fp, "%sOSCam cardserver v%s@%s (%s)\n", buf, CS_VERSION, CS_GIT_COMMIT, CS_TARGET);
|
|
fprintf(fp, "FATAL: Signal %d: %s Fault. Logged StackTrace:\n\n", sig, (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
|
|
fclose(fp);
|
|
}
|
|
|
|
FILE *cmd = fopen("/tmp/gdbcmd", "w");
|
|
if (cmd)
|
|
{
|
|
fputs("bt\n", cmd);
|
|
fputs("thread apply all bt\n", cmd);
|
|
fclose(cmd);
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf) - 1, "gdb %s %d -batch -x /tmp/gdbcmd >> oscam.crash", prog_name, getpid());
|
|
if(system(buf) == -1)
|
|
{ fprintf(stderr, "Fatal error on trying to start gdb process."); }
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
/**
|
|
* called by signal SIGHUP
|
|
*
|
|
* reloads configs:
|
|
* - useraccounts (oscam.user)
|
|
* - readers (oscam.server)
|
|
* - services ids (oscam.srvid)
|
|
* - tier ids (oscam.tiers)
|
|
* Also clears anticascading stats.
|
|
**/
|
|
static void cs_reload_config(void)
|
|
{
|
|
static pthread_mutex_t mutex;
|
|
static int8_t mutex_init = 0;
|
|
|
|
if(!mutex_init)
|
|
{
|
|
SAFE_MUTEX_INIT(&mutex, NULL);
|
|
mutex_init = 1;
|
|
}
|
|
|
|
if(pthread_mutex_trylock(&mutex))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(cfg.reload_useraccounts)
|
|
{
|
|
cs_accounts_chk();
|
|
}
|
|
|
|
if(cfg.reload_readers)
|
|
{
|
|
reload_readerdb();
|
|
}
|
|
|
|
if(cfg.reload_provid)
|
|
{
|
|
init_provid();
|
|
}
|
|
|
|
if(cfg.reload_services_ids)
|
|
{
|
|
init_srvid();
|
|
}
|
|
|
|
if(cfg.reload_tier_ids)
|
|
{
|
|
init_tierid();
|
|
}
|
|
|
|
if(cfg.reload_fakecws)
|
|
{
|
|
init_fakecws();
|
|
}
|
|
|
|
if(cfg.reload_ac_stat)
|
|
{
|
|
ac_init_stat();
|
|
}
|
|
|
|
if(cfg.reload_log)
|
|
{
|
|
cs_reopen_log(); // FIXME: aclog.log, emm logs, cw logs (?)
|
|
}
|
|
|
|
SAFE_MUTEX_UNLOCK(&mutex);
|
|
}
|
|
|
|
/* Sets signal handlers to ignore for early startup of OSCam because for example log
|
|
could cause SIGPIPE errors and the normal signal handlers can't be used at this point. */
|
|
static void init_signal_pre(void)
|
|
{
|
|
set_signal_handler(SIGPIPE , 1, SIG_IGN);
|
|
set_signal_handler(SIGWINCH, 1, SIG_IGN);
|
|
set_signal_handler(SIGALRM , 1, SIG_IGN);
|
|
set_signal_handler(SIGHUP , 1, SIG_IGN);
|
|
}
|
|
|
|
/* Sets the signal handlers.*/
|
|
static void init_signal(void)
|
|
{
|
|
set_signal_handler(SIGINT, 3, cs_exit);
|
|
#if defined(__APPLE__)
|
|
set_signal_handler(SIGEMT, 3, cs_exit);
|
|
#endif
|
|
set_signal_handler(SIGTERM, 3, cs_exit);
|
|
|
|
set_signal_handler(SIGWINCH, 1, SIG_IGN);
|
|
set_signal_handler(SIGPIPE, 0, cs_sigpipe);
|
|
set_signal_handler(SIGALRM, 0, cs_master_alarm);
|
|
set_signal_handler(SIGHUP, 1, cs_reload_config);
|
|
set_signal_handler(SIGUSR1, 1, cs_debug_level);
|
|
set_signal_handler(SIGUSR2, 1, cs_card_info);
|
|
set_signal_handler(OSCAM_SIGNAL_WAKEUP, 0, cs_dummy);
|
|
|
|
if(cs_capture_SEGV)
|
|
{
|
|
set_signal_handler(SIGSEGV, 1, cs_exit);
|
|
set_signal_handler(SIGBUS, 1, cs_exit);
|
|
}
|
|
else if(cs_dump_stack)
|
|
{
|
|
set_signal_handler(SIGSEGV, 1, cs_dumpstack);
|
|
set_signal_handler(SIGBUS, 1, cs_dumpstack);
|
|
}
|
|
|
|
cs_log("signal handling initialized");
|
|
return;
|
|
}
|
|
|
|
void cs_exit(int32_t sig)
|
|
{
|
|
if(cs_dump_stack && (sig == SIGSEGV || sig == SIGBUS || sig == SIGQUIT))
|
|
{ cs_dumpstack(sig); }
|
|
|
|
set_signal_handler(SIGHUP , 1, SIG_IGN);
|
|
set_signal_handler(SIGPIPE, 1, SIG_IGN);
|
|
|
|
struct s_client *cl = cur_client();
|
|
if(!cl)
|
|
{ return; }
|
|
|
|
// this is very important - do not remove
|
|
if(cl->typ != 's')
|
|
{
|
|
cs_log_dbg(D_TRACE, "thread %8lX ended!", (unsigned long)pthread_self());
|
|
|
|
free_client(cl);
|
|
|
|
// Restore signals before exiting thread
|
|
set_signal_handler(SIGPIPE, 0, cs_sigpipe);
|
|
set_signal_handler(SIGHUP, 1, cs_reload_config);
|
|
|
|
pthread_exit(NULL);
|
|
return;
|
|
}
|
|
|
|
if(!exit_oscam)
|
|
{ exit_oscam = sig ? sig : 1; }
|
|
}
|
|
|
|
static char *read_line_from_file(char *fname, char *buf, int bufsz)
|
|
{
|
|
memset(buf, 0, bufsz);
|
|
FILE *f = fopen(fname, "r");
|
|
if (!f)
|
|
return NULL;
|
|
while (fgets(buf, bufsz, f))
|
|
{
|
|
if (strstr(buf,"\n")) // we need only the first line
|
|
{
|
|
buf[cs_strlen(buf)-1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
if (buf[0])
|
|
return buf;
|
|
return NULL;
|
|
}
|
|
|
|
static void init_machine_info(void)
|
|
{
|
|
struct utsname buffer;
|
|
if (uname(&buffer) == 0)
|
|
{
|
|
cs_log("System name = %s", buffer.sysname);
|
|
cs_log("Host name = %s", buffer.nodename);
|
|
cs_log("Release = %s", buffer.release);
|
|
cs_log("Version = %s", buffer.version);
|
|
cs_log("Machine = %s", buffer.machine);
|
|
} else {
|
|
cs_log("ERROR: uname call failed: %s", strerror(errno));
|
|
}
|
|
|
|
#if !defined(__linux__)
|
|
return;
|
|
#endif
|
|
|
|
// Linux only functionality
|
|
char boxtype[128];
|
|
boxtype[0] = 0;
|
|
char model[64];
|
|
model[0] = 0;
|
|
char vumodel[64];
|
|
vumodel[0] = 0;
|
|
int8_t azmodel = 0;
|
|
FILE *f;
|
|
|
|
if ((f = fopen("/proc/stb/info/azmodel", "r"))){ azmodel = 1; fclose(f);}
|
|
read_line_from_file("/proc/stb/info/model", model, sizeof(model));
|
|
read_line_from_file("/proc/stb/info/boxtype", boxtype, sizeof(boxtype));
|
|
read_line_from_file("/proc/stb/info/vumodel", vumodel, sizeof(vumodel));
|
|
if (vumodel[0] && !boxtype[0] && !azmodel)
|
|
{
|
|
snprintf(boxtype, sizeof(boxtype), "vu%s", vumodel);
|
|
}
|
|
if (!boxtype[0] && azmodel)
|
|
snprintf(boxtype, sizeof(boxtype), "Azbox-%s", model);
|
|
|
|
// Detect dreambox type
|
|
if (strcasecmp(buffer.machine, "ppc") == 0 && !model[0] && !boxtype[0])
|
|
{
|
|
char line[128], *p;
|
|
int have_dreambox = 0;
|
|
if ((f = fopen("/proc/cpuinfo", "r")))
|
|
{
|
|
while (fgets(line, sizeof(line), f))
|
|
{
|
|
if (strstr(line, "STBx25xx")) have_dreambox++;
|
|
if (strstr(line, "pvr" )) have_dreambox++;
|
|
if (strstr(line, "Dreambox")) have_dreambox++;
|
|
if (strstr(line, "9.80" )) have_dreambox++;
|
|
if (strstr(line, "63MHz" )) have_dreambox++;
|
|
}
|
|
fclose(f);
|
|
have_dreambox = have_dreambox == 5 ? 1 : 0; // Need to find all 5 strings
|
|
}
|
|
if (have_dreambox)
|
|
{
|
|
if (read_line_from_file("/proc/meminfo", line, sizeof(line)) && (p = strchr(line, ' ')))
|
|
{
|
|
unsigned long memtotal = strtoul(p, NULL, 10);
|
|
if (memtotal > 40000)
|
|
snprintf(boxtype, sizeof(boxtype), "%s", "dm600pvr");
|
|
else
|
|
snprintf(boxtype, sizeof(boxtype), "%s", "dm500");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!boxtype[0] && !strcasecmp(model, "dm800") && !strcasecmp(buffer.machine, "armv7l"))
|
|
snprintf(boxtype, sizeof(boxtype), "%s", "su980");
|
|
|
|
if (!boxtype[0])
|
|
{
|
|
uint8_t *pos;
|
|
pos = (uint8_t *)memchr(buffer.release, 'd', sizeof(buffer.release));
|
|
if(pos)
|
|
{
|
|
if((!memcmp(pos, "dbox2", sizeof("dbox2"))) && !strcasecmp(buffer.machine, "ppc"))
|
|
{
|
|
snprintf(boxtype, sizeof(boxtype), "%s", "dbox2");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (model[0])
|
|
cs_log("Stb model = %s", model);
|
|
|
|
if (vumodel[0])
|
|
cs_log("Stb vumodel = vu%s", vumodel);
|
|
|
|
if (boxtype[0])
|
|
{
|
|
char boxname[128];
|
|
if(!strcasecmp(boxtype,"ini-8000am")){snprintf(boxname, sizeof(boxname), "%s", "Atemio Nemesis");}
|
|
else if(!strcasecmp(boxtype,"ini-9000ru")){snprintf(boxname, sizeof(boxname), "%s", "Sezam Marvel");}
|
|
else if(!strcasecmp(boxtype,"ini-8000sv")){snprintf(boxname, sizeof(boxname), "%s", "Miraclebox Ultra");}
|
|
else if(!strcasecmp(boxtype,"ini-9000de")){snprintf(boxname, sizeof(boxname), "%s", "Xpeed LX3");}
|
|
else boxname[0] = 0;
|
|
if(boxname[0]){cs_log("Stb boxname = %s", boxname); stb_boxname = cs_strdup(boxname);}
|
|
cs_log("Stb boxtype = %s", boxtype);
|
|
}
|
|
|
|
if (boxtype[0])
|
|
stb_boxtype = cs_strdup(boxtype);
|
|
else if (model[0])
|
|
stb_boxtype = cs_strdup(model);
|
|
}
|
|
|
|
const char *boxtype_get(void)
|
|
{
|
|
return stb_boxtype ? stb_boxtype : "generic";
|
|
}
|
|
|
|
const char *boxname_get(void)
|
|
{
|
|
return stb_boxname ? stb_boxname : "generic";
|
|
}
|
|
|
|
bool boxtype_is(const char *boxtype)
|
|
{
|
|
return strcasecmp(boxtype_get(), boxtype) == 0;
|
|
}
|
|
|
|
bool boxname_is(const char *boxname)
|
|
{
|
|
return strcasecmp(boxname_get(), boxname) == 0;
|
|
}
|
|
|
|
/* Checks if the date of the system is correct and waits if necessary. */
|
|
static void init_check(void)
|
|
{
|
|
char *ptr = __DATE__;
|
|
int32_t month, year = atoi(ptr + cs_strlen(ptr) - 4), day = atoi(ptr + 4);
|
|
if(day > 0 && day < 32 && year > 2010 && year < 9999)
|
|
{
|
|
struct tm timeinfo;
|
|
char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
for(month = 0; month < 12; ++month)
|
|
{
|
|
if(!strncmp(ptr, months[month], 3)) { break; }
|
|
}
|
|
if(month > 11) { month = 0; }
|
|
memset(&timeinfo, 0, sizeof(timeinfo));
|
|
timeinfo.tm_mday = day;
|
|
timeinfo.tm_mon = month;
|
|
timeinfo.tm_year = year - 1900;
|
|
time_t builddate = mktime(&timeinfo) - 86400;
|
|
int32_t i = 0;
|
|
while(time((time_t *)0) < builddate)
|
|
{
|
|
if(i == 0) { cs_log("The current system time is smaller than the build date (%s). Waiting up to %d seconds for time to correct", ptr, cs_waittime); }
|
|
cs_sleepms(1000);
|
|
++i;
|
|
if(i > cs_waittime)
|
|
{
|
|
cs_log("Waiting was not successful. OSCam will be started but is UNSUPPORTED this way. Do not report any errors with this version.");
|
|
break;
|
|
}
|
|
}
|
|
// adjust login time of first client
|
|
if(i > 0) { first_client->login = time((time_t *)0); }
|
|
}
|
|
}
|
|
|
|
#ifdef __linux__
|
|
#include <sys/prctl.h>
|
|
// PR_SET_NAME is introduced in 2.6.9 (which is ancient, released 18 Oct 2004)
|
|
// but apparantly we can't count on having at least that version :(
|
|
#ifndef PR_SET_NAME
|
|
#define PR_SET_NAME 15
|
|
#endif
|
|
// Set the thread name (comm) under linux (the limit is 16 chars)
|
|
void set_thread_name(const char *thread_name)
|
|
{
|
|
prctl(PR_SET_NAME, thread_name, NULL, NULL, NULL);
|
|
}
|
|
#else
|
|
void set_thread_name(const char *UNUSED(thread_name)) { }
|
|
#endif
|
|
|
|
|
|
static void fix_stacksize(void)
|
|
{
|
|
// Changing the default stack size is generally a bad idea.
|
|
// We are doing it anyway at the moment, because we are using several threads,
|
|
// and are running on machnies with little RAM.
|
|
// HOWEVER, as we do not know which minimal stack size is needed to run
|
|
// oscam without SEQFAULT (stack overflow), this is risky business.
|
|
// If after a code change SEQFAULTs related to stack overflow appear,
|
|
// increase OSCAM_STACK_MIN or remove the calls to SAFE_ATTR_SETSTACKSIZE.
|
|
|
|
#ifndef PTHREAD_STACK_MIN
|
|
#define PTHREAD_STACK_MIN 64000
|
|
#endif
|
|
#define OSCAM_STACK_MIN PTHREAD_STACK_MIN + 32768
|
|
|
|
if(oscam_stacksize < OSCAM_STACK_MIN)
|
|
{
|
|
long pagesize = sysconf(_SC_PAGESIZE);
|
|
if(pagesize < 1)
|
|
{
|
|
oscam_stacksize = OSCAM_STACK_MIN;
|
|
return;
|
|
}
|
|
|
|
oscam_stacksize = ((OSCAM_STACK_MIN) / pagesize + 1) * pagesize;
|
|
}
|
|
}
|
|
|
|
/* Starts a thread named nameroutine with the start function startroutine. */
|
|
int32_t start_thread(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize)
|
|
{
|
|
pthread_t temp;
|
|
pthread_attr_t attr;
|
|
|
|
cs_log_dbg(D_TRACE, "starting thread %s", nameroutine);
|
|
|
|
SAFE_ATTR_INIT(&attr);
|
|
|
|
if(modify_stacksize)
|
|
{ SAFE_ATTR_SETSTACKSIZE(&attr, oscam_stacksize); }
|
|
|
|
int32_t ret = pthread_create(pthread == NULL ? &temp : pthread, &attr, startroutine, arg);
|
|
if(ret)
|
|
{ cs_log("ERROR: can't create %s thread (errno=%d %s)", nameroutine, ret, strerror(ret)); }
|
|
else
|
|
{
|
|
cs_log_dbg(D_TRACE, "%s thread started", nameroutine);
|
|
|
|
if(detach)
|
|
{ pthread_detach(pthread == NULL ? temp : *pthread); }
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int32_t start_thread_nolog(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize)
|
|
{
|
|
pthread_t temp;
|
|
pthread_attr_t attr;
|
|
|
|
SAFE_ATTR_INIT(&attr);
|
|
|
|
if(modify_stacksize)
|
|
{ SAFE_ATTR_SETSTACKSIZE(&attr, oscam_stacksize); }
|
|
|
|
int32_t ret = pthread_create(pthread == NULL ? &temp : pthread, &attr, startroutine, arg);
|
|
if(ret)
|
|
{ fprintf(stderr, "ERROR: can't create %s thread (errno=%d %s)", nameroutine, ret, strerror(ret)); }
|
|
else
|
|
{
|
|
if(detach)
|
|
{ pthread_detach(pthread == NULL ? temp : *pthread); }
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Allows to kill another thread specified through the client cl with locking.
|
|
If the own thread has to be cancelled, cs_exit or cs_disconnect_client has to be used. */
|
|
void kill_thread(struct s_client *cl)
|
|
{
|
|
if(!cl || cl->kill) { return; }
|
|
if(cl == cur_client())
|
|
{
|
|
cs_log("Trying to kill myself, exiting.");
|
|
cs_exit(0);
|
|
}
|
|
add_job(cl, ACTION_CLIENT_KILL, NULL, 0); //add kill job, ...
|
|
cl->kill = 1; //then set kill flag!
|
|
}
|
|
|
|
struct s_module *get_module(struct s_client *cl)
|
|
{
|
|
return &modules[cl->module_idx];
|
|
}
|
|
|
|
void module_reader_set(struct s_reader *rdr)
|
|
{
|
|
int i;
|
|
if(!is_cascading_reader(rdr))
|
|
{ return; }
|
|
for(i = 0; i < CS_MAX_MOD; i++)
|
|
{
|
|
struct s_module *module = &modules[i];
|
|
if(module->num && module->num == rdr->typ)
|
|
rdr->ph = *module;
|
|
}
|
|
}
|
|
|
|
static void cs_waitforcardinit(void)
|
|
{
|
|
if(cfg.waitforcards)
|
|
{
|
|
cs_log("waiting for local card init");
|
|
int32_t card_init_done;
|
|
do
|
|
{
|
|
card_init_done = 1;
|
|
struct s_reader *rdr;
|
|
LL_ITER itr = ll_iter_create(configured_readers);
|
|
while((rdr = ll_iter_next(&itr)))
|
|
{
|
|
if(rdr->enable && !is_cascading_reader(rdr) && (rdr->card_status == CARD_NEED_INIT || rdr->card_status == UNKNOWN))
|
|
{
|
|
card_init_done = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!card_init_done)
|
|
{ cs_sleepms(300); } // wait a little bit
|
|
//alarm(cfg.cmaxidle + cfg.ctimeout / 1000 + 1);
|
|
}
|
|
while(!card_init_done && !exit_oscam);
|
|
|
|
if(cfg.waitforcards_extra_delay > 0 && !exit_oscam)
|
|
{ cs_sleepms(cfg.waitforcards_extra_delay); }
|
|
cs_log("init for all local cards done");
|
|
}
|
|
}
|
|
|
|
static uint32_t resize_pfd_cllist(struct pollfd **pfd, struct s_client ***cl_list, uint32_t old_size, uint32_t new_size)
|
|
{
|
|
if(old_size != new_size)
|
|
{
|
|
struct pollfd *pfd_new;
|
|
if(!cs_malloc(&pfd_new, new_size * sizeof(struct pollfd)))
|
|
{
|
|
return old_size;
|
|
}
|
|
struct s_client **cl_list_new;
|
|
if(!cs_malloc(&cl_list_new, new_size * sizeof(cl_list)))
|
|
{
|
|
NULLFREE(pfd_new);
|
|
return old_size;
|
|
}
|
|
if(old_size > 0)
|
|
{
|
|
memcpy(pfd_new, *pfd, old_size * sizeof(struct pollfd));
|
|
memcpy(cl_list_new, *cl_list, old_size * sizeof(cl_list));
|
|
NULLFREE(*pfd);
|
|
NULLFREE(*cl_list);
|
|
}
|
|
*pfd = pfd_new;
|
|
*cl_list = cl_list_new;
|
|
}
|
|
return new_size;
|
|
}
|
|
|
|
static uint32_t chk_resize_cllist(struct pollfd **pfd, struct s_client ***cl_list, uint32_t cur_size, uint32_t chk_size)
|
|
{
|
|
chk_size++;
|
|
if(chk_size > cur_size)
|
|
{
|
|
uint32_t new_size = ((chk_size % 100) + 1) * 100; //increase 100 step
|
|
cur_size = resize_pfd_cllist(pfd, cl_list, cur_size, new_size);
|
|
}
|
|
return cur_size;
|
|
}
|
|
|
|
static void process_clients(void)
|
|
{
|
|
int32_t i, k, j, rc, pfdcount = 0;
|
|
struct s_client *cl;
|
|
struct s_reader *rdr;
|
|
struct pollfd *pfd;
|
|
struct s_client **cl_list;
|
|
struct timeb start, end; // start time poll, end time poll
|
|
uint32_t cl_size = 0;
|
|
|
|
uint8_t buf[10];
|
|
|
|
if(pipe(thread_pipe) == -1)
|
|
{
|
|
printf("cannot create pipe, errno=%d\n", errno);
|
|
exit(1);
|
|
}
|
|
|
|
cl_size = chk_resize_cllist(&pfd, &cl_list, 0, 100);
|
|
|
|
pfd[pfdcount].fd = thread_pipe[0];
|
|
pfd[pfdcount].events = POLLIN | POLLPRI;
|
|
cl_list[pfdcount] = NULL;
|
|
|
|
while(!exit_oscam)
|
|
{
|
|
pfdcount = 1;
|
|
|
|
// connected tcp clients
|
|
for(cl = first_client->next; cl; cl = cl->next)
|
|
{
|
|
if(cl->init_done && !cl->kill && cl->pfd && cl->typ == 'c' && !cl->is_udp)
|
|
{
|
|
if(cl->pfd && !cl->thread_active)
|
|
{
|
|
cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount);
|
|
cl_list[pfdcount] = cl;
|
|
pfd[pfdcount].fd = cl->pfd;
|
|
pfd[pfdcount++].events = POLLIN | POLLPRI;
|
|
}
|
|
}
|
|
//reader:
|
|
//TCP:
|
|
// - TCP socket must be connected
|
|
// - no active init thread
|
|
//UDP:
|
|
// - connection status ignored
|
|
// - no active init thread
|
|
rdr = cl->reader;
|
|
if(rdr && cl->typ == 'p' && cl->init_done)
|
|
{
|
|
if(cl->pfd && !cl->thread_active && ((rdr->tcp_connected && rdr->ph.type == MOD_CONN_TCP) || (rdr->ph.type == MOD_CONN_UDP)))
|
|
{
|
|
cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount);
|
|
cl_list[pfdcount] = cl;
|
|
pfd[pfdcount].fd = cl->pfd;
|
|
pfd[pfdcount++].events = (POLLIN | POLLPRI);
|
|
}
|
|
}
|
|
}
|
|
|
|
//server (new tcp connections or udp messages)
|
|
for(k = 0; k < CS_MAX_MOD; k++)
|
|
{
|
|
struct s_module *module = &modules[k];
|
|
if((module->type & MOD_CONN_NET))
|
|
{
|
|
for(j = 0; j < module->ptab.nports; j++)
|
|
{
|
|
if(module->ptab.ports[j].fd)
|
|
{
|
|
cl_size = chk_resize_cllist(&pfd, &cl_list, cl_size, pfdcount);
|
|
cl_list[pfdcount] = NULL;
|
|
pfd[pfdcount].fd = module->ptab.ports[j].fd;
|
|
pfd[pfdcount++].events = (POLLIN | POLLPRI);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pfdcount >= 1024)
|
|
{ cs_log("WARNING: too many users!"); }
|
|
cs_ftime(&start); // register start time
|
|
rc = poll(pfd, pfdcount, 5000);
|
|
if(rc < 1) { continue; }
|
|
cs_ftime(&end); // register end time
|
|
|
|
for(i = 0; i < pfdcount && rc > 0; i++)
|
|
{
|
|
if(pfd[i].revents == 0) { continue; } // skip sockets with no changes
|
|
rc--; //event handled!
|
|
cs_log_dbg(D_TRACE, "[OSCAM] new event %d occurred on fd %d after %"PRId64" ms inactivity", pfd[i].revents,
|
|
pfd[i].fd, comp_timeb(&end, &start));
|
|
//clients
|
|
cl = cl_list[i];
|
|
if(cl && !is_valid_client(cl))
|
|
{ continue; }
|
|
|
|
if(pfd[i].fd == thread_pipe[0] && (pfd[i].revents & (POLLIN | POLLPRI)))
|
|
{
|
|
// a thread ended and cl->pfd should be added to pollfd list again (thread_active==0)
|
|
int32_t len = read(thread_pipe[0], buf, sizeof(buf));
|
|
if(len == -1)
|
|
{
|
|
cs_log_dbg(D_TRACE, "[OSCAM] Reading from pipe failed (errno=%d %s)", errno, strerror(errno));
|
|
}
|
|
cs_log_dump_dbg(D_TRACE, buf, len, "[OSCAM] Readed:");
|
|
continue;
|
|
}
|
|
|
|
//clients
|
|
// message on an open tcp connection
|
|
if(cl && cl->init_done && cl->pfd && (cl->typ == 'c' || cl->typ == 'm'))
|
|
{
|
|
if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
|
|
{
|
|
//client disconnects
|
|
kill_thread(cl);
|
|
continue;
|
|
}
|
|
if(pfd[i].fd == cl->pfd && (pfd[i].revents & (POLLIN | POLLPRI)))
|
|
{
|
|
add_job(cl, ACTION_CLIENT_TCP, NULL, 0);
|
|
}
|
|
}
|
|
|
|
//reader
|
|
// either an ecm answer, a keepalive or connection closed from a proxy
|
|
// physical reader ('r') should never send data without request
|
|
rdr = NULL;
|
|
struct s_client *cl2 = NULL;
|
|
if(cl && cl->typ == 'p')
|
|
{
|
|
rdr = cl->reader;
|
|
if(rdr)
|
|
{ cl2 = rdr->client; }
|
|
}
|
|
|
|
if(rdr && cl2 && cl2->init_done)
|
|
{
|
|
if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
|
|
{
|
|
//connection to remote proxy was closed
|
|
//oscam should check for rdr->tcp_connected and reconnect on next ecm request sent to the proxy
|
|
network_tcp_connection_close(rdr, "closed");
|
|
rdr_log_dbg(rdr, D_READER, "connection closed");
|
|
}
|
|
if(cl2->pfd && pfd[i].fd == cl2->pfd && (pfd[i].revents & (POLLIN | POLLPRI)))
|
|
{
|
|
add_job(cl2, ACTION_READER_REMOTE, NULL, 0);
|
|
}
|
|
}
|
|
|
|
//server sockets
|
|
// new connection on a tcp listen socket or new message on udp listen socket
|
|
if(!cl && (pfd[i].revents & (POLLIN | POLLPRI)))
|
|
{
|
|
for(k = 0; k < CS_MAX_MOD; k++)
|
|
{
|
|
struct s_module *module = &modules[k];
|
|
if((module->type & MOD_CONN_NET))
|
|
{
|
|
for(j = 0; j < module->ptab.nports; j++)
|
|
{
|
|
if(module->ptab.ports[j].fd && module->ptab.ports[j].fd == pfd[i].fd)
|
|
{
|
|
accept_connection(module, k, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cs_ftime(&start); // register start time for new poll next run
|
|
first_client->last = time((time_t *)0);
|
|
}
|
|
NULLFREE(pfd);
|
|
NULLFREE(cl_list);
|
|
return;
|
|
}
|
|
|
|
static pthread_cond_t reader_check_sleep_cond;
|
|
static pthread_mutex_t reader_check_sleep_cond_mutex;
|
|
|
|
static void *reader_check(void)
|
|
{
|
|
struct s_client *cl;
|
|
struct s_reader *rdr;
|
|
set_thread_name(__func__);
|
|
cs_pthread_cond_init(__func__, &reader_check_sleep_cond_mutex, &reader_check_sleep_cond);
|
|
while(!exit_oscam)
|
|
{
|
|
for(cl = first_client->next; cl ; cl = cl->next)
|
|
{
|
|
if(!cl->thread_active)
|
|
{ client_check_status(cl); }
|
|
}
|
|
cs_readlock(__func__, &readerlist_lock);
|
|
for(rdr = first_active_reader; rdr; rdr = rdr->next)
|
|
{
|
|
if(rdr->enable)
|
|
{
|
|
cl = rdr->client;
|
|
if(!cl || cl->kill)
|
|
{ restart_cardreader(rdr, 0); }
|
|
else if(!cl->thread_active)
|
|
{ client_check_status(cl); }
|
|
}
|
|
}
|
|
cs_readunlock(__func__, &readerlist_lock);
|
|
sleepms_on_cond(__func__, &reader_check_sleep_cond_mutex, &reader_check_sleep_cond, 1000);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static pthread_cond_t card_poll_sleep_cond;
|
|
|
|
static void * card_poll(void) {
|
|
struct s_client *cl;
|
|
struct s_reader *rdr;
|
|
pthread_mutex_t card_poll_sleep_cond_mutex;
|
|
SAFE_MUTEX_INIT(&card_poll_sleep_cond_mutex, NULL);
|
|
SAFE_COND_INIT(&card_poll_sleep_cond, NULL);
|
|
set_thread_name(__func__);
|
|
while (!exit_oscam) {
|
|
cs_readlock(__func__, &readerlist_lock);
|
|
for (rdr=first_active_reader; rdr; rdr=rdr->next) {
|
|
if (rdr->enable && rdr->card_status == CARD_INSERTED) {
|
|
cl = rdr->client;
|
|
if (cl && !cl->kill)
|
|
{ add_job(cl, ACTION_READER_POLL_STATUS, 0, 0); }
|
|
}
|
|
}
|
|
cs_readunlock(__func__, &readerlist_lock);
|
|
struct timespec ts;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
ts.tv_sec = tv.tv_sec;
|
|
ts.tv_nsec = tv.tv_usec * 1000;
|
|
ts.tv_sec += 1;
|
|
SAFE_MUTEX_LOCK(&card_poll_sleep_cond_mutex);
|
|
SAFE_COND_TIMEDWAIT(&card_poll_sleep_cond, &card_poll_sleep_cond_mutex, &ts); // sleep on card_poll_sleep_cond
|
|
SAFE_MUTEX_UNLOCK(&card_poll_sleep_cond_mutex);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef WEBIF
|
|
static pid_t pid;
|
|
|
|
static void fwd_sig(int32_t sig)
|
|
{
|
|
kill(pid, sig);
|
|
}
|
|
|
|
static void restart_daemon(void)
|
|
{
|
|
while(1)
|
|
{
|
|
// start client process:
|
|
pid = fork();
|
|
if(!pid)
|
|
{ return; } // client process=oscam process
|
|
if(pid < 0)
|
|
{ exit(1); }
|
|
|
|
// set signal handler for the restart daemon:
|
|
set_signal_handler(SIGINT, 3, fwd_sig);
|
|
#if defined(__APPLE__)
|
|
set_signal_handler(SIGEMT, 3, fwd_sig);
|
|
#endif
|
|
set_signal_handler(SIGTERM, 3, fwd_sig);
|
|
set_signal_handler(SIGQUIT, 0, fwd_sig);
|
|
set_signal_handler(SIGHUP , 0, fwd_sig);
|
|
set_signal_handler(SIGUSR1, 0, fwd_sig);
|
|
set_signal_handler(SIGUSR2, 0, fwd_sig);
|
|
set_signal_handler(SIGALRM , 0, fwd_sig);
|
|
set_signal_handler(SIGWINCH, 1, SIG_IGN);
|
|
set_signal_handler(SIGPIPE , 0, SIG_IGN);
|
|
set_signal_handler(OSCAM_SIGNAL_WAKEUP, 0, SIG_IGN);
|
|
|
|
// restart control process:
|
|
int32_t res = 0;
|
|
int32_t status = 0;
|
|
do
|
|
{
|
|
res = waitpid(pid, &status, 0);
|
|
if(res == -1)
|
|
{
|
|
if(errno != EINTR)
|
|
{ exit(1); }
|
|
}
|
|
}
|
|
while(res != pid);
|
|
|
|
if(cs_restart_mode == 2 && WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)
|
|
{ status = 99; } // restart on segfault!
|
|
else
|
|
{ status = WEXITSTATUS(status); }
|
|
|
|
// status=99 restart oscam, all other->terminate
|
|
if(status != 99)
|
|
{
|
|
exit(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cs_restart_oscam(void)
|
|
{
|
|
exit_oscam = 99;
|
|
cs_log("restart oscam requested");
|
|
}
|
|
|
|
int32_t cs_get_restartmode(void)
|
|
{
|
|
return cs_restart_mode;
|
|
}
|
|
#endif
|
|
|
|
void cs_exit_oscam(void)
|
|
{
|
|
exit_oscam = 1;
|
|
cs_log("exit oscam requested");
|
|
}
|
|
|
|
static void pidfile_create(char *pidfile)
|
|
{
|
|
FILE *f = fopen(pidfile, "w");
|
|
if(f)
|
|
{
|
|
pid_t my_pid = getpid();
|
|
cs_log("creating pidfile %s with pid %d", pidfile, my_pid);
|
|
fprintf(f, "%d\n", my_pid);
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
static bool running_under_valgrind;
|
|
|
|
static void detect_valgrind(void)
|
|
{
|
|
#ifdef __linux__
|
|
char fname[32];
|
|
snprintf(fname, sizeof(fname), "/proc/%d/maps", getpid());
|
|
FILE *f = fopen(fname, "r");
|
|
if (f) {
|
|
char line[256];
|
|
while (fgets(line, sizeof(line), f)) {
|
|
if (strstr(line, "/valgrind/")) {
|
|
running_under_valgrind = true;
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef BUILD_TESTS
|
|
extern void run_all_tests(void);
|
|
__attribute__ ((noreturn)) static void run_tests(void)
|
|
{
|
|
run_all_tests();
|
|
exit(0);
|
|
}
|
|
#else
|
|
static void run_tests(void) { }
|
|
#endif
|
|
|
|
const struct s_cardsystem *cardsystems[] =
|
|
{
|
|
#ifdef READER_NAGRA
|
|
&reader_nagra,
|
|
#endif
|
|
#ifdef READER_NAGRA_MERLIN
|
|
&reader_nagracak7,
|
|
#endif
|
|
#ifdef READER_IRDETO
|
|
&reader_irdeto,
|
|
#endif
|
|
#ifdef READER_CONAX
|
|
&reader_conax,
|
|
#endif
|
|
#ifdef READER_CRYPTOWORKS
|
|
&reader_cryptoworks,
|
|
#endif
|
|
#ifdef READER_SECA
|
|
&reader_seca,
|
|
#endif
|
|
#ifdef READER_VIACCESS
|
|
&reader_viaccess,
|
|
#endif
|
|
#ifdef READER_VIDEOGUARD
|
|
&reader_videoguard1,
|
|
&reader_videoguard2,
|
|
&reader_videoguard12,
|
|
#endif
|
|
#ifdef READER_DRE
|
|
&reader_dre,
|
|
#endif
|
|
#ifdef READER_DRECAS
|
|
&reader_drecas,
|
|
#endif
|
|
#ifdef READER_TONGFANG
|
|
&reader_tongfang,
|
|
#endif
|
|
#ifdef READER_BULCRYPT
|
|
&reader_bulcrypt,
|
|
#endif
|
|
#ifdef READER_GRIFFIN
|
|
&reader_griffin,
|
|
#endif
|
|
#ifdef READER_DGCRYPT
|
|
&reader_dgcrypt,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
const struct s_cardreader *cardreaders[] =
|
|
{
|
|
#ifdef CARDREADER_DB2COM
|
|
&cardreader_db2com,
|
|
#endif
|
|
#if defined(CARDREADER_INTERNAL_AZBOX)
|
|
&cardreader_internal_azbox,
|
|
#elif defined(CARDREADER_INTERNAL_COOLAPI)
|
|
&cardreader_internal_cool,
|
|
#elif defined(CARDREADER_INTERNAL_COOLAPI2)
|
|
&cardreader_internal_cool,
|
|
#elif defined(CARDREADER_INTERNAL_AMSMC)
|
|
&cardreader_internal_amsmc,
|
|
#elif defined(CARDREADER_INTERNAL_SCI)
|
|
&cardreader_internal_sci,
|
|
#endif
|
|
#ifdef CARDREADER_PHOENIX
|
|
&cardreader_mouse,
|
|
#endif
|
|
#ifdef CARDREADER_DRECAS
|
|
&cardreader_drecas,
|
|
#endif
|
|
#ifdef CARDREADER_MP35
|
|
&cardreader_mp35,
|
|
#endif
|
|
#ifdef CARDREADER_PCSC
|
|
&cardreader_pcsc,
|
|
#endif
|
|
#ifdef CARDREADER_SC8IN1
|
|
&cardreader_sc8in1,
|
|
#endif
|
|
#ifdef CARDREADER_SMARGO
|
|
&cardreader_smargo,
|
|
#endif
|
|
#ifdef CARDREADER_SMART
|
|
&cardreader_smartreader,
|
|
#endif
|
|
#if defined(CARDREADER_STAPI) || defined(CARDREADER_STAPI5)
|
|
&cardreader_stapi,
|
|
#endif
|
|
#ifdef CARDREADER_STINGER
|
|
&cardreader_stinger,
|
|
#endif
|
|
#ifdef WITH_EMU
|
|
&cardreader_emu,
|
|
#endif
|
|
|
|
NULL
|
|
};
|
|
|
|
static void find_conf_dir(void)
|
|
{
|
|
static const char* confdirs[] =
|
|
{
|
|
"/etc/tuxbox/config/",
|
|
"/etc/tuxbox/config/oscam/",
|
|
"/var/tuxbox/config/",
|
|
"/usr/keys/",
|
|
"/var/keys/",
|
|
"/var/etc/oscam/",
|
|
"/var/etc/",
|
|
"/var/oscam/",
|
|
"/config/oscam/",
|
|
NULL
|
|
};
|
|
|
|
char conf_file[128+16];
|
|
int32_t i;
|
|
|
|
if(cs_confdir[cs_strlen(cs_confdir) - 1] != '/')
|
|
{ cs_strncat(cs_confdir, "/", sizeof(cs_confdir)); }
|
|
|
|
if(snprintf(conf_file, sizeof(conf_file), "%soscam.conf", cs_confdir) < 0)
|
|
{ return; }
|
|
|
|
if(!access(conf_file, F_OK))
|
|
{ return; }
|
|
|
|
for(i=0; confdirs[i] != NULL; i++)
|
|
{
|
|
if(snprintf(conf_file, sizeof(conf_file), "%soscam.conf", confdirs[i]) < 0)
|
|
{ return; }
|
|
|
|
if (!access(conf_file, F_OK))
|
|
{
|
|
cs_strncpy(cs_confdir, confdirs[i], sizeof(cs_confdir));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t main(int32_t argc, char *argv[])
|
|
{
|
|
fix_stacksize();
|
|
|
|
run_tests();
|
|
int32_t i, j;
|
|
prog_name = argv[0];
|
|
|
|
if(pthread_key_create(&getclient, NULL))
|
|
{
|
|
fprintf(stderr, "Could not create getclient, exiting...");
|
|
exit(1);
|
|
}
|
|
|
|
void (*mod_def[])(struct s_module *) =
|
|
{
|
|
#ifdef MODULE_MONITOR
|
|
module_monitor,
|
|
#endif
|
|
#ifdef MODULE_CAMD33
|
|
module_camd33,
|
|
#endif
|
|
#ifdef MODULE_CAMD35
|
|
module_camd35,
|
|
#endif
|
|
#ifdef MODULE_CAMD35_TCP
|
|
module_camd35_tcp,
|
|
#endif
|
|
#ifdef MODULE_NEWCAMD
|
|
module_newcamd,
|
|
#endif
|
|
#ifdef MODULE_CCCAM
|
|
module_cccam,
|
|
#endif
|
|
#ifdef MODULE_PANDORA
|
|
module_pandora,
|
|
#endif
|
|
#ifdef MODULE_GHTTP
|
|
module_ghttp,
|
|
#endif
|
|
#ifdef CS_CACHEEX
|
|
module_csp,
|
|
#endif
|
|
#ifdef MODULE_GBOX
|
|
module_gbox,
|
|
#endif
|
|
#ifdef MODULE_CONSTCW
|
|
module_constcw,
|
|
#endif
|
|
#ifdef MODULE_RADEGAST
|
|
module_radegast,
|
|
#endif
|
|
#ifdef MODULE_SCAM
|
|
module_scam,
|
|
#endif
|
|
#ifdef MODULE_SERIAL
|
|
module_serial,
|
|
#endif
|
|
#ifdef HAVE_DVBAPI
|
|
module_dvbapi,
|
|
#endif
|
|
#ifdef MODULE_STREAMRELAY
|
|
module_streamrelay,
|
|
#endif
|
|
0
|
|
};
|
|
|
|
set_default_dirs_first();
|
|
|
|
find_conf_dir();
|
|
|
|
parse_cmdline_params(argc, argv);
|
|
|
|
cs_log("DEBUG: main - cs_confdir after parsing command line and finding config dir: '%s'", cs_confdir);
|
|
|
|
if(bg && do_daemon(1, 0))
|
|
{
|
|
printf("Error starting in background (errno=%d: %s)", errno, strerror(errno));
|
|
cs_exit(1);
|
|
}
|
|
|
|
get_random_bytes_init();
|
|
|
|
#ifdef WEBIF
|
|
if(cs_restart_mode)
|
|
{ restart_daemon(); }
|
|
#endif
|
|
|
|
memset(&cfg, 0, sizeof(struct s_config));
|
|
cfg.max_pending = max_pending;
|
|
|
|
if(cs_confdir[cs_strlen(cs_confdir) - 1] != '/') { cs_strncat(cs_confdir, "/", sizeof(cs_confdir)); }
|
|
init_signal_pre(); // because log could cause SIGPIPE errors, init a signal handler first
|
|
init_first_client();
|
|
cs_lock_create(__func__, &system_lock, "system_lock", 5000);
|
|
cs_lock_create(__func__, &config_lock, "config_lock", 10000);
|
|
cs_lock_create(__func__, &gethostbyname_lock, "gethostbyname_lock", 10000);
|
|
cs_lock_create(__func__, &clientlist_lock, "clientlist_lock", 5000);
|
|
cs_lock_create(__func__, &readerlist_lock, "readerlist_lock", 5000);
|
|
cs_lock_create(__func__, &fakeuser_lock, "fakeuser_lock", 5000);
|
|
cs_lock_create(__func__, &ecmcache_lock, "ecmcache_lock", 5000);
|
|
cs_lock_create(__func__, &ecm_pushed_deleted_lock, "ecm_pushed_deleted_lock", 5000);
|
|
cs_lock_create(__func__, &cwcycle_lock, "cwcycle_lock", 5000);
|
|
init_cache();
|
|
cacheex_init_hitcache();
|
|
init_config();
|
|
#ifdef CS_CACHEEX_AIO
|
|
init_cw_cache();
|
|
init_ecm_cache();
|
|
#endif
|
|
cs_init_log();
|
|
init_machine_info();
|
|
init_check();
|
|
if(!oscam_pidfile && cfg.pidfile)
|
|
{ oscam_pidfile = cfg.pidfile; }
|
|
if(!oscam_pidfile)
|
|
{
|
|
oscam_pidfile = get_tmp_dir_filename(default_pidfile, sizeof(default_pidfile), "oscam.pid");
|
|
}
|
|
if(oscam_pidfile)
|
|
{ pidfile_create(oscam_pidfile); }
|
|
cs_init_statistics();
|
|
coolapi_open_all();
|
|
init_stat();
|
|
ssl_init();
|
|
|
|
// These initializations *MUST* be called after init_config()
|
|
// because modules depend on config values.
|
|
for(i = 0; mod_def[i]; i++)
|
|
{
|
|
struct s_module *module = &modules[i];
|
|
mod_def[i](module);
|
|
}
|
|
|
|
init_sidtab();
|
|
init_readerdb();
|
|
#ifdef WITH_EMU
|
|
add_emu_reader();
|
|
#endif
|
|
cfg.account = init_userdb();
|
|
init_signal();
|
|
init_provid();
|
|
init_srvid();
|
|
init_tierid();
|
|
init_fakecws();
|
|
|
|
start_garbage_collector(gbdb);
|
|
|
|
cacheex_init();
|
|
|
|
write_versionfile(false);
|
|
|
|
led_init();
|
|
led_status_default();
|
|
|
|
azbox_init();
|
|
|
|
mca_init();
|
|
|
|
global_whitelist_read();
|
|
ratelimit_read();
|
|
|
|
#ifdef MODULE_SERIAL
|
|
twin_read();
|
|
#endif
|
|
|
|
for(i = 0; i < CS_MAX_MOD; i++)
|
|
{
|
|
struct s_module *module = &modules[i];
|
|
if((module->type & MOD_CONN_NET))
|
|
{
|
|
for(j = 0; j < module->ptab.nports; j++)
|
|
{
|
|
start_listener(module, &module->ptab.ports[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// set time for server to now to avoid 0 in monitor/webif
|
|
first_client->last = time((time_t *)0);
|
|
|
|
webif_init();
|
|
|
|
start_thread("reader check", (void *) &reader_check, NULL, NULL, 1, 1);
|
|
cw_process_thread_start();
|
|
checkcache_process_thread_start();
|
|
|
|
lcd_thread_start();
|
|
|
|
do_report_emm_support();
|
|
|
|
init_cardreader();
|
|
|
|
cs_waitforcardinit();
|
|
|
|
emm_load_cache();
|
|
load_emmstat_from_file();
|
|
|
|
led_status_starting();
|
|
|
|
ac_init();
|
|
|
|
gbox_send_init_hello();
|
|
|
|
start_thread("card poll", (void *) &card_poll, NULL, NULL, 1, 1);
|
|
|
|
for(i = 0; i < CS_MAX_MOD; i++)
|
|
{
|
|
struct s_module *module = &modules[i];
|
|
if((module->type & MOD_CONN_SERIAL) && module->s_handler)
|
|
{ module->s_handler(NULL, NULL, i); }
|
|
}
|
|
|
|
// main loop function
|
|
process_clients();
|
|
|
|
SAFE_COND_SIGNAL(&card_poll_sleep_cond); // Stop card_poll thread
|
|
cw_process_thread_wakeup(); // Stop cw_process thread
|
|
SAFE_COND_SIGNAL(&reader_check_sleep_cond); // Stop reader_check thread
|
|
|
|
// Cleanup
|
|
#ifdef MODULE_GBOX
|
|
stop_gbx_ticker();
|
|
#endif
|
|
#ifdef MODULE_STREAMRELAY
|
|
stop_stream_server();
|
|
#endif
|
|
webif_close();
|
|
azbox_close();
|
|
coolapi_close_all();
|
|
mca_close();
|
|
|
|
led_status_stopping();
|
|
led_stop();
|
|
lcd_thread_stop();
|
|
|
|
remove_versionfile();
|
|
|
|
stat_finish();
|
|
dvbapi_stop_all_descrambling(0);
|
|
dvbapi_save_channel_cache();
|
|
emm_save_cache();
|
|
save_emmstat_to_file();
|
|
|
|
cccam_done_share();
|
|
gbox_send_good_night();
|
|
|
|
kill_all_clients();
|
|
kill_all_readers();
|
|
for(i = 0; i < CS_MAX_MOD; i++)
|
|
{
|
|
struct s_module *module = &modules[i];
|
|
if((module->type & MOD_CONN_NET))
|
|
{
|
|
for(j = 0; j < module->ptab.nports; j++)
|
|
{
|
|
struct s_port *port = &module->ptab.ports[j];
|
|
if(port->fd)
|
|
{
|
|
shutdown(port->fd, SHUT_RDWR);
|
|
close(port->fd);
|
|
port->fd = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(oscam_pidfile)
|
|
{ unlink(oscam_pidfile); }
|
|
|
|
// sleep a bit, so hopefully all threads are stopped when we continue
|
|
cs_sleepms(200);
|
|
|
|
free_cache();
|
|
#ifdef CS_CACHEEX_AIO
|
|
free_ecm_cache();
|
|
#endif
|
|
cacheex_free_hitcache();
|
|
webif_tpls_free();
|
|
#if defined(WEBIF) && defined(WEBIF_WIKI)
|
|
webif_wiki_free();
|
|
#endif
|
|
init_free_userdb(cfg.account);
|
|
cfg.account = NULL;
|
|
init_free_sidtab();
|
|
free_readerdb();
|
|
config_free();
|
|
ssl_done();
|
|
|
|
detect_valgrind();
|
|
if (!running_under_valgrind)
|
|
cs_log("cardserver down");
|
|
else
|
|
cs_log("running under valgrind, waiting 5 seconds before stopping cardserver");
|
|
log_free();
|
|
|
|
if (running_under_valgrind) sleep(5); // HACK: Wait a bit for things to settle
|
|
|
|
stop_garbage_collector();
|
|
|
|
NULLFREE(first_client->account);
|
|
NULLFREE(first_client);
|
|
free(stb_boxtype);
|
|
free(stb_boxname);
|
|
|
|
// This prevents the compiler from removing config_mak from the final binary
|
|
syslog_ident = config_mak;
|
|
|
|
return exit_oscam;
|
|
}
|