You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/src/main.cpp

1918 lines
51 KiB

#define MAIN_PROXY_SQLITE3
#include <iostream>
#include <thread>
#include "btree_map.h"
#include "proxysql.h"
#if defined(__FreeBSD__) || defined(__APPLE__)
#include <fcntl.h>
#endif
#include <fcntl.h>
#include <unistd.h>
//#define PROXYSQL_EXTERN
#include "cpp.h"
#include "ProxySQL_Statistics.hpp"
#include "MySQL_PreparedStatement.h"
#include "ProxySQL_Cluster.hpp"
#include "MySQL_Logger.hpp"
#include "SQLite3_Server.h"
#include "query_processor.h"
#include "MySQL_Authentication.hpp"
#include "MySQL_LDAP_Authentication.hpp"
#include "proxysql_restapi.h"
#include "Web_Interface.hpp"
#include <libdaemon/dfork.h>
#include <libdaemon/dsignal.h>
#include <libdaemon/dlog.h>
#include <libdaemon/dpid.h>
#include <libdaemon/dexec.h>
#include "ev.h"
#include "curl/curl.h"
#include <openssl/x509v3.h>
// Minimal headers for exporting metrics using prometheus
#include <prometheus/counter.h>
#include <prometheus/exposer.h>
#include <prometheus/registry.h>
#include <sys/mman.h>
/*
extern "C" MySQL_LDAP_Authentication * create_MySQL_LDAP_Authentication_func() {
return NULL;
}
*/
volatile create_MySQL_LDAP_Authentication_t * create_MySQL_LDAP_Authentication = NULL;
void * __mysql_ldap_auth;
volatile create_Web_Interface_t * create_Web_Interface = NULL;
void * __web_interface;
// absolute path of ssl files
char *ssl_key_fp = NULL;
char *ssl_cert_fp = NULL;
char *ssl_ca_fp = NULL;
char *binary_sha1 = NULL;
// MariaDB client library redefines dlerror(), see https://mariadb.atlassian.net/browse/CONC-101
#ifdef dlerror
#undef dlerror
#endif
struct dh_st {
int pad;
int version;
BIGNUM *p;
BIGNUM *g;
long length;
BIGNUM *pub_key;
BIGNUM *priv_key;
int flags;
BN_MONT_CTX *method_mont_p;
BIGNUM *q;
BIGNUM *j;
unsigned char *seed;
int seedlen;
BIGNUM *counter;
int references;
CRYPTO_EX_DATA ex_data;
const DH_METHOD *meth;
ENGINE *engine;
CRYPTO_RWLOCK *lock;
};
static pthread_mutex_t *lockarray;
#include <openssl/crypto.h>
// this fuction will be called as a deatached thread
static void * waitpid_thread(void *arg) {
pid_t *cpid_ptr=(pid_t *)arg;
int status;
waitpid(*cpid_ptr, &status, 0);
free(cpid_ptr);
return NULL;
}
/*
generated with: $ openssl dhparam -5 -C 2048
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAtS5UPzxesyj7QtLe6hRGE1Cv4TnDbSzKTmy0izFabdn0wR1QVmij
S8YSb1jE+O7IGImtk84Wg4y141PAHkCMTEeCMKH5tOD0WfiVyuQDTp4Vbt0vOReM
hK7tgLHLC1P3v0nxFCcce3U6IXmXBQ9IkNMFcXSRIAdBOjPkFPfbZ648qSgcoX+z
gfEP9WAXeeNGk62rDb3R0mguA9HcQ4NyKk6ETBVsZD4bTAcSIBaX05ISV7qY2eLj
9HFYBXYX4cxBfMyiqGrCj2IMg8aRKmf7rTvwBQXT0cWmu+kpnlpXIjx6vdpBmeKd
hSypLEcUVIvzc6rtfWlYKT35wQ+AGKNADwIBBQ==
-----END DH PARAMETERS-----
*/
#ifndef HEADER_DH_H
#include <openssl/dh.h>
#endif
DH *get_dh2048()
{
static unsigned char dh2048_p[]={
0xB5,0x2E,0x54,0x3F,0x3C,0x5E,0xB3,0x28,0xFB,0x42,0xD2,0xDE,
0xEA,0x14,0x46,0x13,0x50,0xAF,0xE1,0x39,0xC3,0x6D,0x2C,0xCA,
0x4E,0x6C,0xB4,0x8B,0x31,0x5A,0x6D,0xD9,0xF4,0xC1,0x1D,0x50,
0x56,0x68,0xA3,0x4B,0xC6,0x12,0x6F,0x58,0xC4,0xF8,0xEE,0xC8,
0x18,0x89,0xAD,0x93,0xCE,0x16,0x83,0x8C,0xB5,0xE3,0x53,0xC0,
0x1E,0x40,0x8C,0x4C,0x47,0x82,0x30,0xA1,0xF9,0xB4,0xE0,0xF4,
0x59,0xF8,0x95,0xCA,0xE4,0x03,0x4E,0x9E,0x15,0x6E,0xDD,0x2F,
0x39,0x17,0x8C,0x84,0xAE,0xED,0x80,0xB1,0xCB,0x0B,0x53,0xF7,
0xBF,0x49,0xF1,0x14,0x27,0x1C,0x7B,0x75,0x3A,0x21,0x79,0x97,
0x05,0x0F,0x48,0x90,0xD3,0x05,0x71,0x74,0x91,0x20,0x07,0x41,
0x3A,0x33,0xE4,0x14,0xF7,0xDB,0x67,0xAE,0x3C,0xA9,0x28,0x1C,
0xA1,0x7F,0xB3,0x81,0xF1,0x0F,0xF5,0x60,0x17,0x79,0xE3,0x46,
0x93,0xAD,0xAB,0x0D,0xBD,0xD1,0xD2,0x68,0x2E,0x03,0xD1,0xDC,
0x43,0x83,0x72,0x2A,0x4E,0x84,0x4C,0x15,0x6C,0x64,0x3E,0x1B,
0x4C,0x07,0x12,0x20,0x16,0x97,0xD3,0x92,0x12,0x57,0xBA,0x98,
0xD9,0xE2,0xE3,0xF4,0x71,0x58,0x05,0x76,0x17,0xE1,0xCC,0x41,
0x7C,0xCC,0xA2,0xA8,0x6A,0xC2,0x8F,0x62,0x0C,0x83,0xC6,0x91,
0x2A,0x67,0xFB,0xAD,0x3B,0xF0,0x05,0x05,0xD3,0xD1,0xC5,0xA6,
0xBB,0xE9,0x29,0x9E,0x5A,0x57,0x22,0x3C,0x7A,0xBD,0xDA,0x41,
0x99,0xE2,0x9D,0x85,0x2C,0xA9,0x2C,0x47,0x14,0x54,0x8B,0xF3,
0x73,0xAA,0xED,0x7D,0x69,0x58,0x29,0x3D,0xF9,0xC1,0x0F,0x80,
0x18,0xA3,0x40,0x0F,
};
static unsigned char dh2048_g[]={
0x05,
};
DH *dh;
if ((dh=DH_new()) == NULL) return(NULL);
dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
if ((dh->p == NULL) || (dh->g == NULL))
{ DH_free(dh); return(NULL); }
return(dh);
}
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
assert(mem->memory);
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
static char * main_check_latest_version() {
CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = (char *)malloc(1);
chunk.size = 0;
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.proxysql.com/latest");
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
string s = "proxysql-agent/";
s += PROXYSQL_VERSION;
if (binary_sha1) {
s += " (" ;
s+= binary_sha1;
s += ")" ;
}
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, s.c_str());
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 10);
res = curl_easy_perform(curl_handle);
if (res != CURLE_OK) {
switch (res) {
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_COULDNT_CONNECT:
case CURLE_OPERATION_TIMEDOUT:
break;
default:
proxy_error("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
break;
}
free(chunk.memory);
chunk.memory = NULL;
}
curl_easy_cleanup(curl_handle);
curl_global_cleanup();
return chunk.memory;
}
void * main_check_latest_version_thread(void *arg) {
char * latest_version = main_check_latest_version();
if (latest_version) {
proxy_info("Latest ProxySQL version available: %s\n", latest_version);
}
free(latest_version);
return NULL;
}
// Note: if you are running ProxySQL under gdb, you may consider setting this
// variable immediately to 1
// Example:
// set disable_watchdog=1
volatile int disable_watchdog = 0;
void parent_open_error_log() {
if (GloVars.global.foreground==false) {
int outfd=0;
int errfd=0;
outfd=open(GloVars.errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (outfd>0) {
dup2(outfd, STDOUT_FILENO);
close(outfd);
} else {
proxy_error("Impossible to open file\n");
}
errfd=open(GloVars.errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (errfd>0) {
dup2(errfd, STDERR_FILENO);
close(errfd);
} else {
proxy_error("Impossible to open file\n");
}
}
}
void parent_close_error_log() {
if (GloVars.global.foreground==false) {
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
}
time_t laststart;
pid_t pid;
static const char * proxysql_pid_file() {
static char fn[512];
snprintf(fn, sizeof(fn), "%s", daemon_pid_file_ident);
return fn;
}
/*struct cpu_timer
{
~cpu_timer()
{
auto end = std::clock() ;
std::cerr << double( end - begin ) / CLOCKS_PER_SEC << " secs.\n" ;
};
const std::clock_t begin = std::clock() ;
};
*/
struct cpu_timer
{
cpu_timer() {
begin = monotonic_time();
}
~cpu_timer()
{
unsigned long long end = monotonic_time();
#ifdef DEBUG
std::cerr << double( end - begin ) / 1000000 << " secs.\n" ;
#endif /* DEBUG */
begin=end-begin; // here only to make compiler happy
};
unsigned long long begin;
};
static void lock_callback(int mode, int type, const char *file, int line) {
(void)file;
(void)line;
if(mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(lockarray[type]));
} else {
pthread_mutex_unlock(&(lockarray[type]));
}
}
static unsigned long thread_id(void) {
unsigned long ret;
ret = (unsigned long)pthread_self();
return ret;
}
static void init_locks(void) {
int i;
lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
for(i = 0; i<CRYPTO_num_locks(); i++) {
pthread_mutex_init(&(lockarray[i]), NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())thread_id);
CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback);
}
X509 * generate_x509(EVP_PKEY *pkey, const unsigned char *cn, uint32_t serial, int days, X509 *ca_x509, EVP_PKEY *ca_pkey) {
int rc;
X509 * x = NULL;
X509_NAME * name= NULL;
if ((x = X509_new()) == NULL) {
proxy_error("Unable to run X509_new()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
X509_set_version(x, 2);
ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
X509_gmtime_adj(X509_get_notBefore(x), 0);
X509_gmtime_adj(X509_get_notAfter(x), (long)60 * 60 * 24 * days);
rc = X509_set_pubkey(x, pkey);
if (rc==0){
proxy_error("Unable to set pubkey: %s\n", ERR_error_string(ERR_get_error(),NULL));
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
name = X509_get_subject_name(x);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn, -1, -1, 0);
if (ca_x509) {
rc = X509_set_issuer_name(x, X509_get_subject_name(ca_x509));
} else {
X509_EXTENSION* extension = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, "critical, CA:FALSE");
X509_add_ext(x, extension, -1);
rc = X509_set_issuer_name(x, name);
}
if (rc==0) {
proxy_error("Unable to set issuer: %s\n", ERR_error_string(ERR_get_error(),NULL));
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
if (ca_pkey) {
rc = X509_sign(x, ca_pkey, EVP_sha256());
} else {
rc = X509_sign(x, pkey, EVP_sha256());
}
if (rc==0) {
proxy_error("Unable to X509 sign: %s\n", ERR_error_string(ERR_get_error(),NULL));
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
return x;
}
void write_x509(const char *filen, X509 *x) {
BIO * x509file = NULL;
x509file = BIO_new_file(filen, "w" );
if (!x509file ) {
proxy_error("Error on BIO_new_file\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
if (!PEM_write_bio_X509( x509file, x)) {
proxy_error("Error on PEM_write_bio_X509 for %s\n", filen);
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
BIO_free_all( x509file );
}
void write_rsa_key(const char *filen, RSA *rsa) {
BIO* pOut = BIO_new_file(filen, "w");
if (!pOut) {
proxy_error("Error on BIO_new_file\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
if (!PEM_write_bio_RSAPrivateKey( pOut, rsa, NULL, NULL, 0, NULL, NULL)) {
proxy_error("Error on PEM_write_bio_RSAPrivateKey for %s\n", filen);
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
BIO_free_all( pOut );
}
EVP_PKEY * rsa_key_read(const char *filen) {
EVP_PKEY * pkey = NULL;
RSA * rsa = NULL;
BIO * pIn = BIO_new_file(filen,"r");
if (!pIn) {
proxy_error("Error on BIO_new_file\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
rsa= PEM_read_bio_RSAPrivateKey( pIn , NULL, NULL, NULL);
if (rsa==NULL) {
proxy_error("Error on PEM_read_bio_RSAPrivateKey for %s\n", filen);
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
BIO_free(pIn);
return pkey;
}
X509 * read_x509(const char *filen) {
X509 * x = NULL;
BIO * x509file = NULL;
x509file = BIO_new_file(filen, "r" );
if (!x509file ) {
proxy_error("Error on BIO_new_file\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
x = PEM_read_bio_X509( x509file, NULL, NULL, NULL);
if (x == NULL) {
proxy_error("Error on PEM_read_bio_X509 for %s\n", filen);
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
BIO_free_all( x509file );
return x;
}
int ssl_mkit(X509 **x509ca, X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days) {
X509 *x1;
X509 *x2;
EVP_PKEY *pk;
RSA *rsa;
DH *dh;
//X509_NAME *name = NULL;
// relative path to datadir of ssl files
const char * ssl_key_rp = (const char *)"proxysql-key.pem";
const char * ssl_cert_rp = (const char *)"proxysql-cert.pem";
const char * ssl_ca_rp = (const char *)"proxysql-ca.pem";
/*
// absolute path of ssl files
char *ssl_key_fp = NULL;
char *ssl_cert_fp = NULL;
char *ssl_ca_fp = NULL;
*/
// how many files exists ?
int nfiles = 0;
bool ssl_key_exists = true;
bool ssl_cert_exists = true;
bool ssl_ca_exists = true;
// check if files exists
ssl_key_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_key_rp)+8);
sprintf(ssl_key_fp,"%s/%s",GloVars.datadir,ssl_key_rp);
if (access(ssl_key_fp, R_OK)) {
ssl_key_exists = false;
//free(ssl_key);
//ssl_key = NULL;
}
ssl_cert_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_cert_rp)+8);
sprintf(ssl_cert_fp,"%s/%s",GloVars.datadir,ssl_cert_rp);
if (access(ssl_cert_fp, R_OK)) {
ssl_cert_exists = false;
//free(ssl_cert);
//ssl_cert = NULL;
}
ssl_ca_fp = (char *)malloc(strlen(GloVars.datadir)+strlen(ssl_ca_rp)+8);
sprintf(ssl_ca_fp,"%s/%s",GloVars.datadir,ssl_ca_rp);
if (access(ssl_ca_fp, R_OK)) {
ssl_ca_exists = false;
//free(ssl_ca);
//ssl_ca = NULL;
}
nfiles += (ssl_key_exists ? 1 : 0);
nfiles += (ssl_cert_exists ? 1 : 0);
nfiles += (ssl_ca_exists ? 1 : 0);
if ((nfiles != 0 && nfiles != 3)) {
proxy_error("Only some SSL files are present. Either all files are present, or none. Exiting.\n");
proxy_error("%s : %s\n" , ssl_key_rp, (ssl_key_exists ? (char *)"YES" : (char *)"NO"));
proxy_error("%s : %s\n" , ssl_cert_rp, (ssl_cert_exists ? (char *)"YES" : (char *)"NO"));
proxy_error("%s : %s\n" , ssl_ca_rp, (ssl_ca_exists ? (char *)"YES" : (char *)"NO"));
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
if (nfiles == 0) {
proxy_info("No SSL keys/certificates found in datadir (%s). Generating new keys/certificates.\n", GloVars.datadir);
if ((pkeyp == NULL) || (*pkeyp == NULL)) {
if ((pk = EVP_PKEY_new()) == NULL) {
proxy_error("Unable to run EVP_PKEY_new()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
} else
pk = *pkeyp;
rsa = RSA_new();
if (!rsa) {
proxy_error("Unable to run RSA_new()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
BIGNUM *e= BN_new();
if (!e) {
proxy_error("Unable to run BN_new()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
if (!BN_set_word(e, RSA_F4) || !RSA_generate_key_ex(rsa, bits, e, NULL)) {
RSA_free(rsa);
BN_free(e);
proxy_error("Unable to run BN_new()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
BN_free(e);
write_rsa_key(ssl_key_fp, rsa);
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
proxy_error("Unable to run EVP_PKEY_assign_RSA()\n");
exit(EXIT_SUCCESS); // we exit gracefully to avoid being restarted
}
time_t t = time(NULL);
x1 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_CA_Certificate", t, 3650, NULL, NULL);
write_x509(ssl_ca_fp, x1);
x2 = generate_x509(pk, (const unsigned char *)"ProxySQL_Auto_Generated_Server_Certificate", t, 3650, x1, pk);
write_x509(ssl_cert_fp, x2);
rsa = NULL;
} else {
proxy_info("SSL keys/certificates found in datadir (%s): loading them.\n", GloVars.datadir);
pk = rsa_key_read(ssl_key_fp);
x1 = read_x509(ssl_ca_fp);
x2 = read_x509(ssl_cert_fp);
}
*x509ca = x1;
*x509p = x2;
*pkeyp = pk;
dh = get_dh2048();
if (SSL_CTX_set_tmp_dh(GloVars.global.ssl_ctx, dh) == 0) {
proxy_error("Error in SSL while initializing DH: %s . Shutting down.\n",ERR_error_string(ERR_get_error(), NULL));
exit(EXIT_SUCCESS); // EXIT_SUCCESS to avoid a restart loop
}
return 1;
}
void ProxySQL_Main_init_SSL_module() {
int rc = SSL_library_init();
if (rc==0) {
proxy_error("%s\n", SSL_alert_desc_string_long(rc));
}
init_locks();
proxy_info("Using OpenSSL version: %s\n", OpenSSL_version(OPENSSL_VERSION));
SSL_METHOD *ssl_method;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
//ssl_method = (SSL_METHOD *)TLSv1_server_method();
//ssl_method = (SSL_METHOD *)SSLv23_server_method();
ssl_method = (SSL_METHOD *)TLS_server_method();
GloVars.global.ssl_ctx = SSL_CTX_new(ssl_method);
if (GloVars.global.ssl_ctx==NULL) {
ERR_print_errors_fp(stderr);
proxy_error("Unable to initialize SSL. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
if (!SSL_CTX_set_min_proto_version(GloVars.global.ssl_ctx,TLS1_VERSION)) {
proxy_error("Unable to initialize SSL. SSL_set_min_proto_version failed. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
//SSL_CTX_set_options(GloVars.global.ssl_ctx, SSL_OP_NO_SSLv3); // no necessary, because of previous SSL_CTX_set_min_proto_version
#ifdef DEBUG
{
STACK_OF(SSL_CIPHER) *ciphers;
ciphers = SSL_CTX_get_ciphers(GloVars.global.ssl_ctx);
fprintf(stderr,"List of cipher avaiable:\n");
if (ciphers) {
int num = sk_SSL_CIPHER_num(ciphers);
char buf[130];
for(int i = 0; i < num; i++){
const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
fprintf(stderr,"%s: %s\n", SSL_CIPHER_get_name(cipher), SSL_CIPHER_description(cipher, buf, 128));
}
}
}
#endif
BIO *bio_err;
X509 *x509 = NULL;
X509 *x509ca = NULL;
EVP_PKEY *pkey = NULL;
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
if (ssl_mkit(&x509ca, &x509, &pkey, 2048, 0, 730) == 0) {
proxy_error("Unable to initialize SSL. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
if ( SSL_CTX_use_certificate(GloVars.global.ssl_ctx, x509) <= 0 ) {
ERR_print_errors_fp(stderr);
proxy_error("Unable to use SSL certificate. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
if ( SSL_CTX_add_extra_chain_cert(GloVars.global.ssl_ctx, x509ca) <= 0 ) {
ERR_print_errors_fp(stderr);
proxy_error("Unable to use SSL CA chain. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
if ( SSL_CTX_use_PrivateKey(GloVars.global.ssl_ctx, pkey) <= 0 ) {
ERR_print_errors_fp(stderr);
proxy_error("Unable to use SSL key. Shutting down...\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
if ( !SSL_CTX_check_private_key(GloVars.global.ssl_ctx) ) {
proxy_error("Private key does not match the public certificate\n");
exit(EXIT_SUCCESS); // we exit gracefully to not be restarted
}
X509_free(x509);
EVP_PKEY_free(pkey);
BIO_free(bio_err);
}
/*
void example_listern() {
// few examples tests to demonstrate the ability to add and remove listeners at runtime
GloMTH->listener_add((char *)"0.0.0.0:6033");
sleep(3);
GloMTH->listener_add((char *)"127.0.0.1:5033");
sleep(3);
GloMTH->listener_add((char *)"127.0.0.2:5033");
sleep(3);
GloMTH->listener_add((char *)"/tmp/proxysql.sock");
for (int t=0; t<10; t++) {
GloMTH->listener_add((char *)"127.0.0.1",7000+t);
sleep(3);
}
GloMTH->listener_del((char *)"0.0.0.0:6033");
sleep(3);
GloMTH->listener_del((char *)"127.0.0.1:5033");
sleep(3);
GloMTH->listener_del((char *)"127.0.0.2:5033");
sleep(3);
GloMTH->listener_del((char *)"/tmp/proxysql.sock");
}
*/
void * __qc;
void * __mysql_thread;
void * __mysql_threads_handler;
void * __query_processor;
//void * __mysql_auth;
using namespace std;
//__cmd_proxysql_config_file=NULL;
#define MAX_EVENTS 100
static volatile int load_;
//__thread l_sfp *__thr_sfp=NULL;
//#ifdef DEBUG
//const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,purge:decay,junk:true,tcache:false";
//#else
//const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,purge:decay";
#ifndef __FreeBSD__
const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,prof:true,prof_leak:true,lg_prof_sample:20,lg_prof_interval:30,prof_active:false";
#endif
//#endif /* DEBUG */
//const char *malloc_conf = "prof_leak:true,lg_prof_sample:0,prof_final:true,xmalloc:true,lg_tcache_max:16";
int listen_fd;
int socket_fd;
Query_Cache *GloQC;
MySQL_Authentication *GloMyAuth;
MySQL_LDAP_Authentication *GloMyLdapAuth;
#ifdef PROXYSQLCLICKHOUSE
ClickHouse_Authentication *GloClickHouseAuth;
#endif /* PROXYSQLCLICKHOUSE */
Query_Processor *GloQPro;
ProxySQL_Admin *GloAdmin;
MySQL_Threads_Handler *GloMTH = NULL;
Web_Interface *GloWebInterface;
MySQL_STMT_Manager_v14 *GloMyStmt;
MySQL_Monitor *GloMyMon;
std::thread *MyMon_thread = NULL;
MySQL_Logger *GloMyLogger;
MySQL_Variables mysql_variables;
SQLite3_Server *GloSQLite3Server;
#ifdef PROXYSQLCLICKHOUSE
ClickHouse_Server *GloClickHouseServer;
#endif /* PROXYSQLCLICKHOUSE */
ProxySQL_Cluster *GloProxyCluster = NULL;
ProxySQL_Statistics *GloProxyStats = NULL;
void * mysql_worker_thread_func(void *arg) {
// __thr_sfp=l_mem_init();
pthread_attr_t thread_attr;
size_t tmp_stack_size=0;
if (!pthread_attr_init(&thread_attr)) {
if (!pthread_attr_getstacksize(&thread_attr , &tmp_stack_size )) {
__sync_fetch_and_add(&GloVars.statuses.stack_memory_mysql_threads,tmp_stack_size);
}
}
proxysql_mysql_thread_t *mysql_thread=(proxysql_mysql_thread_t *)arg;
MySQL_Thread *worker = new MySQL_Thread();
mysql_thread->worker=worker;
worker->init();
// worker->poll_listener_add(listen_fd);
// worker->poll_listener_add(socket_fd);
__sync_fetch_and_sub(&load_,1);
do { usleep(50); } while (load_);
worker->run();
//delete worker;
delete worker;
mysql_thread->worker=NULL;
// l_mem_destroy(__thr_sfp);
__sync_fetch_and_sub(&GloVars.statuses.stack_memory_mysql_threads,tmp_stack_size);
return NULL;
}
#ifdef IDLE_THREADS
void * mysql_worker_thread_func_idles(void *arg) {
pthread_attr_t thread_attr;
size_t tmp_stack_size=0;
if (!pthread_attr_init(&thread_attr)) {
if (!pthread_attr_getstacksize(&thread_attr , &tmp_stack_size )) {
__sync_fetch_and_add(&GloVars.statuses.stack_memory_mysql_threads,tmp_stack_size);
}
}
// __thr_sfp=l_mem_init();
proxysql_mysql_thread_t *mysql_thread=(proxysql_mysql_thread_t *)arg;
MySQL_Thread *worker = new MySQL_Thread();
mysql_thread->worker=worker;
worker->epoll_thread=true;
worker->init();
// worker->poll_listener_add(listen_fd);
// worker->poll_listener_add(socket_fd);
__sync_fetch_and_sub(&load_,1);
do { usleep(50); } while (load_);
worker->run();
//delete worker;
delete worker;
// l_mem_destroy(__thr_sfp);
__sync_fetch_and_sub(&GloVars.statuses.stack_memory_mysql_threads,tmp_stack_size);
return NULL;
}
#endif // IDLE_THREADS
void * mysql_shared_query_cache_funct(void *arg) {
GloQC->purgeHash_thread(NULL);
return NULL;
}
void ProxySQL_Main_process_global_variables(int argc, const char **argv) {
GloVars.errorlog = NULL;
GloVars.parse(argc,argv);
GloVars.process_opts_pre();
GloVars.restart_on_missing_heartbeats = 10; // default
// alwasy try to open a config file
if (GloVars.confFile->OpenFile(GloVars.config_file) == true) {
GloVars.configfile_open=true;
proxy_info("Using config file %s\n", GloVars.config_file);
const Setting& root = GloVars.confFile->cfg.getRoot();
if (root.exists("restart_on_missing_heartbeats")==true) {
// restart_on_missing_heartbeats datadir from config file
int restart_on_missing_heartbeats;
bool rc;
rc=root.lookupValue("restart_on_missing_heartbeats", restart_on_missing_heartbeats);
if (rc==true) {
GloVars.restart_on_missing_heartbeats=restart_on_missing_heartbeats;
}
}
if (root.exists("execute_on_exit_failure")==true) {
// restart_on_missing_heartbeats datadir from config file
string execute_on_exit_failure;
bool rc;
rc=root.lookupValue("execute_on_exit_failure", execute_on_exit_failure);
if (rc==true) {
GloVars.execute_on_exit_failure=strdup(execute_on_exit_failure.c_str());
}
}
if (root.exists("errorlog")==true) {
// restart_on_missing_heartbeats datadir from config file
string errorlog_path;
bool rc;
rc=root.lookupValue("errorlog", errorlog_path);
if (rc==true) {
GloVars.errorlog = strdup(errorlog_path.c_str());
}
}
if (root.exists("sqlite3_plugin")==true) {
string sqlite3_plugin;
bool rc;
rc=root.lookupValue("sqlite3_plugin", sqlite3_plugin);
if (rc==true) {
GloVars.sqlite3_plugin=strdup(sqlite3_plugin.c_str());
}
}
if (root.exists("web_interface_plugin")==true) {
string web_interface_plugin;
bool rc;
rc=root.lookupValue("web_interface_plugin", web_interface_plugin);
if (rc==true) {
GloVars.web_interface_plugin=strdup(web_interface_plugin.c_str());
}
}
if (root.exists("ldap_auth_plugin")==true) {
string ldap_auth_plugin;
bool rc;
rc=root.lookupValue("ldap_auth_plugin", ldap_auth_plugin);
if (rc==true) {
GloVars.ldap_auth_plugin=strdup(ldap_auth_plugin.c_str());
}
}
} else {
proxy_warning("Unable to open config file %s\n", GloVars.config_file); // issue #705
if (GloVars.__cmd_proxysql_config_file) {
proxy_error("Unable to open config file %s specified in the command line. Aborting!\n", GloVars.config_file);
exit(EXIT_SUCCESS); // we exit gracefully to avoid restart
}
}
char *t=getcwd(NULL, 512);
if (GloVars.__cmd_proxysql_datadir==NULL) {
// datadir was not specified , try to read config file
if (GloVars.configfile_open==true) {
const Setting& root = GloVars.confFile->cfg.getRoot();
if (root.exists("datadir")==true) {
// reading datadir from config file
std::string datadir;
bool rc;
rc=root.lookupValue("datadir", datadir);
if (rc==true) {
GloVars.datadir=strdup(datadir.c_str());
} else {
GloVars.datadir=strdup(t);
}
} else {
// datadir was not specified in config file
GloVars.datadir=strdup(t);
}
if (root.exists("restart_on_missing_heartbeats")==true) {
// restart_on_missing_heartbeats datadir from config file
int restart_on_missing_heartbeats;
bool rc;
rc=root.lookupValue("restart_on_missing_heartbeats", restart_on_missing_heartbeats);
if (rc==true) {
GloVars.restart_on_missing_heartbeats=restart_on_missing_heartbeats;
} else {
GloVars.restart_on_missing_heartbeats = 10; // default
}
} else {
// restart_on_missing_heartbeats was not specified in config file
GloVars.restart_on_missing_heartbeats = 10; // default
}
} else {
// config file not readable
GloVars.datadir=strdup(t);
std::cerr << "[Warning]: Cannot open any default config file . Using default datadir in current working directory " << GloVars.datadir << endl;
}
} else {
GloVars.datadir=GloVars.__cmd_proxysql_datadir;
}
free(t);
GloVars.admindb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.db")+2);
sprintf(GloVars.admindb,"%s/%s",GloVars.datadir, (char *)"proxysql.db");
GloVars.sqlite3serverdb=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"sqlite3server.db")+2);
sprintf(GloVars.sqlite3serverdb,"%s/%s",GloVars.datadir, (char *)"sqlite3server.db");
GloVars.statsdb_disk=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql_stats.db")+2);
sprintf(GloVars.statsdb_disk,"%s/%s",GloVars.datadir, (char *)"proxysql_stats.db");
if (GloVars.errorlog == NULL) {
GloVars.errorlog=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.log")+2);
sprintf(GloVars.errorlog,"%s/%s",GloVars.datadir, (char *)"proxysql.log");
}
GloVars.pid=(char *)malloc(strlen(GloVars.datadir)+strlen((char *)"proxysql.pid")+2);
sprintf(GloVars.pid,"%s/%s",GloVars.datadir, (char *)"proxysql.pid");
if (GloVars.__cmd_proxysql_initial==true) {
std::cerr << "Renaming database file " << GloVars.admindb << endl;
char *newpath=(char *)malloc(strlen(GloVars.admindb)+8);
sprintf(newpath,"%s.bak",GloVars.admindb);
rename(GloVars.admindb,newpath); // FIXME: should we check return value, or ignore whatever it successed or not?
}
GloVars.confFile->ReadGlobals();
GloVars.process_opts_post();
}
void ProxySQL_Main_init_main_modules() {
GloQC=NULL;
GloQPro=NULL;
GloMTH=NULL;
GloMyAuth=NULL;
GloMyLdapAuth = NULL;
#ifdef PROXYSQLCLICKHOUSE
GloClickHouseAuth=NULL;
#endif /* PROXYSQLCLICKHOUSE */
GloMyMon=NULL;
GloMyLogger=NULL;
GloMyStmt=NULL;
// initialize libev
if (!ev_default_loop (EVBACKEND_POLL | EVFLAG_NOENV)) {
fprintf(stderr,"could not initialise libev");
exit(EXIT_FAILURE);
}
MyHGM=new MySQL_HostGroups_Manager();
MyHGM->init();
MySQL_Threads_Handler * _tmp_GloMTH = NULL;
_tmp_GloMTH=new MySQL_Threads_Handler();
GloMTH = _tmp_GloMTH;
GloMyLogger = new MySQL_Logger();
GloMyLogger->print_version();
GloMyStmt=new MySQL_STMT_Manager_v14();
}
void ProxySQL_Main_init_Admin_module() {
// cluster module needs to be initialized before
GloProxyCluster = new ProxySQL_Cluster();
GloProxyCluster->init();
GloProxyCluster->print_version();
GloProxyStats = new ProxySQL_Statistics();
//GloProxyStats->init();
GloProxyStats->print_version();
GloAdmin = new ProxySQL_Admin();
GloAdmin->init();
GloAdmin->print_version();
if (binary_sha1) {
proxy_info("ProxySQL SHA1 checksum: %s\n", binary_sha1);
}
}
void ProxySQL_Main_init_Auth_module() {
GloMyAuth = new MySQL_Authentication();
GloMyAuth->print_version();
GloAdmin->init_users();
//GloMyLdapAuth = create_MySQL_LDAP_Authentication();
if (GloMyLdapAuth) {
GloMyLdapAuth->print_version();
}
}
void ProxySQL_Main_init_Query_module() {
GloQPro = new Query_Processor();
GloQPro->print_version();
GloAdmin->init_mysql_query_rules();
GloAdmin->init_mysql_firewall();
// if (GloWebInterface) {
// GloWebInterface->print_version();
// }
}
void ProxySQL_Main_init_MySQL_Threads_Handler_module() {
unsigned int i;
GloMTH->init();
load_ = 1;
load_ += GloMTH->num_threads;
#ifdef IDLE_THREADS
if (GloVars.global.idle_threads) {
load_ += GloMTH->num_threads;
}
#endif // IDLE_THREADS
for (i=0; i<GloMTH->num_threads; i++) {
GloMTH->create_thread(i,mysql_worker_thread_func, false);
#ifdef IDLE_THREADS
if (GloVars.global.idle_threads) {
GloMTH->create_thread(i,mysql_worker_thread_func_idles, true);
}
#endif // IDLE_THREADS
}
}
void ProxySQL_Main_init_Query_Cache_module() {
GloQC = new Query_Cache();
GloQC->print_version();
pthread_create(&GloQC->purge_thread_id, NULL, mysql_shared_query_cache_funct , NULL);
}
void ProxySQL_Main_init_MySQL_Monitor_module() {
// start MySQL_Monitor
// GloMyMon = new MySQL_Monitor();
if (MyMon_thread == NULL) { // only if not created yet
MyMon_thread = new std::thread(&MySQL_Monitor::run,GloMyMon);
GloMyMon->print_version();
}
}
void ProxySQL_Main_init_SQLite3Server() {
// start SQLite3Server
GloSQLite3Server = new SQLite3_Server();
GloSQLite3Server->init();
GloAdmin->init_sqliteserver_variables();
GloSQLite3Server->print_version();
}
#ifdef PROXYSQLCLICKHOUSE
void ProxySQL_Main_init_ClickHouseServer() {
// start SQServer
GloClickHouseServer = new ClickHouse_Server();
GloClickHouseServer->init();
GloAdmin->init_clickhouse_variables();
GloClickHouseServer->print_version();
GloClickHouseAuth = new ClickHouse_Authentication();
GloClickHouseAuth->print_version();
GloAdmin->init_clickhouse_users();
}
#endif /* PROXYSQLCLICKHOUSE */
void ProxySQL_Main_join_all_threads() {
cpu_timer t;
if (GloMTH) {
cpu_timer t;
GloMTH->shutdown_threads();
#ifdef DEBUG
std::cerr << "GloMTH joined in ";
#endif
}
if (GloQC) {
GloQC->shutdown=1;
}
if (GloMyMon) {
GloMyMon->shutdown=true;
}
// join GloMyMon thread
if (GloMyMon && MyMon_thread) {
cpu_timer t;
MyMon_thread->join();
#ifdef DEBUG
std::cerr << "GloMyMon joined in ";
#endif
}
// join GloQC thread
if (GloQC) {
cpu_timer t;
pthread_join(GloQC->purge_thread_id, NULL);
#ifdef DEBUG
std::cerr << "GloQC joined in ";
#endif
}
#ifdef DEBUG
std::cerr << "All threads joined in ";
#endif
}
void ProxySQL_Main_shutdown_all_modules() {
if (GloMyMon) {
cpu_timer t;
delete GloMyMon;
GloMyMon=NULL;
#ifdef DEBUG
std::cerr << "GloMyMon shutdown in ";
#endif
}
if (GloQC) {
cpu_timer t;
delete GloQC;
GloQC=NULL;
#ifdef DEBUG
std::cerr << "GloQC shutdown in ";
#endif
}
if (GloQPro) {
cpu_timer t;
delete GloQPro;
GloQPro=NULL;
#ifdef DEBUG
std::cerr << "GloQPro shutdown in ";
#endif
}
#ifdef PROXYSQLCLICKHOUSE
if (GloClickHouseAuth) {
cpu_timer t;
delete GloClickHouseAuth;
GloClickHouseAuth=NULL;
#ifdef DEBUG
std::cerr << "GloClickHouseAuth shutdown in ";
#endif
}
if (GloClickHouseServer) {
cpu_timer t;
delete GloClickHouseServer;
GloClickHouseServer=NULL;
#ifdef DEBUG
std::cerr << "GloClickHouseServer shutdown in ";
#endif
}
#endif /* PROXYSQLCLICKHOUSE */
if (GloSQLite3Server) {
cpu_timer t;
delete GloSQLite3Server;
GloSQLite3Server=NULL;
#ifdef DEBUG
std::cerr << "GloSQLite3Server shutdown in ";
#endif
}
if (GloMyAuth) {
cpu_timer t;
delete GloMyAuth;
GloMyAuth=NULL;
#ifdef DEBUG
std::cerr << "GloMyAuth shutdown in ";
#endif
}
if (GloMTH) {
cpu_timer t;
pthread_mutex_lock(&GloVars.global.ext_glomth_mutex);
delete GloMTH;
GloMTH=NULL;
pthread_mutex_unlock(&GloVars.global.ext_glomth_mutex);
#ifdef DEBUG
std::cerr << "GloMTH shutdown in ";
#endif
}
if (GloMyLogger) {
cpu_timer t;
delete GloMyLogger;
GloMyLogger=NULL;
#ifdef DEBUG
std::cerr << "GloMyLogger shutdown in ";
#endif
}
{
cpu_timer t;
delete GloAdmin;
#ifdef DEBUG
std::cerr << "GloAdmin shutdown in ";
#endif
}
{
cpu_timer t;
MyHGM->shutdown();
delete MyHGM;
#ifdef DEBUG
std::cerr << "GloHGM shutdown in ";
#endif
}
if (GloMyStmt) {
delete GloMyStmt;
GloMyStmt=NULL;
}
}
void ProxySQL_Main_init() {
#ifdef DEBUG
GloVars.global.gdbg=false;
glovars.has_debug=true;
#else
glovars.has_debug=false;
#endif /* DEBUG */
// __thr_sfp=l_mem_init();
}
static void LoadPlugins() {
//LoadPlugin_sqlite3_plugin();
SQLite3DB::LoadPlugin(GloVars.sqlite3_plugin);
if (GloVars.web_interface_plugin) {
dlerror();
char * dlsym_error = NULL;
dlerror();
dlsym_error=NULL;
__web_interface = dlopen(GloVars.web_interface_plugin, RTLD_NOW);
if (!__web_interface) {
cerr << "Cannot load library: " << dlerror() << '\n';
exit(EXIT_FAILURE);
} else {
dlerror();
create_Web_Interface = (create_Web_Interface_t *) dlsym(__web_interface, "create_Web_Interface_func");
dlsym_error = dlerror();
if (dlsym_error!=NULL) {
cerr << "Cannot load symbol create_Web_Interface: " << dlsym_error << '\n';
exit(EXIT_FAILURE);
}
}
if (__web_interface==NULL || dlsym_error) {
proxy_error("Unable to load Web_Interface from %s\n", GloVars.web_interface_plugin);
exit(EXIT_FAILURE);
} else {
GloWebInterface = create_Web_Interface();
if (GloWebInterface) {
//GloAdmin->init_WebInterfacePlugin();
//GloAdmin->load_ldap_variables_to_runtime();
}
}
}
if (GloVars.ldap_auth_plugin) {
dlerror();
char * dlsym_error = NULL;
dlerror();
dlsym_error=NULL;
__mysql_ldap_auth = dlopen(GloVars.ldap_auth_plugin, RTLD_NOW);
if (!__mysql_ldap_auth) {
cerr << "Cannot load library: " << dlerror() << '\n';
exit(EXIT_FAILURE);
} else {
dlerror();
create_MySQL_LDAP_Authentication = (create_MySQL_LDAP_Authentication_t *) dlsym(__mysql_ldap_auth, "create_MySQL_LDAP_Authentication_func");
dlsym_error = dlerror();
if (dlsym_error!=NULL) {
cerr << "Cannot load symbol create_MySQL_LDAP_Authentication: " << dlsym_error << '\n';
exit(EXIT_FAILURE);
}
}
if (__mysql_ldap_auth==NULL || dlsym_error) {
proxy_error("Unable to load MySQL_LDAP_Authentication from %s\n", GloVars.ldap_auth_plugin);
exit(EXIT_FAILURE);
} else {
GloMyLdapAuth = create_MySQL_LDAP_Authentication();
// we are removing this from here, and copying in
// ProxySQL_Main_init_phase2___not_started
// the keep record of these two lines to make sure we don't
// do a similar mistakes with other plugins
//
//if (GloMyLdapAuth) {
// GloAdmin->init_ldap();
// GloAdmin->load_ldap_variables_to_runtime();
//}
}
}
}
void ProxySQL_Main_init_phase2___not_started() {
LoadPlugins();
ProxySQL_Main_init_main_modules();
ProxySQL_Main_init_Admin_module();
GloMTH->print_version();
{
cpu_timer t;
GloMyLogger->events_set_datadir(GloVars.datadir);
GloMyLogger->audit_set_datadir(GloVars.datadir);
#ifdef DEBUG
std::cerr << "Main phase3 : GloMyLogger initialized in ";
#endif
}
if (GloVars.configfile_open) {
GloVars.confFile->CloseFile();
}
if (GloMyLdapAuth) {
GloAdmin->init_ldap();
GloAdmin->load_ldap_variables_to_runtime();
}
ProxySQL_Main_init_Auth_module();
if (GloVars.global.nostart) {
pthread_mutex_lock(&GloVars.global.start_mutex);
}
}
void ProxySQL_Main_init_phase3___start_all() {
{
cpu_timer t;
GloMyLogger->events_set_datadir(GloVars.datadir);
GloMyLogger->audit_set_datadir(GloVars.datadir);
#ifdef DEBUG
std::cerr << "Main phase3 : GloMyLogger initialized in ";
#endif
}
// Initialized monitor, no matter if it will be started or not
GloMyMon = new MySQL_Monitor();
// load all mysql servers to GloHGH
{
cpu_timer t;
GloAdmin->init_mysql_servers();
GloAdmin->init_proxysql_servers();
GloAdmin->load_scheduler_to_runtime();
GloAdmin->proxysql_restapi().load_restapi_to_runtime();
#ifdef DEBUG
std::cerr << "Main phase3 : GloAdmin initialized in ";
#endif
}
{
cpu_timer t;
ProxySQL_Main_init_Query_module();
#ifdef DEBUG
std::cerr << "Main phase3 : Query Processor initialized in ";
#endif
}
{
cpu_timer t;
ProxySQL_Main_init_MySQL_Threads_Handler_module();
#ifdef DEBUG
std::cerr << "Main phase3 : MySQL Threads Handler initialized in ";
#endif
}
{
cpu_timer t;
ProxySQL_Main_init_Query_Cache_module();
#ifdef DEBUG
std::cerr << "Main phase3 : Query Cache initialized in ";
#endif
}
do { /* nothing */ } while (load_ != 1);
load_ = 0;
__sync_fetch_and_add(&GloMTH->status_variables.threads_initialized, 1);
{
cpu_timer t;
GloMTH->start_listeners();
#ifdef DEBUG
std::cerr << "Main phase3 : MySQL Threads Handler listeners started in ";
#endif
}
if ( GloVars.global.sqlite3_server == true ) {
cpu_timer t;
ProxySQL_Main_init_SQLite3Server();
sleep(1);
#ifdef DEBUG
std::cerr << "Main phase3 : SQLite3 Server initialized in ";
#endif
}
if (GloVars.global.monitor==true)
{
cpu_timer t;
ProxySQL_Main_init_MySQL_Monitor_module();
#ifdef DEBUG
std::cerr << "Main phase3 : MySQL Monitor initialized in ";
#endif
}
#ifdef PROXYSQLCLICKHOUSE
if ( GloVars.global.clickhouse_server == true ) {
cpu_timer t;
ProxySQL_Main_init_ClickHouseServer();
#ifdef DEBUG
std::cerr << "Main phase3 : ClickHouse Server initialized in ";
#endif
}
#endif /* PROXYSQLCLICKHOUSE */
// LDAP
if (GloMyLdapAuth) {
GloAdmin->init_ldap_variables();
}
}
void ProxySQL_Main_init_phase4___shutdown() {
cpu_timer t;
ProxySQL_Main_join_all_threads();
//write(GloAdmin->pipefd[1], &GloAdmin->pipefd[1], 1); // write a random byte
if (GloVars.global.nostart) {
pthread_mutex_unlock(&GloVars.global.start_mutex);
}
ProxySQL_Main_shutdown_all_modules();
#ifdef DEBUG
std::cerr << "Main init phase4 shutdown completed in ";
#endif
}
void ProxySQL_daemonize_phase1(char *argv0) {
int rc;
daemon_pid_file_ident=GloVars.pid;
daemon_log_ident=daemon_ident_from_argv0(argv0);
rc=chdir(GloVars.datadir);
if (rc) {
daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", GloVars.datadir, strerror(errno));
exit(EXIT_FAILURE);
}
daemon_pid_file_proc=proxysql_pid_file;
pid=daemon_pid_file_is_running();
if (pid>=0) {
daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
exit(EXIT_FAILURE);
}
if (daemon_retval_init() < 0) {
daemon_log(LOG_ERR, "Failed to create pipe.");
exit(EXIT_FAILURE);
}
}
void ProxySQL_daemonize_wait_daemon() {
int ret;
/* Wait for 20 seconds for the return value passed from the daemon process */
if ((ret = daemon_retval_wait(20)) < 0) {
daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if (ret) {
daemon_log(LOG_ERR, "Daemon returned %i as return value.", ret);
}
exit(ret);
}
bool ProxySQL_daemonize_phase2() {
int rc;
/*
// we DO NOT close FDs anymore. See:
// https://github.com/sysown/proxysql/issues/2628
//
// Close FDs
if (daemon_close_all(-1) < 0) {
daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno));
// Send the error condition to the parent process
daemon_retval_send(1);
return false;
}
*/
rc=chdir(GloVars.datadir);
if (rc) {
daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", GloVars.datadir, strerror(errno));
exit(EXIT_FAILURE);
}
/* Create the PID file */
if (daemon_pid_file_create() < 0) {
daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
daemon_retval_send(2);
return false;
}
/* Send OK to parent process */
daemon_retval_send(0);
GloAdmin->flush_error_log();
//daemon_log(LOG_INFO, "Starting ProxySQL\n");
//daemon_log(LOG_INFO, "Sucessfully started");
proxy_info("Starting ProxySQL\n");
proxy_info("Sucessfully started\n");
return true;
}
void call_execute_on_exit_failure() {
if (GloVars.execute_on_exit_failure == NULL) {
return;
}
proxy_error("Trying to call external script after exit failure: %s\n", GloVars.execute_on_exit_failure);
pid_t cpid;
cpid = fork();
if (cpid == -1) {
exit(EXIT_FAILURE);
}
if (cpid == 0) {
int rc;
rc = system(GloVars.execute_on_exit_failure);
if (rc) {
proxy_error("Execute on EXIT_FAILURE: Failed to run %s\n", GloVars.execute_on_exit_failure);
perror("system()");
exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
}
} else {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize (&attr, 64*1024);
pid_t *cpid_ptr=(pid_t *)malloc(sizeof(pid_t));
*cpid_ptr=cpid;
pthread_t thr;
if (pthread_create(&thr, &attr, waitpid_thread, (void *)cpid_ptr) !=0 ) {
perror("Thread creation");
exit(EXIT_FAILURE);
}
}
}
bool ProxySQL_daemonize_phase3() {
int rc;
int status;
//daemon_log(LOG_INFO, "Angel process started ProxySQL process %d\n", pid);
parent_open_error_log();
proxy_info("Angel process started ProxySQL process %d\n", pid);
parent_close_error_log();
rc=waitpid(pid, &status, 0);
if (rc==-1) {
parent_open_error_log();
perror("waitpid");
//proxy_error("[FATAL]: waitpid: %s\n", perror("waitpid"));
exit(EXIT_FAILURE);
}
rc=WIFEXITED(status);
if (rc) { // client exit()ed
rc=WEXITSTATUS(status);
if (rc==0) {
//daemon_log(LOG_INFO, "Shutdown angel process\n");
parent_open_error_log();
proxy_info("Shutdown angel process\n");
exit(EXIT_SUCCESS);
} else {
//daemon_log(LOG_INFO, "ProxySQL exited with code %d . Restarting!\n", rc);
parent_open_error_log();
proxy_error("ProxySQL exited with code %d . Restarting!\n", rc);
call_execute_on_exit_failure();
parent_close_error_log();
return false;
}
} else {
//daemon_log(LOG_INFO, "ProxySQL crashed. Restarting!\n");
parent_open_error_log();
proxy_error("ProxySQL crashed. Restarting!\n");
proxy_info("ProxySQL version %s\n", PROXYSQL_VERSION);
if (binary_sha1) {
proxy_info("ProxySQL SHA1 checksum: %s\n", binary_sha1);
}
call_execute_on_exit_failure();
parent_close_error_log();
return false;
}
return true;
}
void my_terminate(void) {
proxy_error("ProxySQL crashed due to exception\n");
print_backtrace();
}
namespace {
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
int main(int argc, const char * argv[]) {
{
MYSQL *my = mysql_init(NULL);
mysql_close(my);
// cpu_timer t;
ProxySQL_Main_init();
#ifdef DEBUG
// std::cerr << "Main init phase0 completed in ";
#endif
}
{
cpu_timer t;
ProxySQL_Main_process_global_variables(argc, argv);
GloVars.global.start_time=monotonic_time(); // always initialize it
#ifdef DEBUG
std::cerr << "Main init global variables completed in ";
#endif
}
struct rlimit nlimit;
{
int rc = getrlimit(RLIMIT_NOFILE, &nlimit);
if (rc == 0) {
if (nlimit.rlim_cur <= 1024) {
proxy_error("Current RLIMIT_NOFILE is very low: %d . Tune RLIMIT_NOFILE correctly before running ProxySQL\n", nlimit.rlim_cur);
if (nlimit.rlim_max > nlimit.rlim_cur) {
if (nlimit.rlim_max >= 102400) {
nlimit.rlim_cur = 102400;
} else {
nlimit.rlim_cur = nlimit.rlim_max;
}
proxy_warning("Automatically setting RLIMIT_NOFILE to %d\n", nlimit.rlim_cur);
rc = setrlimit(RLIMIT_NOFILE, &nlimit);
if (rc) {
proxy_error("Unable to increase RLIMIT_NOFILE: %s: \n", strerror(errno));
}
} else {
proxy_error("Unable to increase RLIMIT_NOFILE because rlim_max is low: %d\n", nlimit.rlim_max);
}
}
} else {
proxy_error("Call to getrlimit failed: %s\n", strerror(errno));
}
}
{
cpu_timer t;
ProxySQL_Main_init_SSL_module();
#ifdef DEBUG
std::cerr << "Main SSL init variables completed in ";
#endif
}
{
cpu_timer t;
int fd = -1;
char buff[PATH_MAX+1];
ssize_t len = -1;
#if defined(__FreeBSD__)
len = readlink("/proc/curproc/file", buff, sizeof(buff)-1);
#else
len = readlink("/proc/self/exe", buff, sizeof(buff)-1);
#endif
if (len != -1) {
buff[len] = '\0';
fd = open(buff, O_RDONLY);
}
if(fd >= 0) {
struct stat statbuf;
if(fstat(fd, &statbuf) == 0) {
unsigned char *fb = (unsigned char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (fb != MAP_FAILED) {
unsigned char temp[SHA_DIGEST_LENGTH];
SHA1(fb, statbuf.st_size, temp);
binary_sha1 = (char *)malloc(SHA_DIGEST_LENGTH*2+1);
memset(binary_sha1, 0, SHA_DIGEST_LENGTH*2+1);
char buf[SHA_DIGEST_LENGTH*2];
for (int i=0; i < SHA_DIGEST_LENGTH; i++) {
sprintf((char*)&(buf[i*2]), "%02x", temp[i]);
}
memcpy(binary_sha1, buf, SHA_DIGEST_LENGTH*2);
munmap(fb,statbuf.st_size);
} else {
proxy_error("Unable to mmap %s: %s\n", buff, strerror(errno));
}
} else {
proxy_error("Unable to fstat %s: %s\n", buff, strerror(errno));
}
} else {
proxy_error("Unable to open %s: %s\n", argv[0], strerror(errno));
}
#ifdef DEBUG
std::cerr << "SHA1 generated in ";
#endif
}
if (GloVars.global.foreground==false) {
{
cpu_timer t;
ProxySQL_daemonize_phase1((char *)argv[0]);
#ifdef DEBUG
std::cerr << "Main daemonize phase1 completed in ";
#endif
}
/* Do the fork */
if ((pid = daemon_fork()) < 0) {
/* Exit on error */
daemon_retval_done();
exit(EXIT_FAILURE);
} else if (pid) { /* The parent */
ProxySQL_daemonize_wait_daemon();
} else { /* The daemon */
cpu_timer t;
GloVars.global.start_time=monotonic_time();
GloVars.install_signal_handler();
if (ProxySQL_daemonize_phase2()==false) {
goto finish;
}
#ifdef DEBUG
std::cerr << "Main daemonize phase1 completed in ";
#endif
}
laststart=0;
if (glovars.proxy_restart_on_error) {
gotofork:
if (laststart) {
int currenttime=time(NULL);
if (currenttime == laststart) { /// we do not want to restart multiple times in the same second
// if restart is too frequent, something really bad is going on
//daemon_log(LOG_INFO, "Angel process is waiting %d seconds before starting a new ProxySQL process\n", glovars.proxy_restart_delay);
parent_open_error_log();
proxy_info("Angel process is waiting %d seconds before starting a new ProxySQL process\n", glovars.proxy_restart_delay);
parent_close_error_log();
sleep(glovars.proxy_restart_delay);
}
}
laststart=time(NULL);
pid = fork();
if (pid < 0) {
//daemon_log(LOG_INFO, "[FATAL]: Error in fork()\n");
parent_open_error_log();
proxy_error("[FATAL]: Error in fork()\n");
exit(EXIT_FAILURE);
}
if (pid) { /* The parent */
parent_close_error_log();
if (ProxySQL_daemonize_phase3()==false) {
goto gotofork;
}
} else { /* The daemon */
// we open the files also on the child process
// this is required if the child process was created after a crash
parent_open_error_log();
GloVars.global.start_time=monotonic_time();
GloVars.install_signal_handler();
}
}
} else {
GloAdmin->flush_error_log();
GloVars.install_signal_handler();
}
__start_label:
{
cpu_timer t;
ProxySQL_Main_init_phase2___not_started();
#ifdef DEBUG
std::cerr << "Main init phase2 completed in ";
#endif
}
if (glovars.shutdown) {
goto __shutdown;
}
{
cpu_timer t;
ProxySQL_Main_init_phase3___start_all();
#ifdef DEBUG
std::cerr << "Main init phase3 completed in ";
#endif
}
if (GloVars.global.version_check) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t thr;
if (pthread_create(&thr, &attr, main_check_latest_version_thread, NULL) !=0 ) {
perror("Thread creation");
exit(EXIT_FAILURE);
}
}
{
unsigned int missed_heartbeats = 0;
unsigned long long previous_time = monotonic_time();
unsigned int inner_loops = 0;
while (glovars.shutdown==0) {
usleep(200000);
if (disable_watchdog) {
continue;
}
unsigned long long curtime = monotonic_time();
inner_loops++;
if (curtime >= inner_loops*300000 + previous_time ) {
// if this happens, it means that this very simple loop is blocked
// probably we are running under gdb
previous_time = curtime;
inner_loops = 0;
continue;
}
if (GloMTH) {
unsigned long long atomic_curtime = 0;
unsigned long long poll_timeout = (unsigned int)GloMTH->variables.poll_timeout;
unsigned int threads_missing_heartbeat = 0;
poll_timeout += 1000; // add 1 second (rounding up)
poll_timeout *= 1000; // convert to us
if (curtime < previous_time + poll_timeout) {
continue;
}
previous_time = curtime;
inner_loops = 0;
unsigned int i;
if (GloMTH->mysql_threads) {
for (i=0; i<GloMTH->num_threads; i++) {
if (GloMTH->mysql_threads[i].worker) {
atomic_curtime = GloMTH->mysql_threads[i].worker->atomic_curtime;
if (curtime > atomic_curtime + poll_timeout) {
threads_missing_heartbeat++;
}
}
}
}
#ifdef IDLE_THREADS
if (GloVars.global.idle_threads) {
if (GloMTH->mysql_threads) {
for (i=0; i<GloMTH->num_threads; i++) {
if (GloMTH->mysql_threads_idles[i].worker) {
atomic_curtime = GloMTH->mysql_threads_idles[i].worker->atomic_curtime;
if (curtime > atomic_curtime + poll_timeout) {
threads_missing_heartbeat++;
}
}
}
}
}
#endif
if (threads_missing_heartbeat) {
proxy_error("Watchdog: %u threads missed a heartbeat\n", threads_missing_heartbeat);
missed_heartbeats++;
if (missed_heartbeats >= (unsigned int)GloVars.restart_on_missing_heartbeats) {
if (GloVars.restart_on_missing_heartbeats) {
proxy_error("Watchdog: reached %u missed heartbeats. Aborting!\n", missed_heartbeats);
proxy_error("Watchdog: see details at https://github.com/sysown/proxysql/wiki/Watchdog\n");
assert(0);
}
}
} else {
missed_heartbeats = 0;
}
}
}
}
__shutdown:
proxy_info("Starting shutdown...\n");
ProxySQL_Main_init_phase4___shutdown();
proxy_info("Shutdown completed!\n");
if (glovars.reload) {
if (glovars.reload==2) {
GloVars.global.nostart=true;
}
glovars.reload=0;
glovars.shutdown=0;
goto __start_label;
}
finish:
//daemon_log(LOG_INFO, "Exiting...");
proxy_info("Exiting...\n");
daemon_retval_send(255);
daemon_signal_done();
daemon_pid_file_remove();
// l_mem_destroy(__thr_sfp);
#ifdef RUNNING_ON_VALGRIND
if (RUNNING_ON_VALGRIND==0) {
if (__web_interface) {
dlclose(__web_interface);
}
if (__mysql_ldap_auth) {
dlclose(__mysql_ldap_auth);
}
}
#endif
return 0;
}