2026-02-16 09:02:48 +00:00
# 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_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 ) ;
}
2026-02-23 16:40:08 +00:00
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 ) ;
2026-02-16 09:02:48 +00:00
# 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 <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
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 ;
}