#define MODULE_LOG_PREFIX "main" #include "globals.h" #include #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_SSL #include #include #include 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 | Read configuration files from .\n"); printf(" . Default: %s\n", CS_CONFDIR); printf(" -t, --temp-dir | Set temporary directory to .\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 | Create pidfile when starting.\n"); if(config_enabled(WEBIF)) { printf(" -r, --restart | 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 | 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 | 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 | 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 | 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 | 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", CS_BUILD_DATE); fprintf(fp, "Version: %s@%s\n", CS_VERSION, CS_GIT_COMMIT); fprintf(fp, "Compiler: %s\n", CS_TARGET); #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 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 // 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 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); 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(); 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; }