diff --git a/Documentation/Monitors/Galera-Monitor.md b/Documentation/Monitors/Galera-Monitor.md index 4f0fc47c1..e91373297 100644 --- a/Documentation/Monitors/Galera-Monitor.md +++ b/Documentation/Monitors/Galera-Monitor.md @@ -81,22 +81,6 @@ This disables the assignment of master and slave roles to the Galera cluster nod ``` disable_master_role_setting=true ``` - -### `script` - -This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. The user which is used to run MaxScale should have execution rights to the file itself and the directory it resides in. - -``` -script=/home/user/script.sh -``` - -### `events` - -A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. - -``` -events=master_down,slave_down -``` ### `use_priority` @@ -106,6 +90,10 @@ Enable interaction with server priorities. This will allow the monitor to determ use_priority=true ``` +### Common Monitor Parameters + +For a list of optional parameters that all monitors support, read the [Monitor Common](Monitor-Common.md) document. + ## Interaction with Server Priorities If the `use_priority` option is set and a server is configured with the `priority=` parameter, galeramon will use that as the basis on which the master node is chosen. This requires the `disable_master_role_setting` to be undefined or disabled. The server with the lowest value in `priority` will be chosen as the master node when a replacement Galera node is promoted to a master server inside MaxScale. @@ -135,24 +123,3 @@ priority=2 In this example `node-1` is always used as the master if available. If `node-1` is not available, then the next node with the highest priority rank is used. In this case it would be `node-3`. If both `node-1` and `node-3` were down, then `node-2` would be used. Nodes without priority are considered as having the lowest priority rank and will be used only if all nodes with priority ranks are not available. With priority ranks you can control the order in which MaxScale chooses the master node. This will allow for a controlled failure and replacement of nodes. - -## Script events - -Here is a table of all possible event types and their descriptions. - -Event Name|Description -----------|---------- -master_down|A Master server has gone down -master_up|A Master server has come up -slave_down|A Slave server has gone down -slave_up|A Slave server has come up -server_down|A server with no assigned role has gone down -server_up|A server with no assigned role has come up -synced_down|A synced Galera node has come up -synced_up|A synced Galera node has gone down -lost_master|A server lost Master status -lost_slave|A server lost Slave status -lost_synced|A Galera node lost synced status -new_master|A new Master was detected -new_slave|A new Slave was detected -new_synced|A new synced Galera node was detected diff --git a/Documentation/Monitors/MM-Monitor.md b/Documentation/Monitors/MM-Monitor.md index c2bd5a566..a90d2bf71 100644 --- a/Documentation/Monitors/MM-Monitor.md +++ b/Documentation/Monitors/MM-Monitor.md @@ -67,41 +67,7 @@ This is a situation which can happen if all slave servers are unreachable or the ``` detect_stale_master=true ``` - -### `script` -This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. The user which is used to run MaxScale should have execution rights to the file itself and the directory it resides in. +### Common Monitor Parameters -``` -script=/home/user/script.sh -``` - -This script will be called with the following command line arguments. - -``` - --event= --initiator= --nodelist= -``` -### `events` - -A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. - -``` -events=master_down,slave_down -``` - -## Script events - -Here is a table of all possible event types and their descriptions. - -Event Name|Description -----------|---------- -master_down|A Master server has gone down -master_up|A Master server has come up -slave_down|A Slave server has gone down -slave_up|A Slave server has come up -server_down|A server with no assigned role has gone down -server_up|A server with no assigned role has come up -lost_master|A server lost Master status -lost_slave|A server lost Slave status -new_master|A new Master was detected -new_slave|A new Slave was detected +For a list of optional parameters that all monitors support, read the [Monitor Common](Monitor-Common.md) document. diff --git a/Documentation/Monitors/Monitor-Common.md b/Documentation/Monitors/Monitor-Common.md new file mode 100644 index 000000000..1d27dc419 --- /dev/null +++ b/Documentation/Monitors/Monitor-Common.md @@ -0,0 +1,55 @@ +# Common Monitor Parameters + +This document lists optional parameters that all current monitors support. + +## Parameters + +### `script` + +This command will be executed when a server changes its state. The parameter should be an absolute path to a command or the command should be in the executable path. The user which is used to run MaxScale should have execution rights to the file itself and the directory it resides in. + +``` +script=/home/user/myscript.sh initiator=$INITIATOR event=$EVENT live_nodes=$NODELIST +``` + +The following substitutions will be made to the parameter value: + +* `$INITIATOR` will be replaced with the IP and port of the server who initiated the event +* `$EVENT` will be replaced with the name of the event +* `$NODELIST` will be replaced with a list of server IPs and ports that are running + +For example, the previous example will be executed as: + +``` +/home/user/myscript.sh initiator=192.168.0.10:3306 event=master_down live_nodes=192.168.0.201:3306,192.168.0.121:3306 +``` + +### `events` + +A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. + +``` +events=master_down,slave_down +``` + +## Script events + +Here is a table of all possible event types and their descriptions that the monitors can be called with. + +Event Name|Description +----------|---------- +master_down|A Master server has gone down +master_up|A Master server has come up +slave_down|A Slave server has gone down +slave_up|A Slave server has come up +server_down|A server with no assigned role has gone down +server_up|A server with no assigned role has come up +ndb_down|A MySQL Cluster node has gone down +ndb_up|A MySQL Cluster node has come up +lost_master|A server lost Master status +lost_slave|A server lost Slave status +lost_ndb|A MySQL Cluster node lost node membership +new_master|A new Master was detected +new_slave|A new Slave was detected +new_ndb|A new MySQL Cluster node was found + diff --git a/Documentation/Monitors/MySQL-Monitor.md b/Documentation/Monitors/MySQL-Monitor.md index d56082021..f32af7fde 100644 --- a/Documentation/Monitors/MySQL-Monitor.md +++ b/Documentation/Monitors/MySQL-Monitor.md @@ -83,27 +83,6 @@ This is a situation which can happen if all slave servers are unreachable or the ``` detect_stale_master=true ``` - -### `script` - -This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. The user which is used to run MaxScale should have execution rights to the file itself and the directory it resides in. - -``` -script=/home/user/script.sh -``` - -This script will be called with the following command line arguments. - -``` - --event= --initiator= --nodelist= -``` -### `events` - -A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. - -``` -events=master_down,slave_down -``` ### `mysql51_replication` @@ -113,23 +92,9 @@ Enable support for MySQL 5.1 replication monitoring. This is needed if a MySQL s mysql51_replication=true ``` -## Script events - -Here is a table of all possible event types and their descriptions. - -Event Name|Description -----------|---------- -master_down|A Master server has gone down -master_up|A Master server has come up -slave_down|A Slave server has gone down -slave_up|A Slave server has come up -server_down|A server with no assigned role has gone down -server_up|A server with no assigned role has come up -lost_master|A server lost Master status -lost_slave|A server lost Slave status -new_master|A new Master was detected -new_slave|A new Slave was detected +### Common Monitor Parameters +For a list of optional parameters that all monitors support, read the [Monitor Common](Monitor-Common.md) document. ## Example 1 - Monitor script diff --git a/Documentation/Monitors/NDB-Cluster-Monitor.md b/Documentation/Monitors/NDB-Cluster-Monitor.md index a87ff35a8..806c7858f 100644 --- a/Documentation/Monitors/NDB-Cluster-Monitor.md +++ b/Documentation/Monitors/NDB-Cluster-Monitor.md @@ -54,49 +54,6 @@ This parameter controls the timeout for reading from a monitored server. It is i backend_read_timeout=2 ``` -## MySQL Cluster Monitor optional parameters - -These are optional parameters specific to the MySQL Cluster Monitor. - -### `script` - -This script will be executed when a server changes its state. The parameter should be an absolute path to the script or it should be in the executable path. The user which is used to run MaxScale should have execution rights to the file itself and the directory it resides in. - -``` -script=/home/user/script.sh -``` - -This script will be called with the following command line arguments. - -``` - --event= --initiator= --nodelist= -``` -### `events` - -A list of event names which cause the script to be executed. If this option is not defined, all events cause the script to be executed. The list must contain a comma separated list of event names. - -``` -events=master_down,slave_down -``` - -## Script events - -Here is a table of all possible event types and their descriptions that the MySQL Cluster monitor can be called with. - -Event Name|Description -----------|---------- -master_down|A Master server has gone down -master_up|A Master server has come up -slave_down|A Slave server has gone down -slave_up|A Slave server has come up -server_down|A server with no assigned role has gone down -server_up|A server with no assigned role has come up -ndb_down|A MySQL Cluster node has gone down -ndb_up|A MySQL Cluster node has come up -lost_master|A server lost Master status -lost_slave|A server lost Slave status -lost_ndb|A MySQL Cluster node lost node membership -new_master|A new Master was detected -new_slave|A new Slave was detected -new_ndb|A new MySQL Cluster node was found +### Common Monitor Parameters +For a list of optional parameters that all monitors support, read the [Monitor Common](Monitor-Common.md) document. diff --git a/Documentation/Tutorials/MaxScale-Information-Schema.md b/Documentation/Tutorials/MaxScale-Information-Schema.md index 66bb3ab5b..fcbcd3937 100644 --- a/Documentation/Tutorials/MaxScale-Information-Schema.md +++ b/Documentation/Tutorials/MaxScale-Information-Schema.md @@ -67,6 +67,8 @@ Uptime: 72 Threads: 1 Sessions: 11 The SQL command used to interact with maxinfo is the show command, a variety of show commands are available and will be described in the following sections. +Maxinfo also supports the `FLUSH LOGS`, `SET SERVER ` and `CLEAR SERVER ` commands. These behave the same as their MaxAdmin counterpart. + ## Show variables The show variables command will display a set of name and value pairs for a number of MaxScale system variables. diff --git a/etc/init.d/maxscale.in b/etc/init.d/maxscale.in index ea31e6c19..97d574632 100755 --- a/etc/init.d/maxscale.in +++ b/etc/init.d/maxscale.in @@ -81,10 +81,10 @@ start() { mkdir -p @MAXSCALE_VARDIR@/run/maxscale fi - chown maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale chmod 0755 @MAXSCALE_VARDIR@/log/maxscale chmod 0755 @MAXSCALE_VARDIR@/lib/maxscale chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale diff --git a/etc/postinst.in b/etc/postinst.in index 93cc768ec..63d6de1f7 100755 --- a/etc/postinst.in +++ b/etc/postinst.in @@ -21,10 +21,10 @@ then fi # Change the owner of the directories to maxscale:maxscale -chown maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale -chown maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale -chown maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale -chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale +chown -R maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale +chown -R maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale +chown -R maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale +chown -R maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale chmod 0755 @MAXSCALE_VARDIR@/log/maxscale chmod 0755 @MAXSCALE_VARDIR@/lib/maxscale chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale diff --git a/etc/ubuntu/init.d/maxscale.in b/etc/ubuntu/init.d/maxscale.in index a6985b4ba..341faefb6 100755 --- a/etc/ubuntu/init.d/maxscale.in +++ b/etc/ubuntu/init.d/maxscale.in @@ -81,10 +81,10 @@ start() { mkdir -p @MAXSCALE_VARDIR@/run/maxscale fi - chown maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale - chown maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/log/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/lib/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/cache/maxscale + chown -R maxscale:maxscale @MAXSCALE_VARDIR@/run/maxscale chmod 0755 @MAXSCALE_VARDIR@/log/maxscale chmod 0755 @MAXSCALE_VARDIR@/lib/maxscale chmod 0755 @MAXSCALE_VARDIR@/cache/maxscale diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 3b01141b9..f81f9ef79 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -49,16 +49,44 @@ static const char LOGFILE_NAME_SUFFIX[] = ".log"; extern char *program_invocation_name; extern char *program_invocation_short_name; +/** + * LOG_FLUSH_NO Do not flush after writing. + * LOG_FLUSH_YES Flush after writing. + */ +enum log_flush +{ + LOG_FLUSH_NO = 0, + LOG_FLUSH_YES = 1 +}; + #if defined(SS_DEBUG) static int write_index; static int block_start_index; static int prevval; static simple_mutex_t msg_mutex; #endif -static int highprec = 0; -static int do_syslog = 1; -static int do_maxscalelog = 1; -static int use_stdout = 0; + +/** + * Default augmentation. + */ +static int DEFAULT_LOG_AUGMENTATION = 0; + +static struct +{ + int augmentation; // Can change during the lifetime of log_manager. + bool do_highprecision; // Can change during the lifetime of log_manager. + bool do_syslog; // Can change during the lifetime of log_manager. + bool do_maxscalelog; // Can change during the lifetime of log_manager. + bool use_stdout; // Can NOT changed during the lifetime of log_manager. +} log_config = +{ + DEFAULT_LOG_AUGMENTATION, // augmentation + false, // do_highprecision + true, // do_syslog + true, // do_maxscalelog + false // use_stdout +}; + /** * Variable holding the enabled logfiles information. * Used from log users to check enabled logs prior calling @@ -90,11 +118,8 @@ ssize_t log_ses_count[LOGFILE_LAST] = {0}; */ const char* shm_pathname_prefix = "/dev/shm/"; -/** Logfile ids from call argument '-s' */ -char* shmem_id_str = NULL; /** Errors are written to syslog too by default */ -char* syslog_id_str = strdup("LOGFILE_ERROR"); -char* syslog_ident_str = NULL; +char* syslog_id_str = strdup("LOGFILE_ERROR"); /** Forward declarations */ @@ -113,12 +138,6 @@ static bool flushall_flag; static bool flushall_started_flag; static bool flushall_done_flag; -/** - * Default augmentation. - */ -static int default_log_augmentation = 0; -static int log_augmentation = default_log_augmentation; - /** This is used to detect if the initialization of the log manager has failed * and that it isn't initialized again after a failure has occurred. */ static bool fatal_error = false; @@ -177,7 +196,6 @@ struct logfile flat_obj_state_t lf_state; bool lf_init_started; bool lf_store_shmem; - bool lf_write_syslog; logmanager_t* lf_lmgr; /** fwr_logmes is for messages from log clients */ skygw_message_t* lf_logmes; @@ -231,6 +249,7 @@ struct logmanager fnames_conf_t lm_fnames_conf; logfile_t lm_logfile; filewriter_t lm_filewriter; + log_target_t lm_target; #if defined(SS_DEBUG) skygw_chk_t lm_chk_tail; #endif @@ -252,8 +271,7 @@ typedef struct strpart static bool logfiles_init(logmanager_t* lmgr); static bool logfile_init(logfile_t* logfile, logmanager_t* logmanager, - bool store_shmem, - bool write_syslog); + bool store_shmem); static void logfile_done(logfile_t* logfile); static void logfile_free_memory(logfile_t* lf); static void logfile_flush(logfile_t* lf); @@ -262,27 +280,27 @@ static bool logfile_create(logfile_t* lf); static bool logfile_open_file(filewriter_t* fw, logfile_t* lf); static char* form_full_file_name(strpart_t* parts, logfile_t* lf, int seqnoidx); -static bool filewriter_init(logmanager_t* logmanager, - filewriter_t* fw, - skygw_message_t* clientmes, - skygw_message_t* logmes); +static bool filewriter_init(logmanager_t* logmanager, + filewriter_t* fw); static void filewriter_done(filewriter_t* filewriter); -static bool fnames_conf_init(fnames_conf_t* fn, const char* logdir, int argc, char* argv[]); +static bool fnames_conf_init(fnames_conf_t* fn, const char* logdir); static void fnames_conf_done(fnames_conf_t* fn); static void fnames_conf_free_memory(fnames_conf_t* fn); static void* thr_filewriter_fun(void* data); static logfile_t* logmanager_get_logfile(logmanager_t* lm); static bool logmanager_register(bool writep); static void logmanager_unregister(void); -static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]); +static bool logmanager_init_nomutex(const char* ident, + const char* logdir, + log_target_t target); static void logmanager_done_nomutex(void); static bool logmanager_is_valid_id(logfile_id_t id); -static int logmanager_write_log(logfile_id_t id, - enum log_flush flush, - size_t prefix_len, - size_t len, - const char* str); +static int logmanager_write_log(int priority, + enum log_flush flush, + size_t prefix_len, + size_t len, + const char* str); static blockbuf_t* blockbuf_init(); static void blockbuf_node_done(void* bb_data); @@ -305,25 +323,24 @@ static int find_last_seqno(strpart_t* parts, int seqno, int seqnoidx); void flushall_logfiles(bool flush); bool thr_flushall_check(); -const char* get_logpath_default(void) -{ - return "/var/log/maxscale"; -} - -static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]) +static bool logmanager_init_nomutex(const char* ident, + const char* logdir, + log_target_t target) { fnames_conf_t* fn; filewriter_t* fw; int err; - bool succp = false; + bool succ = false; lm = (logmanager_t *)calloc(1, sizeof(logmanager_t)); if (lm == NULL) { err = 1; - goto return_succp; + goto return_succ; } + + lm->lm_target = (target == LOG_TARGET_DEFAULT ? LOG_TARGET_FS : target); #if defined(SS_DEBUG) lm->lm_chk_top = CHK_NUM_LOGMANAGER; lm->lm_chk_tail = CHK_NUM_LOGMANAGER; @@ -338,7 +355,7 @@ static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]) if (lm->lm_clientmes == NULL || lm->lm_logmes == NULL) { err = 1; - goto return_succp; + goto return_succ; } lm->lm_enabled_logfiles |= LOGFILE_ERROR; @@ -349,24 +366,22 @@ static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]) fn->fn_state = UNINIT; fw->fwr_state = UNINIT; - if (!do_syslog) - { - free(syslog_id_str); - syslog_id_str = NULL; - } + // The openlog call is always made, but a connection to the system logger will + // not be made until a call to syslog is made. + openlog(ident, LOG_PID | LOG_ODELAY, LOG_USER); /** Initialize configuration including log file naming info */ - if (!fnames_conf_init(fn, logdir, argc, argv)) + if (!fnames_conf_init(fn, logdir)) { err = 1; - goto return_succp; + goto return_succ; } /** Initialize logfiles */ if (!logfiles_init(lm)) { err = 1; - goto return_succp; + goto return_succ; } /** @@ -378,10 +393,10 @@ static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]) * Initialize filewriter data and open the log file * for each log file type. */ - if (!filewriter_init(lm, fw, lm->lm_clientmes, lm->lm_logmes)) + if (!filewriter_init(lm, fw)) { err = 1; - goto return_succp; + goto return_succ; } /** Initialize and start filewriter thread */ @@ -390,27 +405,27 @@ static bool logmanager_init_nomutex(const char* logdir, int argc, char* argv[]) if (fw->fwr_thread == NULL) { err = 1; - goto return_succp; + goto return_succ; } if ((err = skygw_thread_start(fw->fwr_thread)) != 0) { - goto return_succp; + goto return_succ; } /** Wait message from filewriter_thr */ skygw_message_wait(fw->fwr_clientmes); - succp = true; + succ = true; lm->lm_enabled = true; -return_succp: +return_succ: if (err != 0) { /** This releases memory of all created objects */ logmanager_done_nomutex(); fprintf(stderr, "*\n* Error : Initializing log manager failed.\n*\n"); } - return succp; + return succ; } @@ -418,31 +433,34 @@ return_succp: /** * Initializes log managing routines in MariaDB Corporation MaxScale. * + * @param ident The syslog ident. If NULL, then the program name is used. * @param logdir The directory for the log file. If NULL logging will be made to stdout. - * @param argc number of arguments in argv array - * @param argv arguments array + * @param target Whether the log should be written to filesystem or shared memory. + * Meaningless if logdir is NULL. * * @return true if succeed, otherwise false * */ -bool skygw_logmanager_init(const char* logdir, int argc, char* argv[]) +bool mxs_log_init(const char* ident, const char* logdir, log_target_t target) { - bool succp = false; + bool succ = false; acquire_lock(&lmlock); - if (lm != NULL) + if (!lm) { - succp = true; - goto return_succp; + succ = logmanager_init_nomutex(ident, logdir, target); + } + else + { + // TODO: This is not ok. If the parameters are different then + // TODO: we pretend something is what it is not. + succ = true; } - succp = logmanager_init_nomutex(logdir, argc, argv); - -return_succp: release_lock(&lmlock); - return succp; + return succ; } /** @@ -476,10 +494,8 @@ static void logmanager_done_nomutex(void) /** Release logfile memory */ logfile_done(lf); - if (syslog_id_str) - { - closelog(); - } + closelog(); + /** Release messages and finally logmanager memory */ fnames_conf_done(&lm->lm_fnames_conf); skygw_message_done(lm->lm_clientmes); @@ -490,53 +506,40 @@ static void logmanager_done_nomutex(void) lm = NULL; } - -/** - * This function is provided for atexit() system function. - */ -void skygw_logmanager_exit(void) -{ - skygw_logmanager_done(); -} - /** * End execution of log manager * * Stops file writing thread, releases filewriter, and logfiles. * */ -void skygw_logmanager_done(void) +void mxs_log_finish(void) { acquire_lock(&lmlock); - if (lm == NULL) + if (lm) { - release_lock(&lmlock); - return; - } - CHK_LOGMANAGER(lm); - /** Mark logmanager unavailable */ - lm->lm_enabled = false; + CHK_LOGMANAGER(lm); + /** Mark logmanager unavailable */ + lm->lm_enabled = false; - /** Wait until all users have left or someone shuts down - * logmanager between lock release and acquire. - */ - while (lm != NULL && lm->lm_nlinks != 0) - { - release_lock(&lmlock); - pthread_yield(); - acquire_lock(&lmlock); + /** Wait until all users have left or someone shuts down + * logmanager between lock release and acquire. + */ + while (lm != NULL && lm->lm_nlinks != 0) + { + release_lock(&lmlock); + pthread_yield(); + acquire_lock(&lmlock); + } + + /** Shut down if not already shutted down. */ + if (lm) + { + ss_dassert(lm->lm_nlinks == 0); + logmanager_done_nomutex(); + } } - /** Logmanager was already shut down. Return successfully. */ - if (lm == NULL) - { - goto return_void; - } - ss_dassert(lm->lm_nlinks == 0); - logmanager_done_nomutex(); - -return_void: release_lock(&lmlock); } @@ -577,7 +580,7 @@ static bool logmanager_is_valid_id(logfile_id_t id) { const char ERRSTR[] = "Invalid logfile id argument."; - int err = logmanager_write_log(LOGFILE_ERROR, + int err = logmanager_write_log(LOG_ERR, LOG_FLUSH_YES, 0, sizeof(ERRSTR), ERRSTR); @@ -595,7 +598,7 @@ static bool logmanager_is_valid_id(logfile_id_t id) * * Parameters: * - * @param id logfile object identifier + * @param priority Syslog priority * @param flush indicates whether log string must be written to disk * immediately * @param rotate if set, closes currently open log file and opens a @@ -607,11 +610,11 @@ static bool logmanager_is_valid_id(logfile_id_t id) * @return 0 if succeed, -1 otherwise * */ -static int logmanager_write_log(logfile_id_t id, - enum log_flush flush, - size_t prefix_len, - size_t str_len, - const char* str) +static int logmanager_write_log(int priority, + enum log_flush flush, + size_t prefix_len, + size_t str_len, + const char* str) { logfile_t* lf; char* wp; @@ -621,194 +624,183 @@ static int logmanager_write_log(logfile_id_t id, size_t timestamp_len; int i; + // The config parameters are copied to local variables, because the values in + // log_config may change during the course of the function, with would have + // unpleasant side-effects. + int do_highprecision = log_config.do_highprecision; + int do_maxscalelog = log_config.do_maxscalelog; + int do_syslog = log_config.do_syslog; + + assert(str); + assert((priority & ~LOG_PRIMASK) == 0); CHK_LOGMANAGER(lm); - if (!logmanager_is_valid_id(id)) - { - err = -1; - ss_dassert(false); - goto return_err; - } // All messages are now logged to the error log file. lf = &lm->lm_logfile; CHK_LOGFILE(lf); + /** Length of string that will be written, limited by bufsize */ + size_t safe_str_len; + /** Length of session id */ + size_t sesid_str_len; + size_t cmplen = 0; /** - * When string pointer is NULL, operation is flush. + * 2 braces, 2 spaces and terminating char + * If session id is stored to tls_log_info structure, allocate + * room for session id too. */ - if (str == NULL) + if ((priority == LOG_INFO) && (tls_log_info.li_sesid != 0)) { - if (flush) - { - logfile_flush(lf); /*< wakes up file writer */ - } + sesid_str_len = 5 * sizeof(char) + get_decimal_len(tls_log_info.li_sesid); } else { - /** Length of string that will be written, limited by bufsize */ - size_t safe_str_len; - /** Length of session id */ - size_t sesid_str_len; - size_t cmplen = 0; - /** - * 2 braces, 2 spaces and terminating char - * If session id is stored to tls_log_info structure, allocate - * room for session id too. - */ - if (id == LOGFILE_TRACE && tls_log_info.li_sesid != 0) - { - sesid_str_len = 5 * sizeof(char) + get_decimal_len(tls_log_info.li_sesid); - } - else - { - sesid_str_len = 0; - } - if (highprec) - { - timestamp_len = get_timestamp_len_hp(); - } - else - { - timestamp_len = get_timestamp_len(); - } - cmplen = sesid_str_len > 0 ? sesid_str_len - sizeof(char) : 0; + sesid_str_len = 0; + } + if (do_highprecision) + { + timestamp_len = get_timestamp_len_hp(); + } + else + { + timestamp_len = get_timestamp_len(); + } + cmplen = sesid_str_len > 0 ? sesid_str_len - sizeof(char) : 0; - bool overflow = false; - /** Find out how much can be safely written with current block size */ - if (timestamp_len - sizeof(char) + cmplen + str_len > lf->lf_buf_size) - { - safe_str_len = lf->lf_buf_size; - overflow = true; - } - else - { - safe_str_len = timestamp_len - sizeof(char) + cmplen + str_len; - } - /** - * Seek write position and register to block buffer. - * Then print formatted string to write position. - */ + bool overflow = false; + /** Find out how much can be safely written with current block size */ + if (timestamp_len - sizeof(char) + cmplen + str_len > lf->lf_buf_size) + { + safe_str_len = lf->lf_buf_size; + overflow = true; + } + else + { + safe_str_len = timestamp_len - sizeof(char) + cmplen + str_len; + } + /** + * Seek write position and register to block buffer. + * Then print formatted string to write position. + */ #if defined (SS_LOG_DEBUG) + { + char *copy, *tok; + int tokval; + + simple_mutex_lock(&msg_mutex, true); + copy = strdup(str); + tok = strtok(copy, "|"); + tok = strtok(NULL, "|"); + + if (strstr(str, "message|") && tok) { - char *copy, *tok; - int tokval; + tokval = atoi(tok); - simple_mutex_lock(&msg_mutex, true); - copy = strdup(str); - tok = strtok(copy, "|"); - tok = strtok(NULL, "|"); - - if (strstr(str, "message|") && tok) + if (prevval > 0) { - tokval = atoi(tok); - - if (prevval > 0) - { - ss_dassert(tokval == (prevval + 1)); - } - prevval = tokval; + ss_dassert(tokval == (prevval + 1)); } - free(copy); - simple_mutex_unlock(&msg_mutex); + prevval = tokval; } + free(copy); + simple_mutex_unlock(&msg_mutex); + } #endif - /** Book space for log string from buffer */ - if (do_maxscalelog) - { - // All messages are now logged to the error log file. - wp = blockbuf_get_writepos(&bb, safe_str_len, flush); - } - else - { - wp = (char*)malloc(sizeof(char) * (timestamp_len - sizeof(char) + cmplen + str_len + 1)); - } + /** Book space for log string from buffer */ + if (do_maxscalelog) + { + // All messages are now logged to the error log file. + wp = blockbuf_get_writepos(&bb, safe_str_len, flush); + } + else + { + wp = (char*)malloc(sizeof(char) * (timestamp_len - sizeof(char) + cmplen + str_len + 1)); + } - if (wp == NULL) - { - return -1; - } + if (wp == NULL) + { + return -1; + } #if defined (SS_LOG_DEBUG) - { - sprintf(wp, "[msg:%d]", atomic_add(&write_index, 1)); - safe_str_len -= strlen(wp); - wp += strlen(wp); - } + { + sprintf(wp, "[msg:%d]", atomic_add(&write_index, 1)); + safe_str_len -= strlen(wp); + wp += strlen(wp); + } #endif + /** + * Write timestamp with at most characters + * to wp. + * Returned timestamp_len doesn't include terminating null. + */ + if (do_highprecision) + { + timestamp_len = snprint_timestamp_hp(wp, timestamp_len); + } + else + { + timestamp_len = snprint_timestamp(wp, timestamp_len); + } + if (sesid_str_len != 0) + { /** - * Write timestamp with at most characters - * to wp. - * Returned timestamp_len doesn't include terminating null. + * Write session id */ - if (highprec) - { - timestamp_len = snprint_timestamp_hp(wp, timestamp_len); - } - else - { - timestamp_len = snprint_timestamp(wp, timestamp_len); - } - if (sesid_str_len != 0) - { - /** - * Write session id - */ - snprintf(wp + timestamp_len, sesid_str_len, "[%lu] ", tls_log_info.li_sesid); - sesid_str_len -= 1; /*< don't calculate terminating char anymore */ - } - /** - * Write next string to overwrite terminating null character - * of the timestamp string. - */ - snprintf(wp + timestamp_len + sesid_str_len, - safe_str_len-timestamp_len-sesid_str_len, - "%s", - str); + snprintf(wp + timestamp_len, sesid_str_len, "[%lu] ", tls_log_info.li_sesid); + sesid_str_len -= 1; /*< don't calculate terminating char anymore */ + } + /** + * Write next string to overwrite terminating null character + * of the timestamp string. + */ + snprintf(wp + timestamp_len + sesid_str_len, + safe_str_len-timestamp_len-sesid_str_len, + "%s", + str); - /** Add an ellipsis to an overflowing message to signal truncation. */ - if (overflow && safe_str_len > 4) - { - memset(wp + safe_str_len - 4, '.', 3); - } - /** write to syslog */ - if (lf->lf_write_syslog) - { - // Strip away the timestamp and the prefix (e.g. "[Error]: "). - const char *message = wp + timestamp_len + prefix_len; + /** Add an ellipsis to an overflowing message to signal truncation. */ + if (overflow && safe_str_len > 4) + { + memset(wp + safe_str_len - 4, '.', 3); + } + /** write to syslog */ + if (do_syslog) + { + // Strip away the timestamp and the prefix (e.g. "error : "). + const char *message = wp + timestamp_len + prefix_len; - switch (id) - { - case LOGFILE_ERROR: - syslog(LOG_ERR, "%s", message); - break; - - case LOGFILE_MESSAGE: - syslog(LOG_NOTICE, "%s", message); - break; - - default: - break; - } - } - /** remove double line feed */ - if (wp[safe_str_len - 2] == '\n') + switch (priority) { - wp[safe_str_len - 2] = ' '; - } - wp[safe_str_len - 1] = '\n'; + case LOG_ERR: + syslog(LOG_ERR, "%s", message); + break; - if (do_maxscalelog) - { - blockbuf_unregister(bb); - } - else - { - free(wp); - } - } /* if (str == NULL) */ + case LOG_NOTICE: + syslog(LOG_NOTICE, "%s", message); + break; + + default: + break; + } + } + /** remove double line feed */ + if (wp[safe_str_len - 2] == '\n') + { + wp[safe_str_len - 2] = ' '; + } + wp[safe_str_len - 1] = '\n'; + + if (do_maxscalelog) + { + blockbuf_unregister(bb); + } + else + { + free(wp); + } -return_err: return err; } @@ -879,7 +871,7 @@ static char* blockbuf_get_writepos(blockbuf_t** p_bb, mlist_node_t* node; blockbuf_t* bb; #if defined(SS_DEBUG) - bool succp; + bool succ; #endif CHK_LOGMANAGER(lm); @@ -963,8 +955,8 @@ static char* blockbuf_get_writepos(blockbuf_t** p_bb, bb_list->mlist_versno += 1; ss_dassert(bb_list->mlist_versno % 2 == 1); - ss_debug(succp =) mlist_add_data_nomutex(bb_list, bb); - ss_dassert(succp); + ss_debug(succ =) mlist_add_data_nomutex(bb_list, bb); + ss_dassert(succ); /** * Increase version to even to mark completion of update. @@ -1064,8 +1056,8 @@ static char* blockbuf_get_writepos(blockbuf_t** p_bb, bb_list->mlist_versno += 1; ss_dassert(bb_list->mlist_versno % 2 == 1); - ss_debug(succp =) mlist_add_data_nomutex(bb_list, bb); - ss_dassert(succp); + ss_debug(succ =) mlist_add_data_nomutex(bb_list, bb); + ss_dassert(succ); /** * Increase version to even to mark completion of update. @@ -1220,7 +1212,7 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) if (logmanager_is_valid_id(id)) { - if (use_stdout == 0) + if (!log_config.use_stdout) { const char *name; @@ -1270,20 +1262,20 @@ static bool logfile_set_enabled(logfile_id_t id, bool val) return rval; } -void skygw_log_set_augmentation(int bits) +/** + * Set log augmentation. + * + * @param bits One of the log_augmentation_t constants. + */ +void mxs_log_set_augmentation(int bits) { - log_augmentation = bits & LOG_AUGMENTATION_MASK; -} - -int skygw_log_get_augmentation() -{ - return log_augmentation; + log_config.augmentation = bits & LOG_AUGMENTATION_MASK; } /** * Helper for skygw_log_write and friends. * - * @param id The id of the log file. + * @param int The syslog priority. * @param file The name of the file where the logging was made. * @param int The line where the logging was made. * @param function The function where the logging was made. @@ -1295,7 +1287,7 @@ int skygw_log_get_augmentation() * @return 0 if the logging to at least one log succeeded. */ -static int log_write(logfile_id_t id, +static int log_write(int priority, const char* file, int line, const char* function, @@ -1310,7 +1302,7 @@ static int log_write(logfile_id_t id, { CHK_LOGMANAGER(lm); - rv = logmanager_write_log(id, flush, prefix_len, len, str); + rv = logmanager_write_log(priority, flush, prefix_len, len, str); logmanager_unregister(); } @@ -1318,261 +1310,6 @@ static int log_write(logfile_id_t id, return rv; } -typedef struct log_prefix -{ - const char* text; // The prefix, e.g. "[Error]: " - int len; // The length of the prefix without the trailing NULL. -} log_prefix_t; - -const char PREFIX_ERROR[] = "[Error] : "; -const char PREFIX_NOTICE[] = "[Notice]: "; -const char PREFIX_INFO[] = "[Info] : "; -const char PREFIX_DEBUG[] = "[Debug] : "; - -/** - * Returns the most "severe" file id. - * - * @param ids A single id or a bitmask of ids. - * @return A single id - */ -static logfile_id_t logfile_ids_to_id(logfile_id_t ids) -{ - // The id can be a bitmask, hence we choose the most "severe" one. - if (ids & LOGFILE_ERROR) - { - return LOGFILE_ERROR; - } - else if (ids & LOGFILE_MESSAGE) - { - return LOGFILE_MESSAGE; - } - else if (ids & LOGFILE_TRACE) - { - return LOGFILE_TRACE; - } - else if (ids & LOGFILE_DEBUG) - { - return LOGFILE_DEBUG; - } - else - { - assert(!true); - return LOGFILE_ERROR; - } -} - -/** - * Returns the prefix to be used for a specific logfile id. - * - * @param id A logfile id (not a mask) - * @return The corresponding prefix. - */ -static log_prefix_t logfile_id_to_prefix(logfile_id_t id) -{ - log_prefix_t prefix; - - switch (id) - { - case LOGFILE_ERROR: - prefix.text = PREFIX_ERROR; - prefix.len = sizeof(PREFIX_ERROR); - break; - - case LOGFILE_MESSAGE: - prefix.text = PREFIX_NOTICE; - prefix.len = sizeof(PREFIX_NOTICE); - break; - - case LOGFILE_TRACE: - prefix.text = PREFIX_INFO; - prefix.len = sizeof(PREFIX_INFO); - break; - - case LOGFILE_DEBUG: - prefix.text = PREFIX_DEBUG; - prefix.len = sizeof(PREFIX_DEBUG); - break; - - default: - assert(!true); - prefix.text = PREFIX_ERROR; - prefix.len = sizeof(PREFIX_ERROR); - break; - } - - --prefix.len; // Remove trailing NULL. - - return prefix; -} - -int skygw_log_write_context(logfile_id_t id, - enum log_flush flush, - const char* file, - int line, - const char* function, - const char* str, - ...) -{ - int err = 0; - - id = logfile_ids_to_id(id); // Pick the most severe one. - - if (LOG_IS_ENABLED(id)) - { - va_list valist; - - /** - * Find out the length of log string (to be formatted str). - */ - va_start(valist, str); - int message_len = vsnprintf(NULL, 0, str, valist); - va_end(valist); - - if (message_len >= 0) - { - log_prefix_t prefix = logfile_id_to_prefix(id); - - static const char FORMAT_FUNCTION[] = "(%s): "; - - int augmentation_len = 0; - - switch (log_augmentation) - { - case LOG_AUGMENT_WITH_FUNCTION: - augmentation_len = sizeof(FORMAT_FUNCTION) - 1; // Remove trailing 0 - augmentation_len -= 2; // Remove the %s - augmentation_len += strlen(function); - break; - - default: - break; - } - - int buffer_len = prefix.len + augmentation_len + message_len + 1; // Trailing NULL - - if (buffer_len > MAX_LOGSTRLEN) - { - message_len -= (buffer_len - MAX_LOGSTRLEN); - buffer_len = MAX_LOGSTRLEN; - - assert(prefix.len + augmentation_len + message_len + 1 == buffer_len); - } - - char buffer[buffer_len]; - - char *prefix_text = buffer; - char *augmentation_text = buffer + prefix.len; - char *message_text = buffer + prefix.len + augmentation_len; - - strcpy(prefix_text, prefix.text); - - if (augmentation_len) - { - int len = 0; - - switch (log_augmentation) - { - case LOG_AUGMENT_WITH_FUNCTION: - len = sprintf(augmentation_text, FORMAT_FUNCTION, function); - break; - - default: - assert(!true); - } - - assert(len == augmentation_len); - } - - va_start(valist, str); - vsnprintf(message_text, message_len + 1, str, valist); - va_end(valist); - - err = log_write(id, file, line, function, prefix.len, buffer_len, buffer, flush); - - if (err != 0) - { - fprintf(stderr, "skygw_log_write failed.\n"); - } - } - } - - return err; -} - - -int skygw_log_flush(logfile_id_t id) -{ - int err = -1; - - if (id == LOGFILE_ERROR) - { - if (logmanager_register(false)) - { - CHK_LOGMANAGER(lm); - - logfile_t *lf = logmanager_get_logfile(lm); - CHK_LOGFILE(lf); - - logfile_flush(lf); - err = 0; - - logmanager_unregister(); - } - else - { - ss_dfprintf(stderr, "Can't register to logmanager, flushing failed.\n"); - } - } - else - { - // We'll pretend everything went ok. - err = 0; - } - - return err; -} - -/** - * Replace current logfile with new file with increased sequence number on - * its name. - */ -int skygw_log_rotate(logfile_id_t id) -{ - int err = -1; - - if (id == LOGFILE_ERROR) - { - if (logmanager_register(false)) - { - CHK_LOGMANAGER(lm); - - logfile_t *lf = logmanager_get_logfile(lm); - CHK_LOGFILE(lf); - - MXS_NOTICE("Log rotation is called for %s.", lf->lf_full_file_name); - - logfile_rotate(lf); - err = 0; - - logmanager_unregister(); - } - else - { - ss_dfprintf(stderr, "Can't register to logmanager, rotating failed.\n"); - } - } - else - { - // We'll pretend everything went ok. - err = 0; - } - - return err; -} - - - - /** * @node Register as a logging client to logmanager. * @@ -1588,7 +1325,7 @@ int skygw_log_rotate(logfile_id_t id) */ static bool logmanager_register(bool writep) { - bool succp = true; + bool succ = true; acquire_lock(&lmlock); @@ -1602,8 +1339,8 @@ static bool logmanager_register(bool writep) */ if (!writep || fatal_error) { - succp = false; - goto return_succp; + succ = false; + goto return_succ; } ss_dassert(lm == NULL || (lm != NULL && !lm->lm_enabled)); @@ -1622,24 +1359,26 @@ static bool logmanager_register(bool writep) if (lm == NULL) { - // TODO: This looks fishy. - succp = logmanager_init_nomutex(get_logpath_default(), 0, NULL); + // If someone is logging before the log manager has been inited, + // or after the log manager has been finished, the messages are + // written to stdout. + succ = logmanager_init_nomutex(NULL, NULL, LOG_TARGET_DEFAULT); } } /** if logmanager existed or was succesfully restarted, increase link */ - if (succp) + if (succ) { lm->lm_nlinks += 1; } -return_succp: +return_succ: - if (!succp) + if (!succ) { fatal_error = true; } release_lock(&lmlock); - return succp; + return succ; } /** @@ -1672,26 +1411,15 @@ static void logmanager_unregister(void) * * @param fn The fnames_conf_t structure to initialize. * @param logdir The directory for the log file. If NULL logging will be made to stdout. - * @param argc number of arguments in argv array - * @param argv arguments array * * @return True if the initialization was performed, false otherwise. * * @details Note that input parameter lenghts are checked here. * */ -static bool fnames_conf_init(fnames_conf_t* fn, - const char* logdir, - int argc, - char* argv[]) +static bool fnames_conf_init(fnames_conf_t* fn, const char* logdir) { - int opt; - bool succp = false; - const char* argstr = - "-h - help\n" - "-l .......(no default)\n" - "-m ............(argv[0])\n" - "-s .......(no default)\n"; + bool succ = false; /** * When init_started is set, clean must be done for it. @@ -1701,87 +1429,29 @@ static bool fnames_conf_init(fnames_conf_t* fn, fn->fn_chk_top = CHK_NUM_FNAMES; fn->fn_chk_tail = CHK_NUM_FNAMES; #endif - optind = 1; /**fn_logpath = strdup(logdir); + log_config.use_stdout = false; + dir = logdir; } else { - use_stdout = 1; + log_config.use_stdout = true; // TODO: Re-arrange things so that fn->fn_logpath can be NULL. - fn->fn_logpath = strdup(get_logpath_default()); + dir = "/tmp"; } - /** Set identity string for syslog if it is not set in config.*/ - if (do_syslog) - { - syslog_ident_str = - (syslog_ident_str == NULL ? - (argv == NULL ? strdup(program_invocation_short_name) : strdup(*argv)) : - syslog_ident_str); - } - /* ss_dfprintf(stderr, "\n\n\tCommand line : "); - for (i = 0; i < argc; i++) - { - ss_dfprintf(stderr, "%s ", argv[i]); - } - ss_dfprintf(stderr, "\n");*/ -#if defined(NOT_USED) - fprintf(stderr, - "Log :\t%s/%s1%s\n\n", - fn->fn_logpath, - LOGFILE_NAME_PREFIX, - LOGFILE_NAME_SUFFIX); -#endif - succp = true; - fn->fn_state = RUN; - CHK_FNAMES_CONF(fn); + fn->fn_logpath = strdup(dir); -return_conf_init: - if (!succp) + if (fn->fn_logpath) { - fnames_conf_done(fn); + succ = true; + fn->fn_state = RUN; + CHK_FNAMES_CONF(fn); } - ss_dassert(fn->fn_state == RUN || fn->fn_state == DONE); - return succp; + + return succ; } @@ -1792,7 +1462,7 @@ return_conf_init: * Parameters: * @param lm Log manager pointer * - * @return succp true if succeed, otherwise false. + * @return true if succeed, otherwise false. * * * @details If logfile is supposed to be located to shared memory @@ -1805,56 +1475,16 @@ return_conf_init: */ static bool logfiles_init(logmanager_t* lm) { - bool succp = true; - bool store_shmem; - bool write_syslog; + bool store_shmem = (lm->lm_target == LOG_TARGET_SHMEM); - /** Open syslog immediately. Print pid of loggind process. */ - if (syslog_id_str != NULL) - { - openlog(syslog_ident_str, LOG_PID | LOG_NDELAY, LOG_USER); - } - /** - * Initialize log file, pass softlink flag if necessary. - */ + bool succ = logfile_init(&lm->lm_logfile, lm, store_shmem); - /** - * Check if the file is stored in shared memory. If so, - * a symbolic link will be created to log directory. - */ - if (shmem_id_str != NULL && - strcasestr(shmem_id_str, STRLOGID(LOGFILE_ERROR)) != NULL) - { - store_shmem = true; - } - else - { - store_shmem = false; - } - /** - * Check if file is also written to syslog. - */ - if (syslog_id_str != NULL && - strcasestr(syslog_id_str, STRLOGID(LOGFILE_ERROR)) != NULL) - { - write_syslog = true; - } - else - { - write_syslog = false; - } - - succp = logfile_init(&lm->lm_logfile, - lm, - store_shmem, - write_syslog); - - if (!succp) + if (!succ) { fprintf(stderr, "*\n* Error : Initializing log files failed.\n"); } - return succp; + return succ; } static void logfile_flush(logfile_t* lf) @@ -1900,16 +1530,16 @@ static bool logfile_create(logfile_t* lf) bool nameconflicts; bool store_shmem; bool writable; - bool succp; + bool succ; strpart_t spart[3]; /*< string parts of which the file is composed of */ - if (use_stdout) + if (log_config.use_stdout) { // TODO: Refactor so that lf_full_file_name can be NULL in this case. lf->lf_full_file_name = strdup("stdout"); - succp = true; + succ = true; // TODO: Refactor to get rid of the gotos. - goto return_succp; + goto return_succ; } /** * sparts is an array but next pointers are used to walk through @@ -1976,8 +1606,8 @@ static bool logfile_create(logfile_t* lf) */ if (!writable) { - succp = false; - goto return_succp; + succ = false; + goto return_succ; } } @@ -1999,8 +1629,8 @@ static bool logfile_create(logfile_t* lf) */ if (!writable) { - succp = false; - goto return_succp; + succ = false; + goto return_succ; } } } @@ -2023,10 +1653,10 @@ static bool logfile_create(logfile_t* lf) } while (namecreatefail || nameconflicts); - succp = true; + succ = true; -return_succp: - return succp; +return_succ: + return succ; } /** @@ -2045,7 +1675,7 @@ static bool logfile_open_file(filewriter_t* fw, logfile_t* lf) { bool rv = true; - if (use_stdout) + if (log_config.use_stdout) { fw->fwr_file = skygw_file_alloc(lf->lf_full_file_name); fw->fwr_file->sf_file = stdout; @@ -2309,7 +1939,7 @@ static bool check_file_and_path(char* filename, bool* writable, bool do_log) static bool file_is_symlink(char* filename) { int rc; - bool succp = false; + bool succ = false; struct stat b; if (filename != NULL) @@ -2318,10 +1948,10 @@ static bool file_is_symlink(char* filename) if (rc != -1 && S_ISLNK(b.st_mode)) { - succp = true; + succ = true; } } - return succp; + return succ; } @@ -2334,16 +1964,14 @@ static bool file_is_symlink(char* filename) * @param logfile log file * @param logmanager log manager pointer * @param store_shmem flag to indicate whether log is physically written to shmem - * @param write_syslog flag to indicate whether log is also written to syslog * * @return true if succeed, false otherwise */ static bool logfile_init(logfile_t* logfile, logmanager_t* logmanager, - bool store_shmem, - bool write_syslog) + bool store_shmem) { - bool succp = false; + bool succ = false; fnames_conf_t* fn = &logmanager->lm_fnames_conf; logfile->lf_state = INIT; #if defined(SS_DEBUG) @@ -2360,7 +1988,6 @@ static bool logfile_init(logfile_t* logfile, logfile->lf_rotateflag = false; logfile->lf_spinlock = 0; logfile->lf_store_shmem = store_shmem; - logfile->lf_write_syslog = write_syslog; logfile->lf_buf_size = MAX_LOGSTRLEN; /** * If file is stored in shared memory in /dev/shm, a link @@ -2379,8 +2006,8 @@ static bool logfile_init(logfile_t* logfile, if (c == NULL) { - succp = false; - goto return_with_succp; + succ = false; + goto return_with_succ; } sprintf(c, "%smaxscale.%d", shm_pathname_prefix, pid); logfile->lf_filepath = c; @@ -2388,8 +2015,8 @@ static bool logfile_init(logfile_t* logfile, if (mkdir(c, S_IRWXU | S_IRWXG) != 0 && errno != EEXIST) { - succp = false; - goto return_with_succp; + succ = false; + goto return_with_succ; } logfile->lf_linkpath = strdup(fn->fn_logpath); logfile->lf_linkpath = add_slash(logfile->lf_linkpath); @@ -2400,9 +2027,9 @@ static bool logfile_init(logfile_t* logfile, } logfile->lf_filepath = add_slash(logfile->lf_filepath); - if (!(succp = logfile_create(logfile))) + if (!(succ = logfile_create(logfile))) { - goto return_with_succp; + goto return_with_succ; } /** * Create a block buffer list for log file. Clients' writes go to buffers @@ -2418,35 +2045,35 @@ static bool logfile_init(logfile_t* logfile, "*\n* Error : Initializing buffers for log files " "failed."); logfile_free_memory(logfile); - goto return_with_succp; + goto return_with_succ; } #if defined(SS_DEBUG) - if (store_shmem && !use_stdout) + if (store_shmem && !log_config.use_stdout) { fprintf(stderr, "%s\t: %s->%s\n", STRLOGNAME(LOGFILE_ERROR), logfile->lf_full_link_name, logfile->lf_full_file_name); } - else if (!use_stdout) + else if (!log_config.use_stdout) { fprintf(stderr, "%s\t: %s\n", STRLOGNAME(LOGFILE_ERROR), logfile->lf_full_file_name); } #endif - succp = true; + succ = true; logfile->lf_state = RUN; CHK_LOGFILE(logfile); -return_with_succp: - if (!succp) +return_with_succ: + if (!succ) { logfile_done(logfile); } ss_dassert(logfile->lf_state == RUN || logfile->lf_state == DONE); - return succp; + return succ; } /** @@ -2503,23 +2130,19 @@ static void logfile_free_memory(logfile_t* lf) /** * @node Initialize filewriter data and open the log file for each log file type. * - * @param logmanager Log manager struct - * @param fw File writer struct - * @param clientmes Messaging from file writer to log manager - * @param logmes Messaging from loggers to file writer thread + * @param logmanager Log manager struct + * @param fw File writer struct * * @return true if succeed, false if failed * */ -static bool filewriter_init(logmanager_t* logmanager, - filewriter_t* fw, - skygw_message_t* clientmes, - skygw_message_t* logmes) +static bool filewriter_init(logmanager_t* logmanager, filewriter_t* fw) { - bool succp = false; - logfile_t* lf; + bool succ = false; CHK_LOGMANAGER(logmanager); + assert(logmanager->lm_clientmes); + assert(logmanager->lm_logmes); fw->fwr_state = INIT; #if defined(SS_DEBUG) @@ -2528,36 +2151,29 @@ static bool filewriter_init(logmanager_t* logmanager, #endif fw->fwr_logmgr = logmanager; /** Message from filewriter to clients */ - fw->fwr_logmes = logmes; + fw->fwr_logmes = logmanager->lm_logmes; /** Message from clients to filewriter */ - fw->fwr_clientmes = clientmes; + fw->fwr_clientmes = logmanager->lm_clientmes; - if (fw->fwr_logmes == NULL || fw->fwr_clientmes == NULL) + logfile_t* lf = logmanager_get_logfile(logmanager); + + if (logfile_open_file(fw, lf)) { - goto return_succp; + fw->fwr_state = RUN; + CHK_FILEWRITER(fw); + succ = true; } - - lf = logmanager_get_logfile(logmanager); - - if (!(succp = logfile_open_file(fw, lf))) + else { fprintf(stderr, "Error : opening log file %s failed. Exiting " "MaxScale\n", lf->lf_full_file_name); - goto return_succp; - } - fw->fwr_state = RUN; - CHK_FILEWRITER(fw); - succp = true; - -return_succp: - if (!succp) - { filewriter_done(fw); } + ss_dassert(fw->fwr_state == RUN || fw->fwr_state == DONE); - return succp; + return succ; } static void filewriter_done(filewriter_t* fw) @@ -2569,7 +2185,7 @@ static void filewriter_done(filewriter_t* fw) case INIT: fw->fwr_logmes = NULL; fw->fwr_clientmes = NULL; - if (use_stdout) + if (log_config.use_stdout) { skygw_file_free(fw->fwr_file); } @@ -2609,19 +2225,19 @@ static bool thr_flush_file(logmanager_t *lm, filewriter_t *fwr) */ if (rotate_logfile) { - bool succp; + bool succ; lf->lf_name_seqno += 1; /*< new sequence number */ - if (!(succp = logfile_create(lf))) + if (!(succ = logfile_create(lf))) { lf->lf_name_seqno -= 1; /*< restore */ } - else if ((succp = logfile_open_file(fwr, lf))) + else if ((succ = logfile_open_file(fwr, lf))) { - if (use_stdout) + if (log_config.use_stdout) { - skygw_file_free (file); + skygw_file_free(file); } else { @@ -2629,7 +2245,7 @@ static bool thr_flush_file(logmanager_t *lm, filewriter_t *fwr) } } - if (!succp) + if (!succ) { LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, @@ -2947,51 +2563,33 @@ void flushall_logfiles(bool flush) } /** - * Flush all log files synchronously + * Enable/disable syslog logging. + * + * @param enabled True, if high precision logging should be enabled, false if it should be disabled. */ -void skygw_log_sync_all(void) +void mxs_log_set_highprecision_enabled(bool enabled) { - if (!use_stdout) - { - skygw_log_write(LOGFILE_TRACE,"Starting log flushing to disk."); - } - - /** If initialization of the log manager has not been done, lm pointer can be - * NULL. */ - if (lm) - { - flushall_logfiles(true); - skygw_message_send(lm->lm_logmes); - skygw_message_wait(lm->lm_clientmes); - } + log_config.do_highprecision = enabled; } /** - * Toggle high precision logging - * @param val 0 for disabled, 1 for enabled + * Enable/disable syslog logging. + * + * @param enabled True, if syslog logging should be enabled, false if it should be disabled. */ -void skygw_set_highp(int val) +void mxs_log_set_syslog_enabled(bool enabled) { - highprec = val; -} - - -/** - * Toggle syslog logging - * @param val 0 for disabled, 1 for enabled - */ -void logmanager_enable_syslog(int val) -{ - do_syslog = val; + log_config.do_syslog = enabled; } /** - * Toggle syslog logging - * @param val 0 for disabled, 1 for enabled + * Enable/disable maxscale log logging. + * + * @param enabled True, if syslog logging should be enabled, false if it should be disabled. */ -void logmanager_enable_maxscalelog(int val) +void mxs_log_set_maxscalelog_enabled(bool enabled) { - do_maxscalelog = val; + log_config.do_maxscalelog = enabled; } @@ -3006,7 +2604,63 @@ void logmanager_enable_maxscalelog(int val) */ int mxs_log_flush() { - return skygw_log_flush(LOGFILE_ERROR); + int err = -1; + + if (logmanager_register(false)) + { + CHK_LOGMANAGER(lm); + + logfile_t *lf = logmanager_get_logfile(lm); + CHK_LOGFILE(lf); + + logfile_flush(lf); + err = 0; + + logmanager_unregister(); + } + else + { + ss_dfprintf(stderr, "Can't register to logmanager, flushing failed.\n"); + } + + return err; +} + +/** + * Explicitly ensure that all pending log messages are flushed. + * + * @return 0 if the flushing was successfully performed, otherwise -1. + * + * When the function returns 0, the flushing has been initiated and the + * flushing thread has indicated that the operation has been performed. + * However, 0 will be returned also in the case that the flushing thread + * for, whatever reason, failed to actually flush the log. + */ +int mxs_log_flush_sync(void) +{ + int err = 0; + + if (!log_config.use_stdout) + { + MXS_INFO("Starting log flushing to disk."); + } + + /** If initialization of the log manager has not been done, lm pointer can be + * NULL. */ + // TODO: Why is logmanager_register() not called here? + if (lm) + { + flushall_logfiles(true); + err = skygw_message_send(lm->lm_logmes); + + if (!err) + { + // TODO: Add error handling to skygw_message_wait. Now void. + skygw_message_wait(lm->lm_clientmes); + } + } + + return err; } /** @@ -3021,7 +2675,28 @@ int mxs_log_flush() */ int mxs_log_rotate() { - return skygw_log_rotate(LOGFILE_ERROR); + int err = -1; + + if (logmanager_register(false)) + { + CHK_LOGMANAGER(lm); + + logfile_t *lf = logmanager_get_logfile(lm); + CHK_LOGFILE(lf); + + MXS_NOTICE("Log rotation is called for %s.", lf->lf_full_file_name); + + logfile_rotate(lf); + err = 0; + + logmanager_unregister(); + } + else + { + ss_dfprintf(stderr, "Can't register to logmanager, rotating failed.\n"); + } + + return err; } static bool convert_priority_to_file(int priority, logfile_id_t* idp, const char** textp) @@ -3065,12 +2740,14 @@ static bool convert_priority_to_file(int priority, logfile_id_t* idp, const char } /** - * Enable a particular syslog priority. + * Enable/disable a particular syslog priority. * * @param priority One of the LOG_ERR etc. constants from sys/syslog.h. + * @param enabled True if the priority should be enabled, false if it to be disabled. + * * @return 0 if the priority was valid, -1 otherwise. */ -int mxs_log_enable_priority(int priority) +int mxs_log_set_priority_enabled(int priority, bool enabled) { int rv = -1; logfile_id_t id; @@ -3080,12 +2757,18 @@ int mxs_log_enable_priority(int priority) { if (!text) { - rv = skygw_log_enable(id); + if (enabled) + { + rv = skygw_log_enable(id); + } + else + { + rv = skygw_log_disable(id); + } } else { - // TODO: Change to warning when available. - MXS_DEBUG("Attempt to enable syslog priority %s, which is not available yet.", text); + MXS_WARNING("Attempt to enable syslog priority %s, which is not available yet.", text); rv = 0; } } @@ -3097,38 +2780,235 @@ int mxs_log_enable_priority(int priority) return rv; } +typedef struct log_prefix +{ + const char* text; // The prefix, e.g. "error: " + int len; // The length of the prefix without the trailing NULL. +} log_prefix_t; + +static const char PREFIX_EMERG[] = "emerg : "; +static const char PREFIX_ALERT[] = "alert : "; +static const char PREFIX_CRIT[] = "crit : "; +static const char PREFIX_ERROR[] = "error : "; +static const char PREFIX_WARNING[] = "warning: "; +static const char PREFIX_NOTICE[] = "notice : "; +static const char PREFIX_INFO[] = "info : "; +static const char PREFIX_DEBUG[] = "debug : "; + +static logfile_id_t priority_to_id(int priority) +{ + assert((priority & ~LOG_PRIMASK) == 0); + + switch (priority) + { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + case LOG_WARNING: + return LOGFILE_ERROR; + + case LOG_NOTICE: + return LOGFILE_MESSAGE; + + case LOG_INFO: + return LOGFILE_TRACE; + + case LOG_DEBUG: + return LOGFILE_DEBUG; + + default: + // Can't happen! + assert(!true); + return LOGFILE_ERROR; + } +} + +static log_prefix_t priority_to_prefix(int priority) +{ + assert((priority & ~LOG_PRIMASK) == 0); + + log_prefix_t prefix; + + switch (priority) + { + case LOG_EMERG: + prefix.text = PREFIX_EMERG; + prefix.len = sizeof(PREFIX_EMERG); + break; + + case LOG_ALERT: + prefix.text = PREFIX_ALERT; + prefix.len = sizeof(PREFIX_ALERT); + break; + + case LOG_CRIT: + prefix.text = PREFIX_CRIT; + prefix.len = sizeof(PREFIX_CRIT); + break; + + case LOG_ERR: + prefix.text = PREFIX_ERROR; + prefix.len = sizeof(PREFIX_ERROR); + break; + + case LOG_WARNING: + prefix.text = PREFIX_WARNING; + prefix.len = sizeof(PREFIX_WARNING); + break; + + case LOG_NOTICE: + prefix.text = PREFIX_NOTICE; + prefix.len = sizeof(PREFIX_NOTICE); + break; + + case LOG_INFO: + prefix.text = PREFIX_INFO; + prefix.len = sizeof(PREFIX_INFO); + break; + + case LOG_DEBUG: + prefix.text = PREFIX_DEBUG; + prefix.len = sizeof(PREFIX_DEBUG); + break; + + default: + assert(!true); + prefix.text = PREFIX_ERROR; + prefix.len = sizeof(PREFIX_ERROR); + break; + } + + --prefix.len; // Remove trailing NULL. + + return prefix; +} + +static enum log_flush priority_to_flush(int priority) +{ + assert((priority & ~LOG_PRIMASK) == 0); + + switch (priority) + { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + return LOG_FLUSH_YES; + + default: + assert(!true); + case LOG_WARNING: + case LOG_NOTICE: + case LOG_INFO: + case LOG_DEBUG: + return LOG_FLUSH_NO; + } +} + /** - * Disable a particular syslog priority. + * Log a message of a particular priority. * - * @param priority One of the LOG_ERR etc. constants from sys/syslog.h. - * - * Note that there is no hierarchy. That is, disabling a priority of - * high importance, such as LOG_ERR, does not automatically disable - * all lower prioritys. + * @param priority One of the syslog constants: LOG_ERR, LOG_WARNING, ... + * @param file The name of the file where the message was logged. + * @param line The line where the message was logged. + * @param function The function where the message was logged. + * @param format The printf format of the following arguments. + * @param ... Optional arguments according to the format. */ -int mxs_log_disable_priority(int priority) +int mxs_log_message(int priority, + const char* file, int line, const char* function, + const char* format, ...) { - int rv = -1; - logfile_id_t id; - const char* text; + int err = 0; - if (convert_priority_to_file(priority, &id, &text)) + assert((priority & ~LOG_PRIMASK) == 0); + + if ((priority & ~LOG_PRIMASK) == 0) // Check that the priority is ok, { - if (!text) + logfile_id_t id = priority_to_id(priority); + + if (LOG_IS_ENABLED(id)) { - rv = skygw_log_disable(id); - } - else - { - // TODO: Change to warning when available. - MXS_DEBUG("Attempt to enable syslog priority %s, which is not available.", text); - rv = 0; + va_list valist; + + /** + * Find out the length of log string (to be formatted str). + */ + va_start(valist, format); + int message_len = vsnprintf(NULL, 0, format, valist); + va_end(valist); + + if (message_len >= 0) + { + log_prefix_t prefix = priority_to_prefix(priority); + + static const char FORMAT_FUNCTION[] = "(%s): "; + + int augmentation = log_config.augmentation; // Other thread might change log_config.augmentation. + int augmentation_len = 0; + + switch (augmentation) + { + case LOG_AUGMENT_WITH_FUNCTION: + augmentation_len = sizeof(FORMAT_FUNCTION) - 1; // Remove trailing 0 + augmentation_len -= 2; // Remove the %s + augmentation_len += strlen(function); + break; + + default: + break; + } + + int buffer_len = prefix.len + augmentation_len + message_len + 1; // Trailing NULL + + if (buffer_len > MAX_LOGSTRLEN) + { + message_len -= (buffer_len - MAX_LOGSTRLEN); + buffer_len = MAX_LOGSTRLEN; + + assert(prefix.len + augmentation_len + message_len + 1 == buffer_len); + } + + char buffer[buffer_len]; + + char *prefix_text = buffer; + char *augmentation_text = buffer + prefix.len; + char *message_text = buffer + prefix.len + augmentation_len; + + strcpy(prefix_text, prefix.text); + + if (augmentation_len) + { + int len = 0; + + switch (augmentation) + { + case LOG_AUGMENT_WITH_FUNCTION: + len = sprintf(augmentation_text, FORMAT_FUNCTION, function); + break; + + default: + assert(!true); + } + + assert(len == augmentation_len); + } + + va_start(valist, format); + vsnprintf(message_text, message_len + 1, format, valist); + va_end(valist); + + enum log_flush flush = priority_to_flush(priority); + + err = log_write(priority, file, line, function, prefix.len, buffer_len, buffer, flush); + } } } else { - MXS_ERROR("Attempt to enable unknown syslog priority: %d", priority); + MXS_WARNING("Invalid syslog priority: %d", priority); } - return rv; + return err; } diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index 21023871b..fa4c79903 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -18,6 +18,8 @@ #if !defined(LOG_MANAGER_H) # define LOG_MANAGER_H +#include + /* * We need a common.h file that is included by every component. */ @@ -42,7 +44,6 @@ typedef enum LOGFILE_LAST = LOGFILE_DEBUG } logfile_id_t; - typedef enum { FILEWRITER_INIT, @@ -50,6 +51,13 @@ typedef enum FILEWRITER_DONE } filewriter_state_t; +typedef enum +{ + LOG_TARGET_DEFAULT = 0, + LOG_TARGET_FS = 1, // File system + LOG_TARGET_SHMEM = 2, // Shared memory +} log_target_t; + /** * Thread-specific logging information. */ @@ -124,78 +132,53 @@ typedef enum LOG_AUGMENTATION_MASK = (LOG_AUGMENT_WITH_FUNCTION) } log_augmentation_t; -/** - * LOG_FLUSH_NO Do not flush after writing. - * LOG_FLUSH_YES Flush after writing. - */ -enum log_flush -{ - LOG_FLUSH_NO = 0, - LOG_FLUSH_YES = 1 -}; - EXTERN_C_BLOCK_BEGIN extern int lm_enabled_logfiles_bitmask; extern ssize_t log_ses_count[]; extern __thread log_info_t tls_log_info; +bool mxs_log_init(const char* ident, const char* logdir, log_target_t target); +void mxs_log_finish(void); + int mxs_log_flush(); +int mxs_log_flush_sync(); int mxs_log_rotate(); -int mxs_log_enable_priority(int priority); -int mxs_log_disable_priority(int priority); -bool skygw_logmanager_init(const char* logdir, int argc, char* argv[]); -void skygw_logmanager_done(void); -void skygw_logmanager_exit(void); +int mxs_log_set_priority_enabled(int priority, bool enabled); +void mxs_log_set_syslog_enabled(bool enabled); +void mxs_log_set_maxscalelog_enabled(bool enabled); +void mxs_log_set_highprecision_enabled(bool enabled); +void mxs_log_set_augmentation(int bits); + +int mxs_log_message(int priority, + const char* file, int line, const char* function, + const char* format, ...); -/** - * free private write buffer list - */ -void skygw_log_done(void); -int skygw_log_write_context(logfile_id_t id, - enum log_flush flush, - const char* file, int line, const char* function, - const char* format, ...); -int skygw_log_flush(logfile_id_t id); -void skygw_log_sync_all(void); -int skygw_log_rotate(logfile_id_t id); int skygw_log_enable(logfile_id_t id); int skygw_log_disable(logfile_id_t id); -void skygw_log_sync_all(void); -void skygw_set_highp(int); -void logmanager_enable_syslog(int); -void logmanager_enable_maxscalelog(int); + +inline int mxs_log_id_to_priority(logfile_id_t id) +{ + if (id & LOGFILE_ERROR) return LOG_ERR; + if (id & LOGFILE_MESSAGE) return LOG_NOTICE; + if (id & LOGFILE_TRACE) return LOG_INFO; + if (id & LOGFILE_DEBUG) return LOG_DEBUG; + return LOG_ERR; +} #define skygw_log_write(id, format, ...)\ - skygw_log_write_context(id, LOG_FLUSH_NO, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) + mxs_log_message(mxs_log_id_to_priority(id), __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) -#define skygw_log_write_flush(id, format, ...)\ - skygw_log_write_context(id, LOG_FLUSH_YES, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) - -/** - * What augmentation if any should a logged message be augmented with. - * - * Currently this is a global setting and affects all loggers. - */ -void skygw_log_set_augmentation(int bits); -int skygw_log_get_augmentation(); +#define skygw_log_write_flush(id, format, ...) skygw_log_write(id, format, ##__VA_ARGS__) EXTERN_C_BLOCK_END -const char* get_logpath_default(void); - /** * Helper, not to be called directly. */ -#define MXS_MESSAGE_FLUSH(id, format, ...)\ - do { if (LOG_IS_ENABLED(id)) { skygw_log_write_flush(id, format, ##__VA_ARGS__); } } while (false) - -/** - * Helper, not to be called directly. - */ -#define MXS_MESSAGE(id, format, ...)\ - do { if (LOG_IS_ENABLED(id)) { skygw_log_write(id, format, ##__VA_ARGS__); } } while (false) +#define MXS_LOG_MESSAGE(priority, format, ...)\ + mxs_log_message(priority, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) /** * Log an error, warning, notice, info, or debug message. @@ -203,10 +186,10 @@ const char* get_logpath_default(void); * @param format The printf format of the message. * @param ... Arguments, depending on the format. */ -#define MXS_ERROR(format, ...) MXS_MESSAGE_FLUSH(LOGFILE_ERROR, format, ##__VA_ARGS__) -#define MXS_WARNING(format, ...) MXS_MESSAGE(LOGFILE_ERROR, format, ##__VA_ARGS__) -#define MXS_NOTICE(format, ...) MXS_MESSAGE(LOGFILE_MESSAGE, format, ##__VA_ARGS__) -#define MXS_INFO(format, ...) MXS_MESSAGE(LOGFILE_TRACE, format, ##__VA_ARGS__) -#define MXS_DEBUG(format, ...) MXS_MESSAGE(LOGFILE_DEBUG, format, ##__VA_ARGS__) +#define MXS_ERROR(format, ...) MXS_LOG_MESSAGE(LOG_ERR, format, ##__VA_ARGS__) +#define MXS_WARNING(format, ...) MXS_LOG_MESSAGE(LOG_WARNING, format, ##__VA_ARGS__) +#define MXS_NOTICE(format, ...) MXS_LOG_MESSAGE(LOG_NOTICE, format, ##__VA_ARGS__) +#define MXS_INFO(format, ...) MXS_LOG_MESSAGE(LOG_INFO, format, ##__VA_ARGS__) +#define MXS_DEBUG(format, ...) MXS_LOG_MESSAGE(LOG_DEBUG, format, ##__VA_ARGS__) #endif /** LOG_MANAGER_H */ diff --git a/log_manager/test/CMakeLists.txt b/log_manager/test/CMakeLists.txt index 6018e746d..e485e747e 100644 --- a/log_manager/test/CMakeLists.txt +++ b/log_manager/test/CMakeLists.txt @@ -3,3 +3,4 @@ add_executable(testorder testorder.c ../../server/core/random_jkiss.c) target_link_libraries(testlog pthread log_manager utils fullcore) target_link_libraries(testorder pthread log_manager utils fullcore) add_test(NAME Internal-TestLogOrder COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/logorder.sh 200 0 1000 ${CMAKE_CURRENT_BINARY_DIR}/logorder.log) +add_test(Internal-TestLog testlog) diff --git a/log_manager/test/testlog.c b/log_manager/test/testlog.c index 886a66c09..ddf1c410a 100644 --- a/log_manager/test/testlog.c +++ b/log_manager/test/testlog.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,15 @@ static void* thr_run_morelog(void* data); #define TEST3 #define TEST4 +const char USAGE[]= + "usage: %s [-t <#threads>]\n" + "\n" + "-t: Number of threads. Default is %d.\n"; +const int N_THR = 4; + +#define TEST_ERROR(msg)\ + do { fprintf(stderr, "[%s:%d]: %s\n", basename(__FILE__), __LINE__, msg); } while (false) + int main(int argc, char* argv[]) { int err = 0; @@ -66,30 +76,34 @@ int main(int argc, char* argv[]) time_t t; struct tm tm; char c; - int nthr = 0; - int log_argc = 0; - char** log_argv = NULL; + int nthr = N_THR; while ((c = getopt(argc, argv, "t:")) != -1) { switch (c) { case 't': nthr = atoi(optarg); + if (nthr <= 0) + { + err = 1; + } break; default: + err = 1; break; } } - if (nthr <= 0) + if (err != 0) { - fprintf(stderr, "Thread count argument is zero or " - "negative. Exiting.\n"); + fprintf(stderr, USAGE, argv[0], N_THR); err = 1; goto return_err; } + printf("Using %d threads.\n", nthr); + thr = (thread_t **)calloc(1, nthr*sizeof(thread_t*)); if (thr == NULL) @@ -99,14 +113,14 @@ int main(int argc, char* argv[]) err = 1; goto return_err; } - i = atexit(skygw_logmanager_exit); + i = atexit(mxs_log_finish); if (i != 0) { fprintf(stderr, "Couldn't register exit function.\n"); } - succp = skygw_logmanager_init("/tmp", log_argc, log_argv); + succp = mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); if (!succp) { @@ -125,7 +139,7 @@ int main(int argc, char* argv[]) tm.tm_min, tm.tm_sec); - skygw_logmanager_init("/tmp", log_argc, log_argv); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("First write with flush."); err = skygw_log_write_flush(LOGFILE_ERROR, logstr); @@ -138,14 +152,14 @@ int main(int argc, char* argv[]) logstr = ("Fourth write, no flush. Next flush only."); err = skygw_log_write(LOGFILE_ERROR, logstr); - err = skygw_log_flush(LOGFILE_ERROR); + err = mxs_log_flush(); logstr = "My name is %s %d years and %d months."; #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); #endif err = skygw_log_write(LOGFILE_TRACE, logstr, "TraceyTracey", 3, 7); - skygw_log_flush(LOGFILE_TRACE); + mxs_log_flush(); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); #endif @@ -156,7 +170,7 @@ int main(int argc, char* argv[]) #endif logstr = "My name is Stacey %s"; err = skygw_log_write_flush(LOGFILE_TRACE, logstr, " "); - skygw_logmanager_done(); + mxs_log_finish(); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); #endif @@ -173,7 +187,7 @@ int main(int argc, char* argv[]) logstr = "Ph%dlip."; err = skygw_log_write(LOGFILE_TRACE, logstr, 1); - skygw_logmanager_init("/tmp", log_argc, log_argv); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("A terrible error has occurred!"); err = skygw_log_write_flush(LOGFILE_ERROR, logstr); @@ -195,7 +209,7 @@ int main(int argc, char* argv[]) "with us. Just me and my mom - and you, of course. Then, if you wish, we could " "listen to the radio and keep company for our little Steven, my mom's cat, you know."); err = skygw_log_write(LOGFILE_MESSAGE, logstr); - skygw_logmanager_done(); + mxs_log_finish(); #if defined(TEST1) mes = skygw_message_init(); @@ -238,7 +252,7 @@ int main(int argc, char* argv[]) pthread_join(thr[i]->tid, NULL); } /** This is to release memory */ - skygw_logmanager_done(); + mxs_log_finish(); simple_mutex_unlock(mtx); @@ -295,7 +309,7 @@ int main(int argc, char* argv[]) pthread_join(thr[i]->tid, NULL); } /** This is to release memory */ - skygw_logmanager_done(); + mxs_log_finish(); simple_mutex_unlock(mtx); @@ -319,20 +333,20 @@ int main(int argc, char* argv[]) #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); #endif + succp = mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + ss_dassert(succp); + logstr = ("\tTEST 3 - test enabling and disabling logs."); err = skygw_log_write(LOGFILE_ERROR, logstr); ss_dassert(err == 0); - succp = skygw_logmanager_init("/tmp", log_argc, log_argv); - ss_dassert(succp); - skygw_log_disable(LOGFILE_TRACE); logstr = ("1.\tWrite once to ERROR and twice to MESSAGE log."); err = skygw_log_write(LOGFILE_MESSAGE, logstr); ss_dassert(err == 0); err = skygw_log_write(LOGFILE_TRACE, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); err = skygw_log_write(LOGFILE_ERROR, logstr); ss_dassert(err == 0); @@ -355,18 +369,18 @@ int main(int argc, char* argv[]) err = skygw_log_write(LOGFILE_TRACE, logstr); ss_dassert(err == 0); err = skygw_log_write(LOGFILE_ERROR, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); skygw_log_disable(LOGFILE_MESSAGE); skygw_log_disable(LOGFILE_TRACE); logstr = ("4.\tWrite to none."); err = skygw_log_write(LOGFILE_MESSAGE, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); err = skygw_log_write(LOGFILE_TRACE, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); err = skygw_log_write(LOGFILE_ERROR, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); skygw_log_enable(LOGFILE_ERROR); skygw_log_enable(LOGFILE_MESSAGE); @@ -375,16 +389,16 @@ int main(int argc, char* argv[]) err = skygw_log_write(LOGFILE_MESSAGE, logstr); ss_dassert(err == 0); err = skygw_log_write(LOGFILE_TRACE, logstr); - ss_dassert(err != 0); /**< Must fail */ + ss_dassert(err == 0); err = skygw_log_write(LOGFILE_ERROR, logstr); ss_dassert(err == 0); - skygw_logmanager_done(); + mxs_log_finish(); #endif /* TEST 3 */ #if defined(TEST4) - succp = skygw_logmanager_init("/tmp", log_argc, log_argv); + succp = mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); ss_dassert(succp); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); @@ -401,6 +415,7 @@ int main(int argc, char* argv[]) err = skygw_log_write(LOGFILE_MESSAGE, logstr); ss_dassert(err == 0); + skygw_log_enable(LOGFILE_TRACE); logstr = ("3.\tWrite to TRACE log only."); err = skygw_log_write(LOGFILE_TRACE, logstr); ss_dassert(err == 0); @@ -415,11 +430,11 @@ int main(int argc, char* argv[]) logstr = ("5.\tThis should not appear anywhere since MESSAGE " "is disabled."); err = skygw_log_write(LOGFILE_MESSAGE, logstr); - ss_dassert(err != 0); + ss_dassert(err == 0); - skygw_logmanager_done(); + mxs_log_finish(); - succp = skygw_logmanager_init("/tmp", log_argc, log_argv); + succp = mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); ss_dassert(succp); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); @@ -447,7 +462,7 @@ int main(int argc, char* argv[]) logstr = ("10.\tThis should not appear anywhere since MESSAGE is " "disabled."); err = skygw_log_write_flush(LOGFILE_MESSAGE, logstr); - ss_dassert(err != 0); + ss_dassert(err == 0); skygw_log_enable(LOGFILE_MESSAGE); @@ -473,7 +488,7 @@ int main(int argc, char* argv[]) ss_dassert(err == 0); - skygw_logmanager_done(); + mxs_log_finish(); #endif /* TEST 4 */ fprintf(stderr, ".. done.\n"); @@ -492,20 +507,18 @@ static void* thr_run(void* data) char* logstr; int err; - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_done(); - skygw_log_flush(LOGFILE_MESSAGE); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + mxs_log_flush(); logstr = ("Hi, how are you?"); err = skygw_log_write(LOGFILE_MESSAGE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_done(); - skygw_log_flush(LOGFILE_TRACE); - skygw_log_flush(LOGFILE_MESSAGE); + mxs_log_finish(); + mxs_log_flush(); logstr = ("I was wondering, you know, it has been such a lovely weather whole morning and " "I thought that would you like to come to my place and have a little piece of " "cheese with us. Just me and my mom - and you, of course. Then, if you wish, " @@ -513,32 +526,34 @@ static void* thr_run(void* data) "cat, you know."); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); err = skygw_log_write(LOGFILE_MESSAGE, logstr); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("Testing. One, two, three\n"); err = skygw_log_write(LOGFILE_ERROR, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_log_flush(LOGFILE_ERROR); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + mxs_log_flush(); logstr = ("For automatic and register variables, it is done each time the function or block is entered."); +#if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); +#endif err = skygw_log_write(LOGFILE_TRACE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_done(); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("Rather more surprising, at least at first sight, is the fact that a reference " "to a[i] can also be written as *(a+i). In evaluating a[i], C converts it to *(a+i) " "immediately; the two forms are equivalent. Applying the operatos & to both parts " @@ -547,23 +562,23 @@ static void* thr_run(void* data) err = skygw_log_write(LOGFILE_ERROR, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_done(); - skygw_log_flush(LOGFILE_ERROR); - skygw_logmanager_done(); - skygw_logmanager_done(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + mxs_log_finish(); + mxs_log_flush(); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("..and you?"); err = skygw_log_write(LOGFILE_MESSAGE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("For automatic and register variables, it is done each time the function or block is entered."); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); @@ -571,10 +586,10 @@ static void* thr_run(void* data) err = skygw_log_write(LOGFILE_TRACE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("Rather more surprising, at least at first sight, is the fact that a reference to " "a[i] can also be written as *(a+i). In evaluating a[i], C converts it to *(a+i) " "immediately; the two forms are equivalent. Applying the operatos & to both parts " @@ -583,22 +598,23 @@ static void* thr_run(void* data) err = skygw_log_write(LOGFILE_ERROR, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("..... and you too?"); err = skygw_log_write(LOGFILE_MESSAGE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_done(); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); #endif - skygw_log_flush(LOGFILE_TRACE); + mxs_log_flush(); logstr = ("For automatic and register variables, it is done each time the function or block is entered."); #if !defined(SS_DEBUG) skygw_log_enable(LOGFILE_TRACE); @@ -606,28 +622,30 @@ static void* thr_run(void* data) err = skygw_log_write(LOGFILE_TRACE, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_done(); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("Testing. One, two, three, four\n"); err = skygw_log_write(LOGFILE_ERROR, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); logstr = ("Testing. One, two, three, .. where was I?\n"); err = skygw_log_write(LOGFILE_ERROR, logstr); if (err != 0) { - fprintf(stderr,"Error, log write failed.\n"); + TEST_ERROR("Error, log write failed."); } ss_dassert(err == 0); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_init("/tmp", 0, NULL); - skygw_logmanager_done(); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + mxs_log_finish(); simple_mutex_lock(td->mtx, true); *td->nactive -= 1; simple_mutex_unlock(td->mtx); diff --git a/log_manager/test/testorder.c b/log_manager/test/testorder.c index edb51fec3..06494cfdd 100644 --- a/log_manager/test/testorder.c +++ b/log_manager/test/testorder.c @@ -31,7 +31,6 @@ int main(int argc, char** argv) char cwd[1024]; char tmp[2048]; char *message; - char** optstr; long msg_index = 1; struct timespec ts1; ts1.tv_sec = 0; @@ -55,7 +54,6 @@ int main(int argc, char** argv) } if (getcwd(cwd, sizeof(cwd)) == NULL || - (optstr = (char**)malloc(sizeof(char*) * 4)) == NULL || (message = (char*)malloc(sizeof(char) * block_size)) == NULL) { fprintf(stderr,"Fatal Error, exiting..."); @@ -65,13 +63,11 @@ int main(int argc, char** argv) memset(tmp, 0, 1024); sprintf(tmp, "%s", cwd); - optstr[0] = strdup("log_manager"); - optstr[1] = NULL; iterations = atoi(argv[1]); interval = atoi(argv[2]); - succp = skygw_logmanager_init(tmp, 1, optstr); + succp = mxs_log_init(NULL, tmp, LOG_TARGET_FS); if (!succp) { @@ -111,13 +107,8 @@ int main(int argc, char** argv) nanosleep(&ts1, NULL); } - skygw_log_flush(LOGFILE_ERROR); - skygw_logmanager_done(); + mxs_log_flush(); + mxs_log_finish(); free(message); - free(optstr[0]); - free(optstr[1]); - free(optstr[2]); - free(optstr[3]); - free(optstr); return 0; } diff --git a/server/core/config.c b/server/core/config.c index 8e152cd41..8011e7ea7 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -22,27 +22,27 @@ * @verbatim * Revision History * - * Date Who Description - * 21/06/13 Mark Riddoch Initial implementation - * 08/07/13 Mark Riddoch Addition on monitor module support - * 23/07/13 Mark Riddoch Addition on default monitor password - * 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services - * 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list - * 11/03/14 Massimiliano Pinto Added Unix socket support - * 11/05/14 Massimiliano Pinto Added version_string support to service - * 19/05/14 Mark Riddoch Added unique names from section headers - * 29/05/14 Mark Riddoch Addition of filter definition - * 23/05/14 Massimiliano Pinto Added automatic set of maxscale-id: first listening ipv4_raw + port + pid - * 28/05/14 Massimiliano Pinto Added detect_replication_lag parameter - * 28/08/14 Massimiliano Pinto Added detect_stale_master parameter - * 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter - * 12/09/14 Mark Riddoch Addition of checks on servers list and - * internal router suppression of messages - * 30/10/14 Massimiliano Pinto Added disable_master_failback parameter - * 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write - * 20/02/15 Markus Mäkelä Added connection_timeout parameter for services - * 05/03/15 Massimiliano Pinto Added notification_feedback support - * 20/04/15 Guillaume Lefranc Added available_when_donor parameter + * Date Who Description + * 21/06/13 Mark Riddoch Initial implementation + * 08/07/13 Mark Riddoch Addition on monitor module support + * 23/07/13 Mark Riddoch Addition on default monitor password + * 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services + * 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list + * 11/03/14 Massimiliano Pinto Added Unix socket support + * 11/05/14 Massimiliano Pinto Added version_string support to service + * 19/05/14 Mark Riddoch Added unique names from section headers + * 29/05/14 Mark Riddoch Addition of filter definition + * 23/05/14 Massimiliano Pinto Added automatic set of maxscale-id: first listening ipv4_raw + port + pid + * 28/05/14 Massimiliano Pinto Added detect_replication_lag parameter + * 28/08/14 Massimiliano Pinto Added detect_stale_master parameter + * 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter + * 12/09/14 Mark Riddoch Addition of checks on servers list and + * internal router suppression of messages + * 30/10/14 Massimiliano Pinto Added disable_master_failback parameter + * 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write + * 20/02/15 Markus Mäkelä Added connection_timeout parameter for services + * 05/03/15 Massimiliano Pinto Added notification_feedback support + * 20/04/15 Guillaume Lefranc Added available_when_donor parameter * 22/04/15 Martin Brampton Added disable_master_role_setting parameter * * @endverbatim @@ -83,50 +83,54 @@ #define MAXSCALE_PCRE_BUFSZ 24 extern int setipaddress(struct in_addr *, char *); -static int process_config_context(CONFIG_CONTEXT *); -static int process_config_update(CONFIG_CONTEXT *); -static void free_config_context(CONFIG_CONTEXT *); -static char *config_get_value(CONFIG_PARAMETER *, const char *); -static const char *config_get_value_string(CONFIG_PARAMETER *, const char *); -static int handle_global_item(const char *, const char *); -static int handle_feedback_item(const char *, const char *); -static void global_defaults(); -static void feedback_defaults(); -static void check_config_objects(CONFIG_CONTEXT *context); +static int process_config_context(CONFIG_CONTEXT *); +static int process_config_update(CONFIG_CONTEXT *); +static void free_config_context(CONFIG_CONTEXT *); +static char *config_get_value(CONFIG_PARAMETER *, const char *); +static const char *config_get_value_string(CONFIG_PARAMETER *, const char *); +static int handle_global_item(const char *, const char *); +static int handle_feedback_item(const char *, const char *); +static void global_defaults(); +static void feedback_defaults(); +static void check_config_objects(CONFIG_CONTEXT *context); static int maxscale_getline(char** dest, int* size, FILE* file); -int config_truth_value(char *str); -bool isInternalService(char *router); -int config_get_ifaddr(unsigned char *output); -int config_get_release_string(char* release); -FEEDBACK_CONF * config_get_feedback_data(); -void config_add_param(CONFIG_CONTEXT*,char*,char*); +int config_truth_value(char *str); +bool isInternalService(char *router); +int config_get_ifaddr(unsigned char *output); +int config_get_release_string(char* release); +FEEDBACK_CONF *config_get_feedback_data(); +void config_add_param(CONFIG_CONTEXT*, char*, char*); bool config_has_duplicate_sections(const char* config); -static char *config_file = NULL; -static GATEWAY_CONF gateway; -static FEEDBACK_CONF feedback; -char *version_string = NULL; +static char *config_file = NULL; +static GATEWAY_CONF gateway; +static FEEDBACK_CONF feedback; +char *version_string = NULL; /** * Trim whitespace from the front and rear of a string * - * @param str String to trim - * @return Trimmed string, changes are done in situ + * @param str String to trim + * @return Trimmed string, changes are done in situ */ static char * trim(char *str) { -char *ptr; + char *ptr; - while (isspace(*str)) - str++; + while (isspace(*str)) + { + str++; + } - /* Point to last character of the string */ - ptr = str + strlen(str) - 1; - while (ptr > str && isspace(*ptr)) - *ptr-- = 0; + /* Point to last character of the string */ + ptr = str + strlen(str) - 1; + while (ptr > str && isspace(*ptr)) + { + *ptr-- = 0; + } - return str; + return str; } /** @@ -139,35 +143,36 @@ char* config_clean_string_list(char* str) { char *tmp; - if((tmp = malloc(sizeof(char)*(strlen(str) + 1))) != NULL) + if ((tmp = malloc(sizeof(char) * (strlen(str) + 1))) != NULL) { char *ptr; int match[MAXSCALE_PCRE_BUFSZ]; pcre* re; const char *re_err; - int err_offset,rval; - + int err_offset, rval; tmp[0] = '\0'; - if((re = pcre_compile("\\s*+([^,]*[^,\\s])",0,&re_err,&err_offset,NULL)) == NULL) + if ((re = pcre_compile("\\s*+([^,]*[^,\\s])", 0, &re_err, &err_offset, NULL)) == NULL) { - skygw_log_write(LE,"[%s] Error: Regular expression compilation failed at %d: %s", - __FUNCTION__,err_offset,re_err); + skygw_log_write(LE, "[%s] Error: Regular expression compilation failed at %d: %s", + __FUNCTION__, err_offset, re_err); free(tmp); return NULL; } ptr = str; - while((rval = pcre_exec(re,NULL,ptr,strlen(ptr),0,0,(int*)&match,MAXSCALE_PCRE_BUFSZ)) > 1) + while ((rval = pcre_exec(re, NULL, ptr, strlen(ptr), 0, 0, (int*)&match, MAXSCALE_PCRE_BUFSZ)) > 1) { const char* substr; - pcre_get_substring(ptr,(int*)&match,rval,1,&substr); - if(strlen(tmp) > 0) - strcat(tmp,","); - strcat(tmp,substr); + pcre_get_substring(ptr, (int*)&match, rval, 1, &substr); + if (strlen(tmp) > 0) + { + strcat(tmp, ","); + } + strcat(tmp, substr); pcre_free_substring(substr); ptr = &ptr[match[1]]; } @@ -175,7 +180,7 @@ char* config_clean_string_list(char* str) } else { - skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); + skygw_log_write(LE, "[%s] Error: Memory allocation failed.", __FUNCTION__); } return tmp; @@ -183,167 +188,192 @@ char* config_clean_string_list(char* str) /** * Config item handler for the ini file reader * - * @param userdata The config context element - * @param section The config file section - * @param name The Parameter name - * @param value The Parameter value + * @param userdata The config context element + * @param section The config file section + * @param name The Parameter name + * @param value The Parameter value * @return zero on error */ static int handler(void *userdata, const char *section, const char *name, const char *value) { -CONFIG_CONTEXT *cntxt = (CONFIG_CONTEXT *)userdata; -CONFIG_CONTEXT *ptr = cntxt; -CONFIG_PARAMETER *param, *p1; + CONFIG_CONTEXT *cntxt = (CONFIG_CONTEXT *)userdata; + CONFIG_CONTEXT *ptr = cntxt; + CONFIG_PARAMETER *param, *p1; - if (strcmp(section, "gateway") == 0 || strcasecmp(section, "MaxScale") == 0) - { - return handle_global_item(name, value); - } + if (strcmp(section, "gateway") == 0 || strcasecmp(section, "MaxScale") == 0) + { + return handle_global_item(name, value); + } - if (strcasecmp(section, "feedback") == 0) - { - return handle_feedback_item(name, value); - } + if (strcasecmp(section, "feedback") == 0) + { + return handle_feedback_item(name, value); + } - /* - * If we already have some parameters for the object - * add the parameters to that object. If not create - * a new object. - */ - while (ptr && strcmp(ptr->object, section) != 0) - ptr = ptr->next; - if (!ptr) - { - if ((ptr = (CONFIG_CONTEXT *)malloc(sizeof(CONFIG_CONTEXT))) == NULL) - return 0; - ptr->object = strdup(section); - ptr->parameters = NULL; - ptr->next = cntxt->next; - ptr->element = NULL; - cntxt->next = ptr; - } - /* Check to see if the parameter already exists for the section */ - p1 = ptr->parameters; - while (p1) - { - if (!strcmp(p1->name, name)) - { - char *tmp; - int paramlen = strlen(p1->value) + strlen(value) + 2; + /* + * If we already have some parameters for the object + * add the parameters to that object. If not create + * a new object. + */ + while (ptr && strcmp(ptr->object, section) != 0) + { + ptr = ptr->next; + } - if((tmp = realloc(p1->value,sizeof(char) * (paramlen))) == NULL) - { - skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); - return 0; - } - strcat(tmp,","); - strcat(tmp,value); - if((p1->value = config_clean_string_list(tmp)) == NULL) - { - p1->value = tmp; - skygw_log_write(LE,"[%s] Error: Cleaning configuration parameter failed.",__FUNCTION__); - return 0; - } - free(tmp); - return 1; - } - p1 = p1->next; - } + if (!ptr) + { + if ((ptr = (CONFIG_CONTEXT *)malloc(sizeof(CONFIG_CONTEXT))) == NULL) + { + return 0; + } + ptr->object = strdup(section); + ptr->parameters = NULL; + ptr->next = cntxt->next; + ptr->element = NULL; + cntxt->next = ptr; + } + /* Check to see if the parameter already exists for the section */ + p1 = ptr->parameters; + while (p1) + { + if (!strcmp(p1->name, name)) + { + char *tmp; + int paramlen = strlen(p1->value) + strlen(value) + 2; - if ((param = (CONFIG_PARAMETER *)malloc(sizeof(CONFIG_PARAMETER))) == NULL) - return 0; - param->name = strdup(name); - param->value = strdup(value); - param->next = ptr->parameters; - ptr->parameters = param; + if ((tmp = realloc(p1->value, sizeof(char) * (paramlen))) == NULL) + { + skygw_log_write(LE, "[%s] Error: Memory allocation failed.", __FUNCTION__); + return 0; + } + strcat(tmp, ","); + strcat(tmp, value); + if ((p1->value = config_clean_string_list(tmp)) == NULL) + { + p1->value = tmp; + skygw_log_write(LE, "[%s] Error: Cleaning configuration parameter failed.", __FUNCTION__); + return 0; + } + free(tmp); + return 1; + } + p1 = p1->next; + } - return 1; + if ((param = (CONFIG_PARAMETER *)malloc(sizeof(CONFIG_PARAMETER))) == NULL) + { + return 0; + } + + param->name = strdup(name); + param->value = strdup(value); + param->next = ptr->parameters; + ptr->parameters = param; + + return 1; } /** * Load the configuration file for the MaxScale * - * @param file The filename of the configuration file + * @param file The filename of the configuration file * @return A zero return indicates a fatal error reading the configuration */ int config_load(char *file) { -CONFIG_CONTEXT config; -int rval, ini_rval; + CONFIG_CONTEXT config; + int rval, ini_rval; if (config_has_duplicate_sections(file)) { return 0; } - MYSQL *conn; - conn = mysql_init(NULL); - if (conn) { - if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) { - char *ptr,*tmp; - - tmp = (char *)mysql_get_server_info(conn); - unsigned int server_version = mysql_get_server_version(conn); - - if(version_string) - free(version_string); - - if((version_string = malloc(strlen(tmp) + strlen("5.5.5-") + 1)) == NULL) - return 0; - - if (server_version >= 100000) - { - strcpy(version_string,"5.5.5-"); - strcat(version_string,tmp); - } - else - { - strcpy(version_string,tmp); - } - - ptr = strstr(version_string, "-embedded"); - if (ptr) { - *ptr = '\0'; - } - - - } - mysql_close(conn); - } - - global_defaults(); - feedback_defaults(); - - config.object = ""; - config.next = NULL; - - if (( ini_rval = ini_parse(file, handler, &config)) != 0) + MYSQL *conn; + conn = mysql_init(NULL); + if (conn) + { + if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) { - char errorbuffer[1024 + 1]; + char *ptr, *tmp; - if (ini_rval > 0) - snprintf(errorbuffer, sizeof(errorbuffer), - "Error: Failed to parse configuration file. Error on line %d.", ini_rval); - else if(ini_rval == -1) - snprintf(errorbuffer, sizeof(errorbuffer), - "Error: Failed to parse configuration file. Failed to open file."); + tmp = (char *)mysql_get_server_info(conn); + unsigned int server_version = mysql_get_server_version(conn); + + if (version_string) + { + free(version_string); + } + + if ((version_string = malloc(strlen(tmp) + strlen("5.5.5-") + 1)) == NULL) + { + return 0; + } + + if (server_version >= 100000) + { + strcpy(version_string, "5.5.5-"); + strcat(version_string, tmp); + } else - snprintf(errorbuffer, sizeof(errorbuffer), - "Error: Failed to parse configuration file. Memory allocation failed."); + { + strcpy(version_string, tmp); + } - skygw_log_write(LE, errorbuffer); - return 0; + ptr = strstr(version_string, "-embedded"); + if (ptr) + { + *ptr = '\0'; + } + } + mysql_close(conn); + } + + global_defaults(); + feedback_defaults(); + + config.object = ""; + config.next = NULL; + + if ((ini_rval = ini_parse(file, handler, &config)) != 0) + { + char errorbuffer[1024 + 1]; + + if (ini_rval > 0) + { + snprintf(errorbuffer, sizeof(errorbuffer), + "Error: Failed to parse configuration file. Error on line %d.", ini_rval); + } + else if(ini_rval == -1) + { + snprintf(errorbuffer, sizeof(errorbuffer), + "Error: Failed to parse configuration file. Failed to open file."); + } + else + { + snprintf(errorbuffer, sizeof(errorbuffer), + "Error: Failed to parse configuration file. Memory allocation failed."); } - config_file = file; + skygw_log_write(LE, errorbuffer); + return 0; + } - check_config_objects(config.next); - rval = process_config_context(config.next); - free_config_context(config.next); + config_file = file; - return rval; + check_config_objects(config.next); + rval = process_config_context(config.next); + free_config_context(config.next); + + /** Start all monitors */ + if (rval) + { + monitorStartAll(); + } + + return rval; } /** @@ -354,147 +384,129 @@ int rval, ini_rval; int config_reload() { -CONFIG_CONTEXT config; -int rval; + CONFIG_CONTEXT config; + int rval; - if (!config_file) - return 0; + if (!config_file) + { + return 0; + } if (config_has_duplicate_sections(config_file)) { return 0; } - if (gateway.version_string) - free(gateway.version_string); + if (gateway.version_string) + { + free(gateway.version_string); + } - global_defaults(); + global_defaults(); - config.object = ""; - config.next = NULL; + config.object = ""; + config.next = NULL; - if (ini_parse(config_file, handler, &config) < 0) - return 0; + if (ini_parse(config_file, handler, &config) < 0) + { + return 0; + } - rval = process_config_update(config.next); - free_config_context(config.next); + rval = process_config_update(config.next); + free_config_context(config.next); - return rval; + return rval; } /** * Process a configuration context and turn it into the set of object * we need. * - * @param context The configuration data + * @param context The configuration data * @return A zero result indicates a fatal error */ -static int +static int process_config_context(CONFIG_CONTEXT *context) { CONFIG_CONTEXT *obj; int error_count = 0; HASHTABLE* monitorhash; - if((monitorhash = hashtable_alloc(5,simple_str_hash,strcmp)) == NULL) + if ((monitorhash = hashtable_alloc(5, simple_str_hash, strcmp)) == NULL) { - skygw_log_write(LOGFILE_ERROR,"Error: Failed to allocate ,monitor configuration check hashtable."); + skygw_log_write(LOGFILE_ERROR, "Error: Failed to allocate, monitor configuration check hashtable."); return 0; } - hashtable_memory_fns(monitorhash,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,NULL); + hashtable_memory_fns(monitorhash, (HASHMEMORYFN)strdup, NULL, (HASHMEMORYFN)free, NULL); /** - * Process the data and create the services and servers defined - * in the data. - */ - obj = context; - while (obj) - { - char *type = config_get_value(obj->parameters, "type"); - if (type == NULL) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Configuration object '%s' has no type.", - obj->object))); - error_count++; - } - else if (!strcmp(type, "service")) - { - char *router = config_get_value(obj->parameters, - "router"); - if (router) - { - char* max_slave_conn_str; - char* max_slave_rlag_str; - char *user; - char *auth; - char *enable_root_user; - char *connection_timeout; - char *auth_all_servers; - char *optimize_wildcard; - char *strip_db_esc; - char *weightby; - char *version_string; - char *subservices; - char *ssl,*ssl_cert,*ssl_key,*ssl_ca_cert,*ssl_version; - char* ssl_cert_verify_depth; - bool is_rwsplit = false; - bool is_schemarouter = false; - char *allow_localhost_match_wildcard_host; + * Process the data and create the services and servers defined + * in the data. + */ + obj = context; + while (obj) + { + char *type = config_get_value(obj->parameters, "type"); + if (type == NULL) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Configuration object '%s' has no type.", + obj->object))); + error_count++; + } + else if (!strcmp(type, "service")) + { + char *router = config_get_value(obj->parameters, "router"); + if (router) + { + char* max_slave_conn_str; + char* max_slave_rlag_str; + char *user; + char *auth; + char *enable_root_user; + char *connection_timeout; + char *auth_all_servers; + char *optimize_wildcard; + char *strip_db_esc; + char *weightby; + char *version_string; + char *subservices; + char *ssl, *ssl_cert, *ssl_key, *ssl_ca_cert, *ssl_version; + char* ssl_cert_verify_depth; + bool is_rwsplit = false; + bool is_schemarouter = false; + char *allow_localhost_match_wildcard_host; - obj->element = service_alloc(obj->object, router); - user = config_get_value(obj->parameters, "user"); - auth = config_get_value(obj->parameters, "passwd"); - subservices = config_get_value(obj->parameters, "subservices"); - ssl = config_get_value(obj->parameters, "ssl"); - ssl_cert = config_get_value(obj->parameters, "ssl_cert"); - ssl_key = config_get_value(obj->parameters, "ssl_key"); - ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); - ssl_version = config_get_value(obj->parameters, "ssl_version"); - ssl_cert_verify_depth = config_get_value(obj->parameters, "ssl_cert_verify_depth"); - enable_root_user = config_get_value( - obj->parameters, - "enable_root_user"); + obj->element = service_alloc(obj->object, router); + user = config_get_value(obj->parameters, "user"); + auth = config_get_value(obj->parameters, "passwd"); + subservices = config_get_value(obj->parameters, "subservices"); + ssl = config_get_value(obj->parameters, "ssl"); + ssl_cert = config_get_value(obj->parameters, "ssl_cert"); + ssl_key = config_get_value(obj->parameters, "ssl_key"); + ssl_ca_cert = config_get_value(obj->parameters, "ssl_ca_cert"); + ssl_version = config_get_value(obj->parameters, "ssl_version"); + ssl_cert_verify_depth = config_get_value(obj->parameters, "ssl_cert_verify_depth"); + enable_root_user = config_get_value(obj->parameters, "enable_root_user"); + connection_timeout = config_get_value(obj->parameters, "connection_timeout"); + optimize_wildcard = config_get_value(obj->parameters, "optimize_wildcard"); + auth_all_servers = config_get_value(obj->parameters, "auth_all_servers"); + strip_db_esc = config_get_value(obj->parameters, "strip_db_esc"); + allow_localhost_match_wildcard_host = config_get_value(obj->parameters, + "localhost_match_wildcard_host"); + weightby = config_get_value(obj->parameters, "weightby"); - connection_timeout = - config_get_value( - obj->parameters, - "connection_timeout"); + version_string = config_get_value(obj->parameters, "version_string"); - optimize_wildcard = - config_get_value( - obj->parameters, - "optimize_wildcard"); - - auth_all_servers = - config_get_value( - obj->parameters, - "auth_all_servers"); - - strip_db_esc = - config_get_value( - obj->parameters, - "strip_db_esc"); - - allow_localhost_match_wildcard_host = - config_get_value(obj->parameters, - "localhost_match_wildcard_host"); - - weightby = config_get_value(obj->parameters, "weightby"); - - version_string = config_get_value(obj->parameters, - "version_string"); - - if(subservices) - { - service_set_param_value(obj->element, - obj->parameters, - subservices, - 1,STRING_TYPE); - } - char *log_auth_warnings = config_get_value(obj->parameters, - "log_auth_warnings"); + if (subservices) + { + service_set_param_value(obj->element, + obj->parameters, + subservices, + 1, STRING_TYPE); + } + char *log_auth_warnings = config_get_value(obj->parameters, "log_auth_warnings"); int truthval; if (log_auth_warnings && (truthval = config_truth_value(log_auth_warnings)) != -1) { @@ -502,991 +514,1029 @@ process_config_context(CONFIG_CONTEXT *context) } CONFIG_PARAMETER* param; - if((param = config_get_param(obj->parameters, "ignore_databases"))) + if ((param = config_get_param(obj->parameters, "ignore_databases"))) { service_set_param_value(obj->element, param, param->value, 0, STRING_TYPE); } - if((param = config_get_param(obj->parameters, "ignore_databases_regex"))) + if ((param = config_get_param(obj->parameters, "ignore_databases_regex"))) { service_set_param_value(obj->element, param, param->value, 0, STRING_TYPE); } - /** flag for rwsplit-specific parameters */ - if (strncmp(router, "readwritesplit", strlen("readwritesplit")+1) == 0) - { - is_rwsplit = true; - } + /** flag for rwsplit-specific parameters */ + if (strncmp(router, "readwritesplit", strlen("readwritesplit")+1) == 0) + { + is_rwsplit = true; + } - allow_localhost_match_wildcard_host = - config_get_value(obj->parameters, "localhost_match_wildcard_host"); + allow_localhost_match_wildcard_host = + config_get_value(obj->parameters, "localhost_match_wildcard_host"); - if (obj->element == NULL) /*< if module load failed */ + if (obj->element == NULL) /*< if module load failed */ + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Reading configuration " + "for router service '%s' failed. " + "Router %s is not loaded.", + obj->object, + obj->object))); + obj = obj->next; + continue; /*< process next obj */ + } + + if (version_string) { + /** Add the 5.5.5- string to the start of the version string if + * the version string starts with "10.". + * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ + if (strncmp(version_string, "10.", 3) == 0) + { + ((SERVICE *)(obj->element))->version_string = + malloc((strlen(version_string) + strlen("5.5.5-") + 1) * sizeof(char)); + strcpy(((SERVICE *)(obj->element))->version_string, "5.5.5-"); + strcat(((SERVICE *)(obj->element))->version_string, version_string); + } + else + { + ((SERVICE *)(obj->element))->version_string = strdup(version_string); + } + } + else + { + if (gateway.version_string) + ((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string); + } + + max_slave_conn_str = config_get_value(obj->parameters, "max_slave_connections"); + max_slave_rlag_str = config_get_value(obj->parameters, "max_slave_replication_lag"); + + if (ssl) + { + if (ssl_cert == NULL) + { + error_count++; + skygw_log_write(LE, "Error: Server certificate missing for service '%s'." + "Please provide the path to the server certificate by adding " + "the ssl_cert= parameter", obj->object); + } + + if (ssl_ca_cert == NULL) + { + error_count++; + skygw_log_write(LE, "Error: CA Certificate missing for service '%s'." + "Please provide the path to the certificate authority " + "certificate by adding the ssl_ca_cert= parameter", + obj->object); + } + + if (ssl_key == NULL) + { + error_count++; + skygw_log_write(LE, "Error: Server private key missing for service '%s'. " + "Please provide the path to the server certificate key by " + "adding the ssl_key= parameter", + obj->object); + } + + if (access(ssl_ca_cert, F_OK) != 0) + { + skygw_log_write(LE, "Error: Certificate authority file for service '%s' " + "not found: %s", + obj->object, ssl_ca_cert); + error_count++; + } + + if (access(ssl_cert, F_OK) != 0) + { + skygw_log_write(LE, "Error: Server certificate file for service '%s' not found: %s", + obj->object, + ssl_cert); + error_count++; + } + + if (access(ssl_key, F_OK) != 0) + { + skygw_log_write(LE, "Error: Server private key file for service '%s' not found: %s", + obj->object, + ssl_key); + error_count++; + } + + if (error_count == 0) + { + if (serviceSetSSL(obj->element, ssl) != 0) + { + skygw_log_write(LE, "Error: Unknown parameter for service '%s': %s", + obj->object, ssl); + error_count++; + } + else + { + serviceSetCertificates(obj->element, ssl_cert, ssl_key, ssl_ca_cert); + if (ssl_version) + { + if (serviceSetSSLVersion(obj->element, ssl_version) != 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Reading configuration " - "for router service '%s' failed. " - "Router %s is not loaded.", - obj->object, - obj->object))); - obj = obj->next; - continue; /*< process next obj */ + skygw_log_write(LE, "Error: Unknown parameter value for " + "'ssl_version' for service '%s': %s", + obj->object, ssl_version); + error_count++; } - - if (version_string) { - - /** Add the 5.5.5- string to the start of the version string if - * the version string starts with "10.". - * This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */ - if(strncmp(version_string,"10.",3) == 0) - { - ((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) + - strlen("5.5.5-") + 1) * sizeof(char)); - strcpy(((SERVICE *)(obj->element))->version_string,"5.5.5-"); - strcat(((SERVICE *)(obj->element))->version_string,version_string); - } - else - { - ((SERVICE *)(obj->element))->version_string = strdup(version_string); - } - } else { - if (gateway.version_string) - ((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string); - } - max_slave_conn_str = - config_get_value(obj->parameters, - "max_slave_connections"); - - max_slave_rlag_str = - config_get_value(obj->parameters, - "max_slave_replication_lag"); - - if(ssl) - { - if(ssl_cert == NULL) - { - error_count++; - skygw_log_write(LE,"Error: Server certificate missing for service '%s'." - "Please provide the path to the server certificate by adding the ssl_cert= parameter", - obj->object); - } - if(ssl_ca_cert == NULL) - { - error_count++; - skygw_log_write(LE,"Error: CA Certificate missing for service '%s'." - "Please provide the path to the certificate authority certificate by adding the ssl_ca_cert= parameter", - obj->object); - } - if(ssl_key == NULL) - { - error_count++; - skygw_log_write(LE,"Error: Server private key missing for service '%s'. " - "Please provide the path to the server certificate key by adding the ssl_key= parameter" - ,obj->object); - } - - if(access(ssl_ca_cert,F_OK) != 0) - { - skygw_log_write(LE,"Error: Certificate authority file for service '%s' not found: %s", - obj->object, - ssl_ca_cert); - error_count++; - } - if(access(ssl_cert,F_OK) != 0) - { - skygw_log_write(LE,"Error: Server certificate file for service '%s' not found: %s", - obj->object, - ssl_cert); - error_count++; - } - if(access(ssl_key,F_OK) != 0) - { - skygw_log_write(LE,"Error: Server private key file for service '%s' not found: %s", - obj->object, - ssl_key); - error_count++; - } - - if(error_count == 0) - { - if(serviceSetSSL(obj->element,ssl) != 0) - { - skygw_log_write(LE,"Error: Unknown parameter for service '%s': %s",obj->object,ssl); - error_count++; - } - else - { - serviceSetCertificates(obj->element,ssl_cert,ssl_key,ssl_ca_cert); - if(ssl_version) - { - if(serviceSetSSLVersion(obj->element,ssl_version) != 0) - { - skygw_log_write(LE,"Error: Unknown parameter value for 'ssl_version' for service '%s': %s",obj->object,ssl_version); - error_count++; - } - } - if(ssl_cert_verify_depth) - { - if(serviceSetSSLVerifyDepth(obj->element,atoi(ssl_cert_verify_depth)) != 0) - { - skygw_log_write(LE,"Error: Invalid parameter value for 'ssl_cert_verify_depth' for service '%s': %s",obj->object,ssl_cert_verify_depth); - error_count++; - } - } - } - } - - } + } + if (ssl_cert_verify_depth) + { + if (serviceSetSSLVerifyDepth(obj->element, atoi(ssl_cert_verify_depth)) != 0) + { + skygw_log_write(LE, "Error: Invalid parameter value for " + "'ssl_cert_verify_depth' for service '%s': %s", + obj->object, ssl_cert_verify_depth); + error_count++; + } + } + } + } + } serviceSetRetryOnFailure(obj->element, config_get_value(obj->parameters, "retry_on_failure")); if (enable_root_user) - serviceEnableRootUser( - obj->element, - config_truth_value(enable_root_user)); + { + serviceEnableRootUser(obj->element, config_truth_value(enable_root_user)); + } - if (connection_timeout) - serviceSetTimeout( - obj->element, - atoi(connection_timeout)); + if (connection_timeout) + { + serviceSetTimeout(obj->element, atoi(connection_timeout)); + } - if(auth_all_servers) - serviceAuthAllServers(obj->element, - config_truth_value(auth_all_servers)); + if(auth_all_servers) + { + serviceAuthAllServers(obj->element, config_truth_value(auth_all_servers)); + } - if(optimize_wildcard) - serviceOptimizeWildcard(obj->element, - config_truth_value(optimize_wildcard)); + if(optimize_wildcard) + { + serviceOptimizeWildcard(obj->element, config_truth_value(optimize_wildcard)); + } - if(strip_db_esc) - serviceStripDbEsc(obj->element, - config_truth_value(strip_db_esc)); + if(strip_db_esc) + { + serviceStripDbEsc(obj->element, config_truth_value(strip_db_esc)); + } - if (weightby) - serviceWeightBy(obj->element, weightby); + if (weightby) + { + serviceWeightBy(obj->element, weightby); + } - if (allow_localhost_match_wildcard_host) - serviceEnableLocalhostMatchWildcardHost( - obj->element, - config_truth_value(allow_localhost_match_wildcard_host)); + if (allow_localhost_match_wildcard_host) + { + serviceEnableLocalhostMatchWildcardHost( + obj->element, + config_truth_value(allow_localhost_match_wildcard_host)); + } - if (!auth) - auth = config_get_value(obj->parameters, - "auth"); + if (!auth) + { + auth = config_get_value(obj->parameters, "auth"); + } - if (obj->element && user && auth) - { - serviceSetUser(obj->element, - user, - auth); - } - else if (user && auth == NULL) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Service '%s' has a " - "user defined but no " - "corresponding password.", - obj->object))); - } - /** Read, validate and set max_slave_connections */ - if (max_slave_conn_str != NULL) + if (obj->element && user && auth) + { + serviceSetUser(obj->element, user, auth); + } + else if (user && auth == NULL) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Service '%s' has a " + "user defined but no " + "corresponding password.", + obj->object))); + } + /** Read, validate and set max_slave_connections */ + if (max_slave_conn_str != NULL) + { + CONFIG_PARAMETER* param; + bool succp; + + param = config_get_param(obj->parameters, "max_slave_connections"); + + if (param == NULL) + { + succp = false; + } + else + { + succp = service_set_param_value( + obj->element, + param, + max_slave_conn_str, + COUNT_ATMOST, + (COUNT_TYPE|PERCENT_TYPE)); + } + + if (!succp) + { + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : invalid value type " + "for parameter \'%s.%s = %s\'\n\tExpected " + "type is either for slave connection " + "count or\n\t%% for specifying the " + "maximum percentage of available the " + "slaves that will be connected.", + ((SERVICE*)obj->element)->name, + param->name, + param->value))); + } + } + /** Read, validate and set max_slave_replication_lag */ + if (max_slave_rlag_str != NULL) + { + CONFIG_PARAMETER* param; + bool succp; + + param = config_get_param( + obj->parameters, + "max_slave_replication_lag"); + + if (param == NULL) + { + succp = false; + } + else + { + succp = service_set_param_value( + obj->element, + param, + max_slave_rlag_str, + COUNT_ATMOST, + COUNT_TYPE); + } + + if (!succp) + { + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : invalid value type " + "for parameter \'%s.%s = %s\'\n\tExpected " + "type is for maximum " + "slave replication lag.", + ((SERVICE*)obj->element)->name, + param->name, + param->value))); + } + } + /** Parameters for rwsplit router only */ + if (is_rwsplit) + { + CONFIG_PARAMETER* param; + char* use_sql_variables_in; + bool succp; + + use_sql_variables_in = + config_get_value(obj->parameters, "use_sql_variables_in"); + + if (use_sql_variables_in != NULL) + { + param = config_get_param(obj->parameters, + "use_sql_variables_in"); + + if (param == NULL) + { + succp = false; + } + else + { + succp = service_set_param_value(obj->element, + param, + use_sql_variables_in, + COUNT_NONE, + SQLVAR_TARGET_TYPE); + } + + if (!succp) + { + if(param) + { + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : invalid value type " + "for parameter \'%s.%s = %s\'\n\tExpected " + "type is [master|all] for " + "use sql variables in.", + ((SERVICE*)obj->element)->name, + param->name, + param->value))); + } + else + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Error : parameter was NULL"))); + } + } + } + } /*< if (rw_split) */ + } /*< if (router) */ + else + { + obj->element = NULL; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : No router defined for service " + "'%s'\n", + obj->object))); + error_count++; + } + } + else if (!strcmp(type, "server")) + { + char *address; + char *port; + char *protocol; + char *monuser; + char *monpw; + + address = config_get_value(obj->parameters, "address"); + port = config_get_value(obj->parameters, "port"); + protocol = config_get_value(obj->parameters, "protocol"); + monuser = config_get_value(obj->parameters, "monitoruser"); + monpw = config_get_value(obj->parameters, "monitorpw"); + + if (address && port && protocol) + { + obj->element = server_alloc(address, + protocol, + atoi(port)); + server_set_unique_name(obj->element, obj->object); + } + else + { + obj->element = NULL; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Server '%s' is missing a " + "required configuration parameter. A " + "server must " + "have address, port and protocol " + "defined.", + obj->object))); + error_count++; + } + + if (obj->element && monuser && monpw) + { + serverAddMonUser(obj->element, monuser, monpw); + } + else if (monuser && monpw == NULL) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Server '%s' has a monitoruser" + "defined but no corresponding password.", + obj->object))); + } + + if (obj->element) + { + SERVER *server = obj->element; + server->persistpoolmax = strtol(config_get_value_string(obj->parameters, + "persistpoolmax"), NULL, 0); + server->persistmaxtime = strtol(config_get_value_string(obj->parameters, + "persistmaxtime"), NULL, 0); + CONFIG_PARAMETER *params = obj->parameters; + + while (params) + { + if (strcmp(params->name, "address") + && strcmp(params->name, "port") + && strcmp(params->name, "protocol") + && strcmp(params->name, "monitoruser") + && strcmp(params->name, "monitorpw") + && strcmp(params->name, "type") + && strcmp(params->name, "persistpoolmax") + && strcmp(params->name, "persistmaxtime")) + { + serverAddParameter(obj->element, params->name, params->value); + } + params = params->next; + } + } + } + else if (!strcmp(type, "filter")) + { + char *module = config_get_value(obj->parameters, "module"); + char *options = config_get_value(obj->parameters, "options"); + + if (module) + { + obj->element = filter_alloc(obj->object, module); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Filter '%s' has no module " + "defined defined to load.", + obj->object))); + error_count++; + } + + if (obj->element && options) + { + char *lasts; + char *s = strtok_r(options, ",", &lasts); + while (s) + { + filterAddOption(obj->element, s); + s = strtok_r(NULL, ",", &lasts); + } + } + + if (obj->element) + { + CONFIG_PARAMETER *params = obj->parameters; + while (params) + { + if (strcmp(params->name, "module") && strcmp(params->name, "options")) + { + filterAddParameter(obj->element, + params->name, + params->value); + } + params = params->next; + } + } + } + obj = obj->next; + } + + /* + * Now we have the services we can add the servers to the services + * add the protocols to the services + */ + obj = context; + while (obj) + { + char *type = config_get_value(obj->parameters, "type"); + if (type == NULL) + { + ; + } + else if (!strcmp(type, "service")) + { + char *servers; + char *roptions; + char *router; + char *filters = config_get_value(obj->parameters, "filters"); + servers = config_get_value(obj->parameters, "servers"); + roptions = config_get_value(obj->parameters, "router_options"); + router = config_get_value(obj->parameters, "router"); + if (servers && obj->element) + { + char *lasts; + char *s = strtok_r(servers, ",", &lasts); + while (s) + { + CONFIG_CONTEXT *obj1 = context; + int found = 0; + while (obj1) + { + if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element) + { + found = 1; + serviceAddBackend(obj->element, obj1->element); + } + obj1 = obj1->next; + } + + if (!found) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured as part of " + "service '%s'.", + s, obj->object))); + } + s = strtok_r(NULL, ",", &lasts); + } + } + else if (servers == NULL && !isInternalService(router)) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Warning: The service '%s' is missing a " + "definition of the servers that provide " + "the service.", + obj->object))); + } + + if (roptions && obj->element) + { + char *lasts; + char *s = strtok_r(roptions, ",", &lasts); + while (s) + { + serviceAddRouterOption(obj->element, s); + s = strtok_r(NULL, ",", &lasts); + } + } + + if (filters && obj->element) + { + serviceSetFilters(obj->element, filters); + } + } + else if (!strcmp(type, "listener")) + { + char *service; + char *address; + char *port; + char *protocol; + char *socket; + struct sockaddr_in serv_addr; + + service = config_get_value(obj->parameters, "service"); + port = config_get_value(obj->parameters, "port"); + address = config_get_value(obj->parameters, "address"); + protocol = config_get_value(obj->parameters, "protocol"); + socket = config_get_value(obj->parameters, "socket"); + + /* if id is not set, do it now */ + if (gateway.id == 0) + { + setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address); + gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + + (port != NULL ? atoi(port) : 0 + getpid())); + } + + if (service && protocol && (socket || port)) + { + if (socket) + { + CONFIG_CONTEXT *ptr = context; + while (ptr && strcmp(ptr->object, service) != 0) + { + ptr = ptr->next; + } + + if (ptr && ptr->element) + { + serviceAddProtocol(ptr->element, protocol, socket, 0); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Listener '%s', " + "service '%s' not found. " + "Listener will not execute for socket %s.", + obj->object, service, socket))); + error_count++; + } + } + + if (port) + { + CONFIG_CONTEXT *ptr = context; + while (ptr && strcmp(ptr->object, service) != 0) + { + ptr = ptr->next; + } + + if (ptr && ptr->element) + { + serviceAddProtocol(ptr->element, protocol, address, atoi(port)); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Listener '%s', " + "service '%s' not found. " + "Listener will not execute.", + obj->object, service))); + error_count++; + } + } + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Listener '%s' is missing a " + "required " + "parameter. A Listener must have a " + "service, port and protocol defined.", + obj->object))); + error_count++; + } + } + else if (!strcmp(type, "monitor")) + { + char *module; + char *servers; + char *user; + char *passwd; + unsigned long interval = 0; + int connect_timeout = 0; + int read_timeout = 0; + int write_timeout = 0; + + module = config_get_value(obj->parameters, "module"); + servers = config_get_value(obj->parameters, "servers"); + user = config_get_value(obj->parameters, "user"); + passwd = config_get_value(obj->parameters, "passwd"); + if (config_get_value(obj->parameters, "monitor_interval")) + { + interval = strtoul(config_get_value(obj->parameters, "monitor_interval"), NULL, 10); + } + + if (config_get_value(obj->parameters, "backend_connect_timeout")) + { + connect_timeout = atoi(config_get_value(obj->parameters, "backend_connect_timeout")); + } + if (config_get_value(obj->parameters, "backend_read_timeout")) + { + read_timeout = atoi(config_get_value(obj->parameters, "backend_read_timeout")); + } + if (config_get_value(obj->parameters, "backend_write_timeout")) + { + write_timeout = atoi(config_get_value(obj->parameters, "backend_write_timeout")); + } + + if (module) + { + obj->element = monitor_alloc(obj->object, module); + if (servers && obj->element) + { + char *s, *lasts; + + /* if id is not set, compute it now with pid only */ + if (gateway.id == 0) + { + gateway.id = getpid(); + } + + monitorAddParameters(obj->element, obj->parameters); + + /* set monitor interval */ + if (interval > 0) + { + monitorSetInterval(obj->element, interval); + } + else + { + skygw_log_write(LOGFILE_ERROR, "Warning: Monitor '%s' " + "missing monitor_interval parameter, " + "default value of 10000 miliseconds.", obj->object); + } + + /* set timeouts */ + if (connect_timeout > 0) + { + monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, connect_timeout); + } + if (read_timeout > 0) + { + monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, read_timeout); + } + if (write_timeout > 0) + { + monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout); + } + + /* get the servers to monitor */ + s = strtok_r(servers, ",", &lasts); + while (s) + { + CONFIG_CONTEXT *obj1 = context; + int found = 0; + while (obj1) + { + if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element) + { + found = 1; + if (hashtable_add(monitorhash, obj1->object, "") == 0) { - CONFIG_PARAMETER* param; - bool succp; - - param = config_get_param(obj->parameters, - "max_slave_connections"); - - if (param == NULL) - { - succp = false; - } - else - { - succp = service_set_param_value( - obj->element, - param, - max_slave_conn_str, - COUNT_ATMOST, - (COUNT_TYPE|PERCENT_TYPE)); - } - - if (!succp) - { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : invalid value type " - "for parameter \'%s.%s = %s\'\n\tExpected " - "type is either for slave connection " - "count or\n\t%% for specifying the " - "maximum percentage of available the " - "slaves that will be connected.", - ((SERVICE*)obj->element)->name, - param->name, - param->value))); - } + skygw_log_write(LOGFILE_ERROR, + "Warning: Multiple monitors are monitoring server [%s]. " + "This will cause undefined behavior.", + obj1->object); } - /** Read, validate and set max_slave_replication_lag */ - if (max_slave_rlag_str != NULL) - { - CONFIG_PARAMETER* param; - bool succp; - - param = config_get_param( - obj->parameters, - "max_slave_replication_lag"); - - if (param == NULL) - { - succp = false; - } - else - { - succp = service_set_param_value( - obj->element, - param, - max_slave_rlag_str, - COUNT_ATMOST, - COUNT_TYPE); - } - - if (!succp) - { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : invalid value type " - "for parameter \'%s.%s = %s\'\n\tExpected " - "type is for maximum " - "slave replication lag.", - ((SERVICE*)obj->element)->name, - param->name, - param->value))); - } - } - /** Parameters for rwsplit router only */ - if (is_rwsplit) - { - CONFIG_PARAMETER* param; - char* use_sql_variables_in; - bool succp; - - use_sql_variables_in = - config_get_value(obj->parameters, - "use_sql_variables_in"); - - if (use_sql_variables_in != NULL) - { - param = config_get_param( - obj->parameters, - "use_sql_variables_in"); - - if (param == NULL) - { - succp = false; - } - else - { - succp = service_set_param_value(obj->element, - param, - use_sql_variables_in, - COUNT_NONE, - SQLVAR_TARGET_TYPE); - } - - if (!succp) - { - if(param){ - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : invalid value type " - "for parameter \'%s.%s = %s\'\n\tExpected " - "type is [master|all] for " - "use sql variables in.", - ((SERVICE*)obj->element)->name, - param->name, - param->value))); - }else{ - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : parameter was NULL"))); - - } - } - } - } /*< if (rw_split) */ - } /*< if (router) */ - else - { - obj->element = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : No router defined for service " - "'%s'\n", - obj->object))); - error_count++; - } - } - else if (!strcmp(type, "server")) - { - char *address; - char *port; - char *protocol; - char *monuser; - char *monpw; + monitorAddServer(obj->element, obj1->element); + } + obj1 = obj1->next; + } + if (!found) + { + LOGIF(LE, + (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured in the " + "monitor '%s'.", + s, obj->object))); + } - address = config_get_value(obj->parameters, "address"); - port = config_get_value(obj->parameters, "port"); - protocol = config_get_value(obj->parameters, "protocol"); - monuser = config_get_value(obj->parameters, - "monitoruser"); - monpw = config_get_value(obj->parameters, "monitorpw"); + s = strtok_r(NULL, ",", &lasts); + } + } - if (address && port && protocol) - { - obj->element = server_alloc(address, - protocol, - atoi(port)); - server_set_unique_name(obj->element, obj->object); - } - else - { - obj->element = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Server '%s' is missing a " - "required configuration parameter. A " - "server must " - "have address, port and protocol " - "defined.", - obj->object))); - error_count++; - } - if (obj->element && monuser && monpw) - serverAddMonUser(obj->element, monuser, monpw); - else if (monuser && monpw == NULL) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Server '%s' has a monitoruser" - "defined but no corresponding password.", - obj->object))); - } - if (obj->element) - { - SERVER *server = obj->element; - server->persistpoolmax = strtol(config_get_value_string(obj->parameters, "persistpoolmax"), NULL, 0); - server->persistmaxtime = strtol(config_get_value_string(obj->parameters, "persistmaxtime"), NULL, 0); - CONFIG_PARAMETER *params = obj->parameters; - while (params) - { - if (strcmp(params->name, "address") - && strcmp(params->name, "port") - && strcmp(params->name, - "protocol") - && strcmp(params->name, - "monitoruser") - && strcmp(params->name, - "monitorpw") - && strcmp(params->name, - "type") - && strcmp(params->name, - "persistpoolmax") - && strcmp(params->name, - "persistmaxtime") - ) - { - serverAddParameter(obj->element, - params->name, - params->value); - } - params = params->next; - } - } - } - else if (!strcmp(type, "filter")) - { - char *module = config_get_value(obj->parameters, - "module"); - char *options = config_get_value(obj->parameters, - "options"); + if (obj->element && user && passwd) + { + monitorAddUser(obj->element, user, passwd); + check_monitor_permissions(obj->element); + } + else if (obj->element && user) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, "Error: " + "Monitor '%s' defines a " + "username with no password.", + obj->object))); + error_count++; + } + } + else + { + obj->element = NULL; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Monitor '%s' is missing a " + "require module parameter.", + obj->object))); + error_count++; + } + } + else if (strcmp(type, "server") != 0 && strcmp(type, "filter") != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Configuration object '%s' has an " + "invalid type specified.", + obj->object))); + error_count++; + } - if (module) - { - obj->element = filter_alloc(obj->object, module); - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Filter '%s' has no module " - "defined defined to load.", - obj->object))); - error_count++; - } - if (obj->element && options) - { - char *lasts; - char *s = strtok_r(options, ",", &lasts); - while (s) - { - filterAddOption(obj->element, s); - s = strtok_r(NULL, ",", &lasts); - } - } - if (obj->element) - { - CONFIG_PARAMETER *params = obj->parameters; - while (params) - { - if (strcmp(params->name, "module") - && strcmp(params->name, - "options")) - { - filterAddParameter(obj->element, - params->name, - params->value); - } - params = params->next; - } - } - } - obj = obj->next; - } + obj = obj->next; + } /*< while */ - /* - * Now we have the services we can add the servers to the services - * add the protocols to the services - */ - obj = context; - while (obj) - { - char *type = config_get_value(obj->parameters, "type"); - if (type == NULL) - ; - else if (!strcmp(type, "service")) - { - char *servers; - char *roptions; - char *router; - char *filters = config_get_value(obj->parameters, - "filters"); - servers = config_get_value(obj->parameters, "servers"); - roptions = config_get_value(obj->parameters, - "router_options"); - router = config_get_value(obj->parameters, "router"); - if (servers && obj->element) - { - char *lasts; - char *s = strtok_r(servers, ",", &lasts); - while (s) - { - CONFIG_CONTEXT *obj1 = context; - int found = 0; - while (obj1) - { - if (strcmp(trim(s), obj1->object) == 0 && - obj->element && obj1->element) - { - found = 1; - serviceAddBackend( - obj->element, - obj1->element); - } - obj1 = obj1->next; - } - if (!found) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Unable to find " - "server '%s' that is " - "configured as part of " - "service '%s'.", - s, obj->object))); - } - s = strtok_r(NULL, ",", &lasts); - } - } - else if (servers == NULL && !isInternalService(router)) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Warning: The service '%s' is missing a " - "definition of the servers that provide " - "the service.", - obj->object))); - } - if (roptions && obj->element) - { - char *lasts; - char *s = strtok_r(roptions, ",", &lasts); - while (s) - { - serviceAddRouterOption(obj->element, s); - s = strtok_r(NULL, ",", &lasts); - } - } - if (filters && obj->element) - { - serviceSetFilters(obj->element, filters); - } - } - else if (!strcmp(type, "listener")) - { - char *service; - char *address; - char *port; - char *protocol; - char *socket; - struct sockaddr_in serv_addr; + /** TODO: consistency check function */ - service = config_get_value(obj->parameters, "service"); - port = config_get_value(obj->parameters, "port"); - address = config_get_value(obj->parameters, "address"); - protocol = config_get_value(obj->parameters, "protocol"); - socket = config_get_value(obj->parameters, "socket"); + hashtable_free(monitorhash); + /** + * error_count += consistency_checks(); + */ - /* if id is not set, do it now */ - if (gateway.id == 0) { - setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address); - gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + (port != NULL ? atoi(port) : 0 + getpid())); - } + if (error_count) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : %d errors where encountered processing the " + "configuration file '%s'.", + error_count, + config_file))); + return 0; + } - if(service && protocol && (socket || port)) - { - if (socket) - { - CONFIG_CONTEXT *ptr = context; - while (ptr && strcmp(ptr->object, service) != 0) - ptr = ptr->next; - if (ptr && ptr->element) - { - serviceAddProtocol(ptr->element, - protocol, - socket, - 0); - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Listener '%s', " - "service '%s' not found. " - "Listener will not execute for socket %s.", - obj->object, service, socket))); - error_count++; - } - } - - if (port) - { - CONFIG_CONTEXT *ptr = context; - while (ptr && strcmp(ptr->object, service) != 0) - ptr = ptr->next; - if (ptr && ptr->element) - { - serviceAddProtocol(ptr->element, - protocol, - address, - atoi(port)); - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Listener '%s', " - "service '%s' not found. " - "Listener will not execute.", - obj->object, service))); - error_count++; - } - } - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Listener '%s' is missing a " - "required " - "parameter. A Listener must have a " - "service, port and protocol defined.", - obj->object))); - error_count++; - } - } - else if (!strcmp(type, "monitor")) - { - char *module; - char *servers; - char *user; - char *passwd; - unsigned long interval = 0; - int connect_timeout = 0; - int read_timeout = 0; - int write_timeout = 0; - - module = config_get_value(obj->parameters, "module"); - servers = config_get_value(obj->parameters, "servers"); - user = config_get_value(obj->parameters, "user"); - passwd = config_get_value(obj->parameters, "passwd"); - if (config_get_value(obj->parameters, "monitor_interval")) { - interval = strtoul(config_get_value(obj->parameters, "monitor_interval"), NULL, 10); - } - - if (config_get_value(obj->parameters, "backend_connect_timeout")) { - connect_timeout = atoi(config_get_value(obj->parameters, "backend_connect_timeout")); - } - if (config_get_value(obj->parameters, "backend_read_timeout")) { - read_timeout = atoi(config_get_value(obj->parameters, "backend_read_timeout")); - } - if (config_get_value(obj->parameters, "backend_write_timeout")) { - write_timeout = atoi(config_get_value(obj->parameters, "backend_write_timeout")); - } - - if (module) - { - obj->element = monitor_alloc(obj->object, module); - if (servers && obj->element) - { - char *s, *lasts; - - /* if id is not set, compute it now with pid only */ - if (gateway.id == 0) { - gateway.id = getpid(); - } - - monitorStart(obj->element,obj->parameters); - - /* set monitor interval */ - if (interval > 0) - monitorSetInterval(obj->element, interval); - else - skygw_log_write(LOGFILE_ERROR,"Warning: Monitor '%s' " - "missing monitor_interval parameter, " - "default value of 10000 miliseconds.",obj->object); - - /* set timeouts */ - if (connect_timeout > 0) - monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, connect_timeout); - if (read_timeout > 0) - monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, read_timeout); - if (write_timeout > 0) - monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout); - - /* get the servers to monitor */ - s = strtok_r(servers, ",", &lasts); - while (s) - { - CONFIG_CONTEXT *obj1 = context; - int found = 0; - while (obj1) - { - if (strcmp(trim(s), obj1->object) == 0 && - obj->element && obj1->element) - { - found = 1; - if(hashtable_add(monitorhash,obj1->object,"") == 0) - { - skygw_log_write(LOGFILE_ERROR, - "Warning: Multiple monitors are monitoring server [%s]. " - "This will cause undefined behavior.", - obj1->object); - } - monitorAddServer( - obj->element, - obj1->element); - } - obj1 = obj1->next; - } - if (!found) - LOGIF(LE, - (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Unable to find " - "server '%s' that is " - "configured in the " - "monitor '%s'.", - s, obj->object))); - - s = strtok_r(NULL, ",", &lasts); - } - } - if (obj->element && user && passwd) - { - monitorAddUser(obj->element, - user, - passwd); - check_monitor_permissions(obj->element); - } - else if (obj->element && user) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, "Error: " - "Monitor '%s' defines a " - "username with no password.", - obj->object))); - error_count++; - } - } - else - { - obj->element = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor '%s' is missing a " - "require module parameter.", - obj->object))); - error_count++; - } - } - else if (strcmp(type, "server") != 0 - && strcmp(type, "filter") != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Configuration object '%s' has an " - "invalid type specified.", - obj->object))); - error_count++; - } - - obj = obj->next; - } /*< while */ - /** TODO: consistency check function */ - - hashtable_free(monitorhash); - /** - * error_count += consistency_checks(); - */ - - if (error_count) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : %d errors where encountered processing the " - "configuration file '%s'.", - error_count, - config_file))); - return 0; - } - return 1; + return 1; } /** * Get the value of a config parameter * - * @param params The linked list of config parameters - * @param name The parameter to return + * @param params The linked list of config parameters + * @param name The parameter to return * @return the parameter value or NULL if not found */ static char * config_get_value(CONFIG_PARAMETER *params, const char *name) { - while (params) - { - if (!strcmp(params->name, name)) - return params->value; - params = params->next; - } - return NULL; + while (params) + { + if (!strcmp(params->name, name)) + { + return params->value; + } + + params = params->next; + } + return NULL; } /** * Get the value of a config parameter as a string * - * @param params The linked list of config parameters - * @param name The parameter to return + * @param params The linked list of config parameters + * @param name The parameter to return * @return the parameter value or null string if not found */ static const char * config_get_value_string(CONFIG_PARAMETER *params, const char *name) { - while (params) - { - if (!strcmp(params->name, name)) - return (const char *)params->value; - params = params->next; - } - return ""; + while (params) + { + if (!strcmp(params->name, name)) + { + return (const char *)params->value; + } + + params = params->next; + } + return ""; } CONFIG_PARAMETER* config_get_param( - CONFIG_PARAMETER* params, - const char* name) + CONFIG_PARAMETER* params, + const char* name) { - while (params) + while (params) + { + if (!strcmp(params->name, name)) { - if (!strcmp(params->name, name)) - return params; - params = params->next; + return params; } - return NULL; + + params = params->next; + } + return NULL; } config_param_type_t config_get_paramtype( - CONFIG_PARAMETER* param) + CONFIG_PARAMETER* param) { - return param->qfd_param_type; + return param->qfd_param_type; } bool config_get_valint( - int* val, - CONFIG_PARAMETER* param, - const char* name, /*< if NULL examine current param only */ - config_param_type_t ptype) -{ - bool succp = false;; - - ss_dassert((ptype == COUNT_TYPE || ptype == PERCENT_TYPE) && param != NULL); - - while (param) - { - if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) - { - switch (ptype) { - case COUNT_TYPE: - *val = param->qfd.valcount; - succp = true; - goto return_succp; - - case PERCENT_TYPE: - *val = param->qfd.valpercent; - succp =true; - goto return_succp; + int* val, + CONFIG_PARAMETER* param, + const char* name, /*< if NULL examine current param only */ + config_param_type_t ptype) +{ + bool succp = false;; - default: - goto return_succp; - } - } - param = param->next; + ss_dassert((ptype == COUNT_TYPE || ptype == PERCENT_TYPE) && param != NULL); + + while (param) + { + if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) + { + switch (ptype) { + case COUNT_TYPE: + *val = param->qfd.valcount; + succp = true; + goto return_succp; + + case PERCENT_TYPE: + *val = param->qfd.valpercent; + succp =true; + goto return_succp; + + default: + goto return_succp; + } } + param = param->next; + } return_succp: - return succp; + return succp; } bool config_get_valbool( - bool* val, - CONFIG_PARAMETER* param, - const char* name, - config_param_type_t ptype) + bool* val, + CONFIG_PARAMETER* param, + const char* name, + config_param_type_t ptype) { - bool succp; - - ss_dassert(ptype == BOOL_TYPE); - ss_dassert(param != NULL); - - if (ptype != BOOL_TYPE || param == NULL) - { - succp = false; - goto return_succp; - } - - while (param) - { - if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) - { - *val = param->qfd.valbool; - succp = true; - goto return_succp; - } - param = param->next; - } - succp = false; - + bool succp; + + ss_dassert(ptype == BOOL_TYPE); + ss_dassert(param != NULL); + + if (ptype != BOOL_TYPE || param == NULL) + { + succp = false; + goto return_succp; + } + + while (param) + { + if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) + { + *val = param->qfd.valbool; + succp = true; + goto return_succp; + } + param = param->next; + } + succp = false; + return_succp: - return succp; - + return succp; } bool config_get_valtarget( - target_t* val, - CONFIG_PARAMETER* param, - const char* name, - config_param_type_t ptype) + target_t* val, + CONFIG_PARAMETER* param, + const char* name, + config_param_type_t ptype) { - bool succp; - - ss_dassert(ptype == SQLVAR_TARGET_TYPE); - ss_dassert(param != NULL); - - if (ptype != SQLVAR_TARGET_TYPE || param == NULL) - { - succp = false; - goto return_succp; - } - - while (param) - { - if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) - { - *val = param->qfd.valtarget; - succp = true; - goto return_succp; - } - param = param->next; - } - succp = false; - + bool succp; + + ss_dassert(ptype == SQLVAR_TARGET_TYPE); + ss_dassert(param != NULL); + + if (ptype != SQLVAR_TARGET_TYPE || param == NULL) + { + succp = false; + goto return_succp; + } + + while (param) + { + if (name == NULL || !strncmp(param->name, name, MAX_PARAM_LEN)) + { + *val = param->qfd.valtarget; + succp = true; + goto return_succp; + } + param = param->next; + } + succp = false; + return_succp: - return succp; - + return succp; } CONFIG_PARAMETER* config_clone_param( - CONFIG_PARAMETER* param) + CONFIG_PARAMETER* param) { - CONFIG_PARAMETER* p2; - - p2 = (CONFIG_PARAMETER*) malloc(sizeof(CONFIG_PARAMETER)); - - if (p2 == NULL) - { - goto return_p2; - } - memcpy(p2, param, sizeof(CONFIG_PARAMETER)); - p2->name = strndup(param->name, MAX_PARAM_LEN); - p2->value = strndup(param->value, MAX_PARAM_LEN); - - if (param->qfd_param_type == STRING_TYPE) - { - p2->qfd.valstr = strndup(param->qfd.valstr, MAX_PARAM_LEN); - } - + CONFIG_PARAMETER* p2; + + p2 = (CONFIG_PARAMETER*) malloc(sizeof(CONFIG_PARAMETER)); + + if (p2 == NULL) + { + goto return_p2; + } + memcpy(p2, param, sizeof(CONFIG_PARAMETER)); + p2->name = strndup(param->name, MAX_PARAM_LEN); + p2->value = strndup(param->value, MAX_PARAM_LEN); + + if (param->qfd_param_type == STRING_TYPE) + { + p2->qfd.valstr = strndup(param->qfd.valstr, MAX_PARAM_LEN); + } + return_p2: - return p2; + return p2; +} + +/** + * Free a configuration parameter + * @param p1 Parameter to free + */ +void free_config_parameter(CONFIG_PARAMETER* p1) +{ + while (p1) + { + free(p1->name); + free(p1->value); + CONFIG_PARAMETER* p2 = p1->next; + free(p1); + p1 = p2; + } } /** * Free a config tree * - * @param context The configuration data + * @param context The configuration data */ -static void +static void free_config_context(CONFIG_CONTEXT *context) { -CONFIG_CONTEXT *obj; -CONFIG_PARAMETER *p1, *p2; + CONFIG_CONTEXT *obj; + CONFIG_PARAMETER *p1, *p2; - while (context) - { - free(context->object); - p1 = context->parameters; - while (p1) - { - free(p1->name); - free(p1->value); - p2 = p1->next; - free(p1); - p1 = p2; - } - obj = context->next; - free(context); - context = obj; - } + while (context) + { + free(context->object); + free_config_parameter(context->parameters); + obj = context->next; + free(context); + context = obj; + } } /** @@ -1497,7 +1547,7 @@ CONFIG_PARAMETER *p1, *p2; int config_threadcount() { - return gateway.n_threads; + return gateway.n_threads; } /** @@ -1509,7 +1559,7 @@ config_threadcount() unsigned int config_nbpolls() { - return gateway.n_nbpoll; + return gateway.n_nbpoll; } /** @@ -1521,7 +1571,7 @@ config_nbpolls() unsigned int config_pollsleep() { - return gateway.pollsleep; + return gateway.pollsleep; } /** @@ -1532,133 +1582,150 @@ config_pollsleep() FEEDBACK_CONF * config_get_feedback_data() { - return &feedback; + return &feedback; } -static struct { - char *logname; - logfile_id_t logfile; +static struct +{ + char *logname; + logfile_id_t logfile; } lognames[] = { - { "log_messages", LOGFILE_MESSAGE }, - { "log_trace", LOGFILE_TRACE }, - { "log_debug", LOGFILE_DEBUG }, - { NULL, 0 } + { "log_messages", LOGFILE_MESSAGE }, + { "log_trace", LOGFILE_TRACE }, + { "log_debug", LOGFILE_DEBUG }, + { NULL, 0 } }; /** * Configuration handler for items in the global [MaxScale] section * - * @param name The item name - * @param value The item value + * @param name The item name + * @param value The item value * @return 0 on error */ -static int +static int handle_global_item(const char *name, const char *value) { -int i; - if (strcmp(name, "threads") == 0) - { - int thrcount = atoi(value); - if (thrcount > 0) - { - gateway.n_threads = thrcount; - } - else - { - skygw_log_write(LE, "Warning: Invalid value for 'threads': %s.", value); - return 0; - } - } - else if (strcmp(name, "non_blocking_polls") == 0) - { - gateway.n_nbpoll = atoi(value); - } - else if (strcmp(name, "poll_sleep") == 0) - { - gateway.pollsleep = atoi(value); + int i; + if (strcmp(name, "threads") == 0) + { + int thrcount = atoi(value); + if (thrcount > 0) + { + gateway.n_threads = thrcount; } - else if (strcmp(name, "ms_timestamp") == 0) - { - skygw_set_highp(config_truth_value((char*)value)); - } + else + { + skygw_log_write(LE, "Warning: Invalid value for 'threads': %s.", value); + return 0; + } + } + else if (strcmp(name, "non_blocking_polls") == 0) + { + gateway.n_nbpoll = atoi(value); + } + else if (strcmp(name, "poll_sleep") == 0) + { + gateway.pollsleep = atoi(value); + } + else if (strcmp(name, "ms_timestamp") == 0) + { + mxs_log_set_highprecision_enabled(config_truth_value((char*)value)); + } else if (strcmp(name, "auth_connect_timeout") == 0) - { + { char* endptr; - int intval = strtol(value, &endptr, 0); - if(*endptr == '\0' && intval > 0) + int intval = strtol(value, &endptr, 0); + if (*endptr == '\0' && intval > 0) + { gateway.auth_conn_timeout = intval; - else - skygw_log_write(LE, "Invalid timeout value for 'auth_connect_timeout': %s", value); - } - else if (strcmp(name, "auth_read_timeout") == 0) - { - char* endptr; - int intval = strtol(value, &endptr, 0); - if(*endptr == '\0' && intval > 0) - gateway.auth_read_timeout = intval; - else - skygw_log_write(LE, "Invalid timeout value for 'auth_read_timeout': %s", value); - } - else if (strcmp(name, "auth_write_timeout") == 0) - { - char* endptr; - int intval = strtol(value, &endptr, 0); - if(*endptr == '\0' && intval > 0) - gateway.auth_write_timeout = intval; - else - skygw_log_write(LE, "Invalid timeout value for 'auth_write_timeout': %s", value); - } - else - { - for (i = 0; lognames[i].logname; i++) - { - if (strcasecmp(name, lognames[i].logname) == 0) - { - if (config_truth_value((char*)value)) - skygw_log_enable(lognames[i].logfile); - else - skygw_log_disable(lognames[i].logfile); - } - } } - return 1; + else + { + skygw_log_write(LE, "Invalid timeout value for 'auth_connect_timeout': %s", value); + } + } + else if (strcmp(name, "auth_read_timeout") == 0) + { + char* endptr; + int intval = strtol(value, &endptr, 0); + if (*endptr == '\0' && intval > 0) + { + gateway.auth_read_timeout = intval; + } + else + { + skygw_log_write(LE, "Invalid timeout value for 'auth_read_timeout': %s", value); + } + } + else if (strcmp(name, "auth_write_timeout") == 0) + { + char* endptr; + int intval = strtol(value, &endptr, 0); + if (*endptr == '\0' && intval > 0) + { + gateway.auth_write_timeout = intval; + } + else + { + skygw_log_write(LE, "Invalid timeout value for 'auth_write_timeout': %s", value); + } + } + else + { + for (i = 0; lognames[i].logname; i++) + { + if (strcasecmp(name, lognames[i].logname) == 0) + { + if (config_truth_value((char*)value)) + { + skygw_log_enable(lognames[i].logfile); + } + else + { + skygw_log_disable(lognames[i].logfile); + } + } + } + } + return 1; } /** * Configuration handler for items in the feedback [feedback] section * - * @param name The item name - * @param value The item value + * @param name The item name + * @param value The item value * @return 0 on error */ -static int +static int handle_feedback_item(const char *name, const char *value) { -int i; - if (strcmp(name, "feedback_enable") == 0) - { - feedback.feedback_enable = config_truth_value((char *)value); - } - else if (strcmp(name, "feedback_user_info") == 0) - { - feedback.feedback_user_info = strdup(value); - } - else if (strcmp(name, "feedback_url") == 0) - { - feedback.feedback_url = strdup(value); - } - if (strcmp(name, "feedback_timeout") == 0) - { - feedback.feedback_timeout = atoi(value); - } - if (strcmp(name, "feedback_connect_timeout") == 0) - { - feedback.feedback_connect_timeout = atoi(value); - } - if (strcmp(name, "feedback_frequency") == 0) - { - feedback.feedback_frequency = atoi(value); - } - return 1; + int i; + if (strcmp(name, "feedback_enable") == 0) + { + feedback.feedback_enable = config_truth_value((char *)value); + } + else if (strcmp(name, "feedback_user_info") == 0) + { + feedback.feedback_user_info = strdup(value); + } + else if (strcmp(name, "feedback_url") == 0) + { + feedback.feedback_url = strdup(value); + } + if (strcmp(name, "feedback_timeout") == 0) + { + feedback.feedback_timeout = atoi(value); + } + if (strcmp(name, "feedback_connect_timeout") == 0) + { + feedback.feedback_connect_timeout = atoi(value); + } + if (strcmp(name, "feedback_frequency") == 0) + { + feedback.feedback_frequency = atoi(value); + } + return 1; } /** @@ -1667,37 +1734,50 @@ int i; static void global_defaults() { - uint8_t mac_addr[6]=""; - struct utsname uname_data; - gateway.n_threads = get_processor_count(); - gateway.n_nbpoll = DEFAULT_NBPOLLS; - gateway.pollsleep = DEFAULT_POLLSLEEP; + uint8_t mac_addr[6]=""; + struct utsname uname_data; + gateway.n_threads = get_processor_count(); + gateway.n_nbpoll = DEFAULT_NBPOLLS; + gateway.pollsleep = DEFAULT_POLLSLEEP; gateway.auth_conn_timeout = DEFAULT_AUTH_CONNECT_TIMEOUT; gateway.auth_read_timeout = DEFAULT_AUTH_READ_TIMEOUT; gateway.auth_write_timeout = DEFAULT_AUTH_WRITE_TIMEOUT; - if (version_string != NULL) - gateway.version_string = strdup(version_string); - else - gateway.version_string = NULL; - gateway.id=0; + if (version_string != NULL) + { + gateway.version_string = strdup(version_string); + } + else + { + gateway.version_string = NULL; + } + gateway.id=0; - /* get release string */ - if(!config_get_release_string(gateway.release_string)) - sprintf(gateway.release_string,"undefined"); + /* get release string */ + if (!config_get_release_string(gateway.release_string)) + { + sprintf(gateway.release_string, "undefined"); + } - /* get first mac_address in SHA1 */ - if(config_get_ifaddr(mac_addr)) { - gw_sha1_str(mac_addr, 6, gateway.mac_sha1); - } else { - memset(gateway.mac_sha1, '\0', sizeof(gateway.mac_sha1)); - memcpy(gateway.mac_sha1, "MAC-undef", 9); - } + /* get first mac_address in SHA1 */ + if (config_get_ifaddr(mac_addr)) + { + gw_sha1_str(mac_addr, 6, gateway.mac_sha1); + } + else + { + memset(gateway.mac_sha1, '\0', sizeof(gateway.mac_sha1)); + memcpy(gateway.mac_sha1, "MAC-undef", 9); + } - /* get uname info */ - if (uname(&uname_data)) - strcpy(gateway.sysname, "undefined"); - else - strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH); + /* get uname info */ + if (uname(&uname_data)) + { + strcpy(gateway.sysname, "undefined"); + } + else + { + strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH); + } } /** @@ -1706,85 +1786,83 @@ global_defaults() static void feedback_defaults() { - feedback.feedback_enable = 0; - feedback.feedback_user_info = NULL; - feedback.feedback_last_action = _NOTIFICATION_SEND_PENDING; - feedback.feedback_timeout = _NOTIFICATION_OPERATION_TIMEOUT; - feedback.feedback_connect_timeout = _NOTIFICATION_CONNECT_TIMEOUT; - feedback.feedback_url = NULL; - feedback.feedback_frequency = 1800; - feedback.release_info = gateway.release_string; - feedback.sysname = gateway.sysname; - feedback.mac_sha1 = gateway.mac_sha1; + feedback.feedback_enable = 0; + feedback.feedback_user_info = NULL; + feedback.feedback_last_action = _NOTIFICATION_SEND_PENDING; + feedback.feedback_timeout = _NOTIFICATION_OPERATION_TIMEOUT; + feedback.feedback_connect_timeout = _NOTIFICATION_CONNECT_TIMEOUT; + feedback.feedback_url = NULL; + feedback.feedback_frequency = 1800; + feedback.release_info = gateway.release_string; + feedback.sysname = gateway.sysname; + feedback.mac_sha1 = gateway.mac_sha1; } /** * Process a configuration context update and turn it into the set of object * we need. * - * @param context The configuration data + * @param context The configuration data */ -static int +static int process_config_update(CONFIG_CONTEXT *context) { -CONFIG_CONTEXT *obj; -SERVICE *service; -SERVER *server; + CONFIG_CONTEXT *obj; + SERVICE *service; + SERVER *server; - /** - * Process the data and create the services and servers defined - * in the data. - */ - obj = context; - while (obj) - { - char *type = config_get_value(obj->parameters, "type"); - if (type == NULL) + /** + * Process the data and create the services and servers defined + * in the data. + */ + obj = context; + while (obj) + { + char *type = config_get_value(obj->parameters, "type"); + if (type == NULL) + { + LOGIF(LE, + (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Configuration object %s has no type.", + obj->object))); + } + else if (!strcmp(type, "service")) + { + char *router = config_get_value(obj->parameters, + "router"); + if (router) + { + if ((service = service_find(obj->object)) != NULL) { - LOGIF(LE, - (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Configuration object %s has no type.", - obj->object))); - } - else if (!strcmp(type, "service")) - { - char *router = config_get_value(obj->parameters, - "router"); - if (router) - { - if ((service = service_find(obj->object)) != NULL) - { - char *user; - char *auth; - char *enable_root_user; + char *user; + char *auth; + char *enable_root_user; - char *connection_timeout; + char *connection_timeout; - char* auth_all_servers; - char* optimize_wildcard; - char* strip_db_esc; - char* max_slave_conn_str; - char* max_slave_rlag_str; - char *version_string; - char *allow_localhost_match_wildcard_host; + char* auth_all_servers; + char* optimize_wildcard; + char* strip_db_esc; + char* max_slave_conn_str; + char* max_slave_rlag_str; + char *version_string; + char *allow_localhost_match_wildcard_host; - enable_root_user = config_get_value(obj->parameters, "enable_root_user"); + enable_root_user = config_get_value(obj->parameters, "enable_root_user"); - connection_timeout = config_get_value(obj->parameters, "connection_timeout"); - user = config_get_value(obj->parameters, - "user"); - auth = config_get_value(obj->parameters, - "passwd"); - - auth_all_servers = config_get_value(obj->parameters, "auth_all_servers"); - optimize_wildcard = config_get_value(obj->parameters, "optimize_wildcard"); - strip_db_esc = config_get_value(obj->parameters, "strip_db_esc"); - version_string = config_get_value(obj->parameters, "version_string"); - allow_localhost_match_wildcard_host = config_get_value(obj->parameters, "localhost_match_wildcard_host"); + connection_timeout = config_get_value(obj->parameters, "connection_timeout"); + user = config_get_value(obj->parameters, "user"); + auth = config_get_value(obj->parameters, "passwd"); - char *log_auth_warnings = config_get_value(obj->parameters, - "log_auth_warnings"); + auth_all_servers = config_get_value(obj->parameters, "auth_all_servers"); + optimize_wildcard = config_get_value(obj->parameters, "optimize_wildcard"); + strip_db_esc = config_get_value(obj->parameters, "strip_db_esc"); + version_string = config_get_value(obj->parameters, "version_string"); + allow_localhost_match_wildcard_host = + config_get_value(obj->parameters, "localhost_match_wildcard_host"); + + char *log_auth_warnings = config_get_value(obj->parameters, "log_auth_warnings"); int truthval; if (log_auth_warnings && (truthval = config_truth_value(log_auth_warnings)) != -1) { @@ -1793,603 +1871,621 @@ SERVER *server; CONFIG_PARAMETER* param; - if((param = config_get_param(obj->parameters, "ignore_databases"))) + if ((param = config_get_param(obj->parameters, "ignore_databases"))) { service_set_param_value(service, param, param->value, 0, STRING_TYPE); } - if((param = config_get_param(obj->parameters, "ignore_databases_regex"))) + if ((param = config_get_param(obj->parameters, "ignore_databases_regex"))) { service_set_param_value(service, param, param->value, 0, STRING_TYPE); } - if (version_string) { - if (service->version_string) { - free(service->version_string); - } - service->version_string = strdup(version_string); - } + if (version_string) + { + if (service->version_string) + { + free(service->version_string); + } + service->version_string = strdup(version_string); + } - if (user && auth) { - service_update(service, router, - user, - auth); - if (enable_root_user) - serviceEnableRootUser(service, config_truth_value(enable_root_user)); + if (user && auth) + { + service_update(service, router, user, auth); + if (enable_root_user) + { + serviceEnableRootUser(service, config_truth_value(enable_root_user)); + } - if (connection_timeout) - serviceSetTimeout(service, config_truth_value(connection_timeout)); + if (connection_timeout) + { + serviceSetTimeout(service, config_truth_value(connection_timeout)); + } + if (auth_all_servers) + { + serviceAuthAllServers(service, config_truth_value(auth_all_servers)); + } + if (optimize_wildcard) + { + serviceOptimizeWildcard(service, config_truth_value(optimize_wildcard)); + } - if(auth_all_servers) - serviceAuthAllServers(service, config_truth_value(auth_all_servers)); - if(optimize_wildcard) - serviceOptimizeWildcard(service, config_truth_value(optimize_wildcard)); - if(strip_db_esc) - serviceStripDbEsc(service, config_truth_value(strip_db_esc)); + if (strip_db_esc) + { + serviceStripDbEsc(service, config_truth_value(strip_db_esc)); + } - if (allow_localhost_match_wildcard_host) - serviceEnableLocalhostMatchWildcardHost( - service, - config_truth_value(allow_localhost_match_wildcard_host)); - - /** Read, validate and set max_slave_connections */ - max_slave_conn_str = - config_get_value( - obj->parameters, - "max_slave_connections"); + if (allow_localhost_match_wildcard_host) + serviceEnableLocalhostMatchWildcardHost( + service, + config_truth_value(allow_localhost_match_wildcard_host)); - if (max_slave_conn_str != NULL) - { - CONFIG_PARAMETER* param; - bool succp; - - param = config_get_param(obj->parameters, - "max_slave_connections"); - - if (param == NULL) - { - succp = false; - } - else - { - succp = service_set_param_value( - service, - param, - max_slave_conn_str, - COUNT_ATMOST, - (PERCENT_TYPE|COUNT_TYPE)); - } - - if (!succp && param != NULL) - { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : invalid value type " - "for parameter \'%s.%s = %s\'\n\tExpected " - "type is either for slave connection " - "count or\n\t%% for specifying the " - "maximum percentage of available the " - "slaves that will be connected.", - ((SERVICE*)obj->element)->name, - param->name, - param->value))); - } - } - /** Read, validate and set max_slave_replication_lag */ - max_slave_rlag_str = - config_get_value(obj->parameters, - "max_slave_replication_lag"); - - if (max_slave_rlag_str != NULL) - { - CONFIG_PARAMETER* param; - bool succp; - - param = config_get_param( - obj->parameters, - "max_slave_replication_lag"); - - if (param == NULL) - { - succp = false; - } - else - { - succp = service_set_param_value( - service, - param, - max_slave_rlag_str, - COUNT_ATMOST, - COUNT_TYPE); - } - - if (!succp) - { - if(param){ - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "* Warning : invalid value type " - "for parameter \'%s.%s = %s\'\n\tExpected " - "type is for maximum " - "slave replication lag.", - ((SERVICE*)obj->element)->name, - param->name, - param->value))); - }else{ - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : parameter was NULL"))); - } - } - } - } + /** Read, validate and set max_slave_connections */ + max_slave_conn_str = + config_get_value(obj->parameters, "max_slave_connections"); - obj->element = service; - } - else - { + if (max_slave_conn_str != NULL) + { + CONFIG_PARAMETER* param; + bool succp; + + param = config_get_param(obj->parameters, + "max_slave_connections"); + + if (param == NULL) + { + succp = false; + } + else + { + succp = service_set_param_value( + service, + param, + max_slave_conn_str, + COUNT_ATMOST, + (PERCENT_TYPE|COUNT_TYPE)); + } + + if (!succp && param != NULL) + { + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : invalid value type " + "for parameter \'%s.%s = %s\'\n\tExpected " + "type is either for slave connection " + "count or\n\t%% for specifying the " + "maximum percentage of available the " + "slaves that will be connected.", + ((SERVICE*)obj->element)->name, + param->name, + param->value))); + } + } + /** Read, validate and set max_slave_replication_lag */ + max_slave_rlag_str = + config_get_value(obj->parameters, "max_slave_replication_lag"); + + if (max_slave_rlag_str != NULL) + { + CONFIG_PARAMETER* param; + bool succp; + + param = config_get_param(obj->parameters, + "max_slave_replication_lag"); + + if (param == NULL) + { + succp = false; + } + else + { + succp = service_set_param_value(service, + param, + max_slave_rlag_str, + COUNT_ATMOST, + COUNT_TYPE); + } + + if (!succp) + { + if(param){ + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "* Warning : invalid value type " + "for parameter \'%s.%s = %s\'\n\tExpected " + "type is for maximum " + "slave replication lag.", + ((SERVICE*)obj->element)->name, + param->name, + param->value))); + } + else + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Error : parameter was NULL"))); + } + } + } + } + + obj->element = service; + } + else + { char *user; - char *auth; - char *enable_root_user; - char *connection_timeout; - char *allow_localhost_match_wildcard_host; + char *auth; + char *enable_root_user; + char *connection_timeout; + char *allow_localhost_match_wildcard_host; - enable_root_user = - config_get_value(obj->parameters, - "enable_root_user"); + enable_root_user = config_get_value(obj->parameters, + "enable_root_user"); - connection_timeout = config_get_value(obj->parameters, + connection_timeout = config_get_value(obj->parameters, "connection_timeout"); - allow_localhost_match_wildcard_host = - config_get_value(obj->parameters, "localhost_match_wildcard_host"); - - user = config_get_value(obj->parameters, - "user"); - auth = config_get_value(obj->parameters, - "passwd"); - obj->element = service_alloc(obj->object, - router); - - if (obj->element && user && auth) - { - serviceSetUser(obj->element, - user, - auth); - if (enable_root_user) - serviceEnableRootUser(obj->element, config_truth_value(enable_root_user)); + allow_localhost_match_wildcard_host = + config_get_value(obj->parameters, "localhost_match_wildcard_host"); - if (connection_timeout) - serviceSetTimeout(obj->element, atoi(connection_timeout)); + user = config_get_value(obj->parameters, "user"); + auth = config_get_value(obj->parameters, "passwd"); + obj->element = service_alloc(obj->object, router); - if (allow_localhost_match_wildcard_host) - serviceEnableLocalhostMatchWildcardHost( - obj->element, - config_truth_value(allow_localhost_match_wildcard_host)); - } - } - } - else - { - obj->element = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : No router defined for service " - "'%s'.", - obj->object))); - } - } - else if (!strcmp(type, "server")) - { - char *address; - char *port; - char *protocol; - char *monuser; - char *monpw; - - address = config_get_value(obj->parameters, "address"); - port = config_get_value(obj->parameters, "port"); - protocol = config_get_value(obj->parameters, "protocol"); - monuser = config_get_value(obj->parameters, - "monitoruser"); - monpw = config_get_value(obj->parameters, "monitorpw"); - - if (address && port && protocol) - { - if ((server = - server_find(address, atoi(port))) != NULL) - { - server_update(server, - protocol, - monuser, - monpw); - obj->element = server; - } - else - { - obj->element = server_alloc(address, - protocol, - atoi(port)); - - server_set_unique_name(obj->element, obj->object); - - if (obj->element && monuser && monpw) - { - serverAddMonUser(obj->element, - monuser, - monpw); - } - } - } - else + if (obj->element && user && auth) + { + serviceSetUser(obj->element, + user, + auth); + if (enable_root_user) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Server '%s' is missing a " - "required " - "configuration parameter. A server must " - "have address, port and protocol " - "defined.", - obj->object))); + serviceEnableRootUser(obj->element, config_truth_value(enable_root_user)); } - } - obj = obj->next; - } - /* - * Now we have the services we can add the servers to the services - * add the protocols to the services - */ - obj = context; - while (obj) - { - char *type = config_get_value(obj->parameters, "type"); - if (type == NULL) - ; - else if (!strcmp(type, "service")) - { - char *servers; - char *roptions; - char *filters; - - servers = config_get_value(obj->parameters, "servers"); - roptions = config_get_value(obj->parameters, - "router_options"); - filters = config_get_value(obj->parameters, "filters"); - if (servers && obj->element) - { - char *lasts; - char *s = strtok_r(servers, ",", &lasts); - while (s) - { - CONFIG_CONTEXT *obj1 = context; - int found = 0; - while (obj1) - { - if (strcmp(trim(s), obj1->object) == 0 && - obj->element && obj1->element) - { - found = 1; - if (!serviceHasBackend(obj->element, obj1->element)) - { - serviceAddBackend( - obj->element, - obj1->element); - } - } - obj1 = obj1->next; - } - if (!found) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Unable to find " - "server '%s' that is " - "configured as part of " - "service '%s'.", - s, obj->object))); - } - s = strtok_r(NULL, ",", &lasts); - } - } - if (roptions && obj->element) - { - char *lasts; - char *s = strtok_r(roptions, ",", &lasts); - serviceClearRouterOptions(obj->element); - while (s) - { - serviceAddRouterOption(obj->element, s); - s = strtok_r(NULL, ",", &lasts); - } - } - if (filters && obj->element) - serviceSetFilters(obj->element, filters); - } - else if (!strcmp(type, "listener")) - { - char *service; - char *port; - char *protocol; - char *address; - char *socket; + if (connection_timeout) + { + serviceSetTimeout(obj->element, atoi(connection_timeout)); + } - service = config_get_value(obj->parameters, "service"); - address = config_get_value(obj->parameters, "address"); - port = config_get_value(obj->parameters, "port"); - protocol = config_get_value(obj->parameters, "protocol"); - socket = config_get_value(obj->parameters, "socket"); + if (allow_localhost_match_wildcard_host) + { + serviceEnableLocalhostMatchWildcardHost( + obj->element, + config_truth_value(allow_localhost_match_wildcard_host)); + } + } + } + } + else + { + obj->element = NULL; + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : No router defined for service " + "'%s'.", + obj->object))); + } + } + else if (!strcmp(type, "server")) + { + char *address; + char *port; + char *protocol; + char *monuser; + char *monpw; - if (service && socket && protocol) - { - CONFIG_CONTEXT *ptr = context; - while (ptr && strcmp(ptr->object, service) != 0) - ptr = ptr->next; - - if (ptr && - ptr->element && - serviceHasProtocol(ptr->element, - protocol, - 0) == 0) - { - serviceAddProtocol(ptr->element, - protocol, - socket, - 0); - serviceStartProtocol(ptr->element, - protocol, - 0); - } - } + address = config_get_value(obj->parameters, "address"); + port = config_get_value(obj->parameters, "port"); + protocol = config_get_value(obj->parameters, "protocol"); + monuser = config_get_value(obj->parameters, "monitoruser"); + monpw = config_get_value(obj->parameters, "monitorpw"); - if (service && port && protocol) - { - CONFIG_CONTEXT *ptr = context; - while (ptr && strcmp(ptr->object, service) != 0) - ptr = ptr->next; - - if (ptr && - ptr->element && - serviceHasProtocol(ptr->element, - protocol, - atoi(port)) == 0) - { - serviceAddProtocol(ptr->element, - protocol, - address, - atoi(port)); - serviceStartProtocol(ptr->element, - protocol, - atoi(port)); - } - } - } - else if (strcmp(type, "server") != 0 && - strcmp(type, "monitor") != 0 && - strcmp(type, "filter") != 0) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Configuration object %s has an invalid " - "type specified.", - obj->object))); - } - obj = obj->next; - } - return 1; + if (address && port && protocol) + { + if ((server = server_find(address, atoi(port))) != NULL) + { + server_update(server, + protocol, + monuser, + monpw); + obj->element = server; + } + else + { + obj->element = server_alloc(address, + protocol, + atoi(port)); + + server_set_unique_name(obj->element, obj->object); + + if (obj->element && monuser && monpw) + { + serverAddMonUser(obj->element, + monuser, + monpw); + } + } + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Server '%s' is missing a " + "required " + "configuration parameter. A server must " + "have address, port and protocol " + "defined.", + obj->object))); + } + } + obj = obj->next; + } + + /* + * Now we have the services we can add the servers to the services + * add the protocols to the services + */ + obj = context; + while (obj) + { + char *type = config_get_value(obj->parameters, "type"); + if (type == NULL) + { + ; + } + else if (!strcmp(type, "service")) + { + char *servers; + char *roptions; + char *filters; + + servers = config_get_value(obj->parameters, "servers"); + roptions = config_get_value(obj->parameters, "router_options"); + filters = config_get_value(obj->parameters, "filters"); + + if (servers && obj->element) + { + char *lasts; + char *s = strtok_r(servers, ",", &lasts); + while (s) + { + CONFIG_CONTEXT *obj1 = context; + int found = 0; + while (obj1) + { + if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element) + { + found = 1; + if (!serviceHasBackend(obj->element, obj1->element)) + { + serviceAddBackend(obj->element, obj1->element); + } + } + + obj1 = obj1->next; + } + if (!found) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Unable to find " + "server '%s' that is " + "configured as part of " + "service '%s'.", + s, obj->object))); + } + s = strtok_r(NULL, ",", &lasts); + } + } + if (roptions && obj->element) + { + char *lasts; + char *s = strtok_r(roptions, ",", &lasts); + serviceClearRouterOptions(obj->element); + while (s) + { + serviceAddRouterOption(obj->element, s); + s = strtok_r(NULL, ",", &lasts); + } + } + if (filters && obj->element) + { + serviceSetFilters(obj->element, filters); + } + } + else if (!strcmp(type, "listener")) + { + char *service; + char *port; + char *protocol; + char *address; + char *socket; + + service = config_get_value(obj->parameters, "service"); + address = config_get_value(obj->parameters, "address"); + port = config_get_value(obj->parameters, "port"); + protocol = config_get_value(obj->parameters, "protocol"); + socket = config_get_value(obj->parameters, "socket"); + + if (service && socket && protocol) + { + CONFIG_CONTEXT *ptr = context; + while (ptr && strcmp(ptr->object, service) != 0) + { + ptr = ptr->next; + } + + if (ptr && + ptr->element && + serviceHasProtocol(ptr->element, protocol, 0) == 0) + { + serviceAddProtocol(ptr->element, protocol, socket, 0); + serviceStartProtocol(ptr->element, protocol, 0); + } + } + + if (service && port && protocol) + { + CONFIG_CONTEXT *ptr = context; + + while (ptr && strcmp(ptr->object, service) != 0) + { + ptr = ptr->next; + } + + if (ptr && + ptr->element && + serviceHasProtocol(ptr->element, protocol, atoi(port)) == 0) + { + serviceAddProtocol(ptr->element, protocol, address, atoi(port)); + serviceStartProtocol(ptr->element, protocol, atoi(port)); + } + } + } + else if (strcmp(type, "server") != 0 && + strcmp(type, "monitor") != 0 && + strcmp(type, "filter") != 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Configuration object %s has an invalid " + "type specified.", + obj->object))); + } + obj = obj->next; + } + + return 1; } static char *service_params[] = - { - "type", - "router", - "router_options", - "servers", - "user", - "passwd", - "enable_root_user", - "connection_timeout", - "auth_all_servers", - "optimize_wildcard", - "strip_db_esc", - "localhost_match_wildcard_host", - "max_slave_connections", - "max_slave_replication_lag", - "use_sql_variables_in", /*< rwsplit only */ - "subservices", - "version_string", - "filters", - "weightby", - "ssl_cert", - "ssl_ca_cert", - "ssl", - "ssl_key", - "ssl_version", - "ssl_cert_verify_depth", - "ignore_databases", - "ignore_databases_regex", - "log_auth_warnings", - NULL - }; +{ + "type", + "router", + "router_options", + "servers", + "user", + "passwd", + "enable_root_user", + "connection_timeout", + "auth_all_servers", + "optimize_wildcard", + "strip_db_esc", + "localhost_match_wildcard_host", + "max_slave_connections", + "max_slave_replication_lag", + "use_sql_variables_in", /*< rwsplit only */ + "subservices", + "version_string", + "filters", + "weightby", + "ssl_cert", + "ssl_ca_cert", + "ssl", + "ssl_key", + "ssl_version", + "ssl_cert_verify_depth", + "ignore_databases", + "ignore_databases_regex", + "log_auth_warnings", + NULL +}; static char *listener_params[] = - { - "type", - "service", - "protocol", - "port", - "address", - "socket", - NULL - }; +{ + "type", + "service", + "protocol", + "port", + "address", + "socket", + NULL +}; static char *monitor_params[] = - { - "type", - "module", - "servers", - "user", - "passwd", - "script", - "events", - "mysql51_replication", - "monitor_interval", - "detect_replication_lag", - "detect_stale_master", - "disable_master_failback", - "backend_connect_timeout", - "backend_read_timeout", - "backend_write_timeout", - "available_when_donor", - "disable_master_role_setting", - NULL - }; +{ + "type", + "module", + "servers", + "user", + "passwd", + "script", + "events", + "mysql51_replication", + "monitor_interval", + "detect_replication_lag", + "detect_stale_master", + "disable_master_failback", + "backend_connect_timeout", + "backend_read_timeout", + "backend_write_timeout", + "available_when_donor", + "disable_master_role_setting", + NULL +}; /** * Check the configuration objects have valid parameters */ static void check_config_objects(CONFIG_CONTEXT *context) { -CONFIG_CONTEXT *obj; -CONFIG_PARAMETER *params; -char *type, **param_set; -int i; + CONFIG_CONTEXT *obj; + CONFIG_PARAMETER *params; + char *type, **param_set; + int i; - /** - * Process the data and create the services and servers defined - * in the data. - */ - obj = context; - while (obj) - { - param_set = NULL; - if (obj->parameters && - (type = config_get_value(obj->parameters, "type"))) - { - if (!strcmp(type, "service")) - param_set = service_params; - else if (!strcmp(type, "listener")) - param_set = listener_params; - else if (!strcmp(type, "monitor")) - param_set = monitor_params; - } - if (param_set != NULL) - { - params = obj->parameters; - while (params) - { - int found = 0; - for (i = 0; param_set[i]; i++) - if (!strcmp(params->name, param_set[i])) - found = 1; - if (found == 0) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unexpected parameter " - "'%s' for object '%s' of type " - "'%s'.", - params->name, - obj->object, - type))); - params = params->next; - } - } - obj = obj->next; - } + /** + * Process the data and create the services and servers defined + * in the data. + */ + obj = context; + while (obj) + { + param_set = NULL; + if (obj->parameters && (type = config_get_value(obj->parameters, "type"))) + { + if (!strcmp(type, "service")) + { + param_set = service_params; + } + else if (!strcmp(type, "listener")) + { + param_set = listener_params; + } + else if (!strcmp(type, "monitor")) + { + param_set = monitor_params; + } + } + + if (param_set != NULL) + { + params = obj->parameters; + while (params) + { + int found = 0; + for (i = 0; param_set[i]; i++) + { + if (!strcmp(params->name, param_set[i])) + { + found = 1; + } + } + + if (found == 0) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Unexpected parameter " + "'%s' for object '%s' of type " + "'%s'.", + params->name, + obj->object, + type))); + } + params = params->next; + } + } + obj = obj->next; + } } /** * Set qualified parameter value to CONFIG_PARAMETER struct. */ -bool config_set_qualified_param( - CONFIG_PARAMETER* param, - void* val, - config_param_type_t type) +bool config_set_qualified_param(CONFIG_PARAMETER* param, + void* val, + config_param_type_t type) { - bool succp; - - switch (type) { - case STRING_TYPE: - param->qfd.valstr = strndup((const char *)val, MAX_PARAM_LEN); - succp = true; - break; + bool succp; - case COUNT_TYPE: - param->qfd.valcount = *(int *)val; - succp = true; - break; - - case PERCENT_TYPE: - param->qfd.valpercent = *(int *)val; - succp = true; - break; - - case BOOL_TYPE: - param->qfd.valbool = *(bool *)val; - succp = true; - break; + switch (type) + { + case STRING_TYPE: + param->qfd.valstr = strndup((const char *)val, MAX_PARAM_LEN); + succp = true; + break; - case SQLVAR_TARGET_TYPE: - param->qfd.valtarget = *(target_t *)val; - succp = true; - break; - default: - succp = false; - break; - } - - if (succp) - { - param->qfd_param_type = type; - } - return succp; + case COUNT_TYPE: + param->qfd.valcount = *(int *)val; + succp = true; + break; + + case PERCENT_TYPE: + param->qfd.valpercent = *(int *)val; + succp = true; + break; + + case BOOL_TYPE: + param->qfd.valbool = *(bool *)val; + succp = true; + break; + + case SQLVAR_TARGET_TYPE: + param->qfd.valtarget = *(target_t *)val; + succp = true; + break; + default: + succp = false; + break; + } + + if (succp) + { + param->qfd_param_type = type; + } + return succp; } /** * Used for boolean settings where values may be 1, yes or true * to enable a setting or -, no, false to disable a setting. * - * @param str String to convert to a boolean - * @return Truth value + * @param str String to convert to a boolean + * @return Truth value */ int config_truth_value(char *str) { - if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 || - strcasecmp(str, "yes") == 0 || strcasecmp(str, "1") == 0) - { - return 1; - } - if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0 || - strcasecmp(str, "no") == 0|| strcasecmp(str, "0") == 0) - { - return 0; - } - skygw_log_write(LOGFILE_ERROR,"Error: Not a boolean value: %s",str); - return -1; + if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 || + strcasecmp(str, "yes") == 0 || strcasecmp(str, "1") == 0) + { + return 1; + } + if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0 || + strcasecmp(str, "no") == 0|| strcasecmp(str, "0") == 0) + { + return 0; + } + skygw_log_write(LOGFILE_ERROR, "Error: Not a boolean value: %s", str); + return -1; } /** * Converts a string into a floating point representation of a percentage value. * For example 75% is converted to 0.75 and -10% is converted to -0.1. - * @param str String to convert - * @return String converted to a floating point percentage + * @param str String to convert + * @return String converted to a floating point percentage */ double config_percentage_value(char *str) { double value = 0; - value = strtod(str,NULL); - if(value != 0) - value /= 100.0; + value = strtod(str, NULL); + if (value != 0) + { + value /= 100.0; + } return value; } -static char *InternalRouters[] = { +static char *InternalRouters[] = +{ "debugcli", "cli", "maxinfo", @@ -2402,251 +2498,292 @@ static char *InternalRouters[] = { * Determine if the router is one of the special internal services that * MaxScale offers. * - * @param router The router name - * @return Non-zero if the router is in the InternalRouters table + * @param router The router name + * @return Non-zero if the router is in the InternalRouters table */ bool isInternalService(char *router) { - if (router) - { - for (int i = 0; InternalRouters[i]; i++) - if (strcmp(router, InternalRouters[i]) == 0) - return true; - } - return false; + if (router) + { + for (int i = 0; InternalRouters[i]; i++) + { + if (strcmp(router, InternalRouters[i]) == 0) + { + return true; + } + } + } + return false; } /** * Get the MAC address of first network interface * * and fill the provided allocated buffer with SHA1 encoding - * @param output Allocated 6 bytes buffer + * @param output Allocated 6 bytes buffer * @return 1 on success, 0 on failure * */ int config_get_ifaddr(unsigned char *output) { - struct ifreq ifr; - struct ifconf ifc; - char buf[1024]; - struct ifreq* it; - struct ifreq* end; - int success = 0; + struct ifreq ifr; + struct ifconf ifc; + char buf[1024]; + struct ifreq* it; + struct ifreq* end; + int success = 0; - int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sock == -1) { - return 0; - }; + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) + { + return 0; + } - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { - close(sock); - return 0; - } + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) + { + close(sock); + return 0; + } - it = ifc.ifc_req; - end = it + (ifc.ifc_len / sizeof(struct ifreq)); + it = ifc.ifc_req; + end = it + (ifc.ifc_len / sizeof(struct ifreq)); - for (; it != end; ++it) { - strcpy(ifr.ifr_name, it->ifr_name); - if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { - if (! (ifr.ifr_flags & IFF_LOOPBACK)) { /* don't count loopback */ - if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { - success = 1; - break; - } - } - } else { - close(sock); - return 0; - } - } + for (; it != end; ++it) + { + strcpy(ifr.ifr_name, it->ifr_name); - if (success) - memcpy(output, ifr.ifr_hwaddr.sa_data, 6); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) + { + if (!(ifr.ifr_flags & IFF_LOOPBACK)) + { /* don't count loopback */ + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) + { + success = 1; + break; + } + } + } + else + { + close(sock); + return 0; + } + } - return success; + if (success) + { + memcpy(output, ifr.ifr_hwaddr.sa_data, 6); + } + + return success; } /** * Get the linux distribution info * - * @param release The allocated buffer where - * the found distribution is copied into. + * @param release The allocated buffer where + * the found distribution is copied into. * @return 1 on success, 0 on failure * */ int config_get_release_string(char* release) { - const char *masks[]= { - "/etc/*-version", "/etc/*-release", - "/etc/*_version", "/etc/*_release" - }; + const char *masks[] = + { + "/etc/*-version", "/etc/*-release", + "/etc/*_version", "/etc/*_release" + }; - bool have_distribution; - char distribution[_RELEASE_STR_LENGTH]=""; - int fd; - int i; - char *to; + bool have_distribution; + char distribution[_RELEASE_STR_LENGTH]=""; + int fd; + int i; + char *to; - have_distribution= false; + have_distribution= false; - /* get data from lsb-release first */ - if ((fd= open("/etc/lsb-release", O_RDONLY)) != -1) - { - /* LSB-compliant distribution! */ - size_t len= read(fd, (char*)distribution, sizeof(distribution)-1); - close(fd); - if (len != (size_t)-1) - { - distribution[len]= 0; - char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); - if (found) - { - have_distribution = true; - char *end = strstr(found, "\n"); - if (end == NULL) - end = distribution + len; - found += 20; + /* get data from lsb-release first */ + if ((fd = open("/etc/lsb-release", O_RDONLY)) != -1) + { + /* LSB-compliant distribution! */ + size_t len = read(fd, (char*)distribution, sizeof(distribution) - 1); + close(fd); - if (*found == '"' && end[-1] == '"') - { - found++; - end--; - } - *end = 0; + if (len != (size_t)-1) + { + distribution[len]= 0; - to = strcpy(distribution, "lsb: "); - memmove(to, found, end - found + 1 < INT_MAX ? end - found + 1 : INT_MAX); + char *found= strstr(distribution, "DISTRIB_DESCRIPTION="); - strncpy(release, to, _RELEASE_STR_LENGTH); + if (found) + { + have_distribution = true; + char *end = strstr(found, "\n"); + if (end == NULL) + { + end = distribution + len; + } + found += 20; - return 1; - } - } - } + if (*found == '"' && end[-1] == '"') + { + found++; + end--; + } + *end = 0; - /* if not an LSB-compliant distribution */ - for (i= 0; !have_distribution && i < 4; i++) - { - glob_t found; - char *new_to; + to = strcpy(distribution, "lsb: "); + memmove(to, found, end - found + 1 < INT_MAX ? end - found + 1 : INT_MAX); - if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) - { - int fd; - int k = 0; - int skipindex = 0; - int startindex = 0; + strncpy(release, to, _RELEASE_STR_LENGTH); - for (k = 0; k< found.gl_pathc; k++) { - if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) { - skipindex = k; - } - } + return 1; + } + } + } - if ( skipindex == 0) - startindex++; + /* if not an LSB-compliant distribution */ + for (i = 0; !have_distribution && i < 4; i++) + { + glob_t found; + char *new_to; - if ((fd= open(found.gl_pathv[startindex], O_RDONLY)) != -1) - { - /* - +5 and -8 below cut the file name part out of the - full pathname that corresponds to the mask as above. - */ - new_to = strncpy(distribution, found.gl_pathv[0] + 5,_RELEASE_STR_LENGTH - 1); - new_to += 8; - *new_to++ = ':'; - *new_to++ = ' '; + if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0) + { + int fd; + int k = 0; + int skipindex = 0; + int startindex = 0; - size_t to_len= distribution + sizeof(distribution) - 1 - new_to; - size_t len= read(fd, (char*)new_to, to_len); + for (k = 0; k< found.gl_pathc; k++) + { + if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) + { + skipindex = k; + } + } - close(fd); + if (skipindex == 0) + startindex++; - if (len != (size_t)-1) - { - new_to[len]= 0; - char *end= strstr(new_to, "\n"); - if (end) - *end= 0; + if ((fd = open(found.gl_pathv[startindex], O_RDONLY)) != -1) + { + /* + +5 and -8 below cut the file name part out of the + full pathname that corresponds to the mask as above. + */ + new_to = strncpy(distribution, found.gl_pathv[0] + 5, _RELEASE_STR_LENGTH - 1); + new_to += 8; + *new_to++ = ':'; + *new_to++ = ' '; - have_distribution= true; - strncpy(release, new_to, _RELEASE_STR_LENGTH); - } - } - } - globfree(&found); - } + size_t to_len = distribution + sizeof(distribution) - 1 - new_to; + size_t len = read(fd, (char*)new_to, to_len); - if (have_distribution) - return 1; - else - return 0; + close(fd); + + if (len != (size_t)-1) + { + new_to[len]= 0; + char *end= strstr(new_to, "\n"); + if (end) + { + *end= 0; + } + + have_distribution= true; + strncpy(release, new_to, _RELEASE_STR_LENGTH); + } + } + } + globfree(&found); + } + + if (have_distribution) + { + return 1; + } + else + { + return 0; + } } /** * Add the 'send_feedback' task to the task list */ void -config_enable_feedback_task(void) { - FEEDBACK_CONF *cfg = config_get_feedback_data(); - int url_set = 0; - int user_info_set = 0; - int enable_set = cfg->feedback_enable; +config_enable_feedback_task(void) +{ + FEEDBACK_CONF *cfg = config_get_feedback_data(); + int url_set = 0; + int user_info_set = 0; + int enable_set = cfg->feedback_enable; - url_set = cfg->feedback_url != NULL && strlen(cfg->feedback_url); - user_info_set = cfg->feedback_user_info != NULL && strlen(cfg->feedback_user_info); + url_set = cfg->feedback_url != NULL && strlen(cfg->feedback_url); + user_info_set = cfg->feedback_user_info != NULL && strlen(cfg->feedback_user_info); - if (enable_set && url_set && user_info_set) { - /* Add the task to the tasl list */ - if (hktask_add("send_feedback", module_feedback_send, cfg, cfg->feedback_frequency)) { - - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Notification service feedback task started: URL=%s, User-Info=%s, Frequency %u seconds", - cfg->feedback_url, - cfg->feedback_user_info, - cfg->feedback_frequency))); - } - } else { - if (enable_set) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Notification service feedback cannot start: feedback_enable=1 but" - " some required parameters are not set: %s%s%s", - url_set == 0 ? "feedback_url is not set" : "", (user_info_set == 0 && url_set == 0) ? ", " : "", user_info_set == 0 ? "feedback_user_info is not set" : ""))); - } else { - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "Notification service feedback is not enabled"))); - } - } + if (enable_set && url_set && user_info_set) + { + /* Add the task to the tasl list */ + if (hktask_add("send_feedback", module_feedback_send, cfg, cfg->feedback_frequency)) + { + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "Notification service feedback task started: URL=%s, User-Info=%s, " + "Frequency %u seconds", + cfg->feedback_url, + cfg->feedback_user_info, + cfg->feedback_frequency))); + } + } + else + { + if (enable_set) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error: Notification service feedback cannot start: feedback_enable=1 but" + " some required parameters are not set: %s%s%s", + url_set == 0 ? "feedback_url is not set" : "", + (user_info_set == 0 && url_set == 0) ? ", " : "", + user_info_set == 0 ? "feedback_user_info is not set" : ""))); + } + else + { + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, + "Notification service feedback is not enabled"))); + } + } } /** * Remove the 'send_feedback' task */ void -config_disable_feedback_task(void) { - hktask_remove("send_feedback"); +config_disable_feedback_task(void) +{ + hktask_remove("send_feedback"); } -unsigned long config_get_gateway_id() +unsigned long config_get_gateway_id() { return gateway.id; } -void config_add_param(CONFIG_CONTEXT* obj, char* key,char* value) + +void config_add_param(CONFIG_CONTEXT* obj, char* key, char* value) { CONFIG_PARAMETER* nptr = malloc(sizeof(CONFIG_PARAMETER)); - if(nptr == NULL) + if (nptr == NULL) { - skygw_log_write(LOGFILE_ERROR,"Memory allocation failed when adding configuration parameters"); - return; + skygw_log_write(LOGFILE_ERROR, "Memory allocation failed when adding configuration parameters"); + return; } nptr->name = strdup(key); @@ -2682,8 +2819,7 @@ bool config_has_duplicate_sections(const char* config) int size = 1024; char *buffer = malloc(size * sizeof(char)); - if (buffer && hash && re && - (mdata = pcre2_match_data_create_from_pattern(re, NULL))) + if (buffer && hash && re && (mdata = pcre2_match_data_create_from_pattern(re, NULL))) { hashtable_memory_fns(hash, (HASHMEMORYFN) strdup, NULL, (HASHMEMORYFN) free, NULL); diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 05334090d..593894158 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -90,12 +90,28 @@ ON user.user=db.user AND user.host=db.host \ WHERE user.user IS NOT NULL" MYSQL_USERS_WITH_DB_ORDER +#define LOAD_MYSQL57_USERS_WITH_DB_QUERY "SELECT \ + user.user AS user, \ + user.host AS host, \ + user.authentication_string AS password, \ + concat(user.user,user.host,user.authentication_string,user.Select_priv,IFNULL(db,'')) AS userdata, \ + user.Select_priv AS anydb, \ + db.db AS db \ + FROM mysql.user LEFT JOIN mysql.db \ + ON user.user=db.user AND user.host=db.host \ + WHERE user.user IS NOT NULL" MYSQL_USERS_WITH_DB_ORDER + #define MYSQL_USERS_WITH_DB_COUNT "SELECT COUNT(1) AS nusers_db FROM (" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS tbl_count" +#define MYSQL57_USERS_WITH_DB_COUNT "SELECT COUNT(1) AS nusers_db FROM (" LOAD_MYSQL57_USERS_WITH_DB_QUERY ") AS tbl_count" #define LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT "SELECT * \ FROM (" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS t1 \ WHERE user NOT IN ('root')" MYSQL_USERS_WITH_DB_ORDER +#define LOAD_MYSQL57_USERS_WITH_DB_QUERY_NO_ROOT "SELECT * \ + FROM (" LOAD_MYSQL57_USERS_WITH_DB_QUERY ") AS t1 \ + WHERE user NOT IN ('root')" MYSQL_USERS_WITH_DB_ORDER + #define LOAD_MYSQL_DATABASE_NAMES "SELECT * \ FROM ( (SELECT COUNT(1) AS ndbs \ FROM INFORMATION_SCHEMA.SCHEMATA) AS tbl1, \ @@ -133,6 +149,39 @@ int add_wildcard_users(USERS *users, static int gw_mysql_set_timeouts(MYSQL* handle); +/** + * Get the user data query. + * @param server_version Server version string + * @param include_root Include root user + * @return Users query + */ +const char* get_mysql_users_query(char* server_version, bool include_root) +{ + const char* rval; + if (strstr(server_version, "5.7.")) + { + rval = include_root ? LOAD_MYSQL57_USERS_WITH_DB_QUERY : + LOAD_MYSQL57_USERS_WITH_DB_QUERY_NO_ROOT; + } + else + { + rval = include_root ? LOAD_MYSQL_USERS_WITH_DB_QUERY : + LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT; + } + return rval; +} + +/** + * Get the user count query. + * @param server_version Server version string + * @return User vount query + * */ +const char* get_mysq_users_db_count_query(char* server_version) +{ + return strstr(server_version, "5.7.") ? + MYSQL57_USERS_WITH_DB_COUNT : MYSQL_USERS_WITH_DB_COUNT; +} + /** * Check if the IP address of the user matches the one in the grant. This assumes * that the grant has one or more single-character wildcards in it. @@ -651,7 +700,8 @@ getAllUsers(SERVICE *service, USERS *users) char *dpwd = NULL; int total_users = 0; SERVER_REF *server; - char *users_query, *tmp; + const char *users_query; + char *tmp; unsigned char hash[SHA_DIGEST_LENGTH]=""; char *users_data = NULL; char *final_data = NULL; @@ -825,9 +875,19 @@ getAllUsers(SERVICE *service, USERS *users) mysql_close(con); goto cleanup; } - + + if (server->server->server_string == NULL) + { + const char *server_string = mysql_get_server_info(con); + if (!server_set_version_string(server->server, server_string)) + { + mysql_close(con); + goto cleanup; + } + } /** Count users. Start with users and db grants for users */ - if (mysql_query(con, MYSQL_USERS_WITH_DB_COUNT)) { + const char *user_with_db_count = get_mysq_users_db_count_query(server->server->server_string); + if (mysql_query(con, user_with_db_count)) { if (mysql_errno(con) != ER_TABLEACCESS_DENIED_ERROR) { /* This is an error we cannot handle, return */ LOGIF(LE, (skygw_log_write_flush( @@ -883,13 +943,10 @@ getAllUsers(SERVICE *service, USERS *users) mysql_close(con); goto cleanup; } - - if(service->enable_root) { - /* enable_root for MySQL protocol module means load the root user credentials from backend databases */ - users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY; - } else { - users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT; - } + + users_query = get_mysql_users_query(server->server->server_string, + service->enable_root); + /* send first the query that fetches users and db grants */ if (mysql_query(con, users_query)) { @@ -1197,7 +1254,7 @@ getUsers(SERVICE *service, USERS *users) char *dpwd; int total_users = 0; SERVER_REF *server; - char *users_query; + const char *users_query; unsigned char hash[SHA_DIGEST_LENGTH]=""; char *users_data = NULL; int nusers = 0; @@ -1343,10 +1400,21 @@ getUsers(SERVICE *service, USERS *users) service->name))); mysql_close(con); return -1; - } + } + if (server->server->server_string == NULL) + { + const char *server_string = mysql_get_server_info(con); + if (!server_set_version_string(server->server, server_string)) + { + mysql_close(con); + return -1; + } + } + + const char *user_with_db_count = get_mysq_users_db_count_query(server->server->server_string); /** Count users. Start with users and db grants for users */ - if (mysql_query(con, MYSQL_USERS_WITH_DB_COUNT)) { + if (mysql_query(con, user_with_db_count)) { if (mysql_errno(con) != ER_TABLEACCESS_DENIED_ERROR) { /* This is an error we cannot handle, return */ LOGIF(LE, (skygw_log_write_flush( @@ -1403,13 +1471,8 @@ getUsers(SERVICE *service, USERS *users) return -1; } - if(service->enable_root) { - /* enable_root for MySQL protocol module means load the root user credentials from backend databases */ - users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY; - } else { - users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT; - } - + users_query = get_mysql_users_query(server->server->server_string, + service->enable_root); /* send first the query that fetches users and db grants */ if (mysql_query(con, users_query)) { /* diff --git a/server/core/dcb.c b/server/core/dcb.c index f19bde6cd..7dafccfd9 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1052,7 +1052,7 @@ int dcb_read_SSL(DCB *dcb, if (GWBUF_LENGTH(buffer) != n) { - skygw_log_sync_all(); + mxs_log_flush_sync(); } ss_info_dassert((buffer->start <= buffer->end), "Buffer start has passed end."); diff --git a/server/core/externcmd.c b/server/core/externcmd.c index 8e8735ac3..ee6e2d28a 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -6,15 +6,17 @@ * @param argv Array of char pointers to be filled with tokenized arguments * @return 0 on success, -1 on error */ -int tokenize_arguments(char* args, char** argv) +int tokenize_arguments(char* argstr, char** argv) { int i = 0; bool quoted = false; bool read = false; bool escaped = false; char *ptr,*start; + char args[strlen(argstr)]; char qc; + strcpy(args, argstr); start = args; ptr = start; @@ -80,36 +82,40 @@ int tokenize_arguments(char* args, char** argv) */ EXTERNCMD* externcmd_allocate(char* argstr) { - EXTERNCMD* cmd; + EXTERNCMD* cmd = (EXTERNCMD*) malloc(sizeof(EXTERNCMD)); + char** argv = (char**) malloc(sizeof(char*) * MAXSCALE_EXTCMD_ARG_MAX); - if(argstr == NULL) - return NULL; - - if((cmd = (EXTERNCMD*)malloc(sizeof(EXTERNCMD))) != NULL) + if (argstr && cmd && argv) { - if(tokenize_arguments(argstr,cmd->parameters) == -1) - { - free(cmd); - return NULL; - } - if(access(cmd->parameters[0],F_OK) != 0) - { - skygw_log_write(LE, - "Error: Cannot find file: %s", - cmd->parameters[0]); - externcmd_free(cmd); - return NULL; - } - - if(access(cmd->parameters[0],X_OK) != 0) - { - skygw_log_write(LE, - "Error: Cannot execute file '%s'. Missing execution permissions.", - cmd->parameters[0]); - externcmd_free(cmd); - return NULL; - } - skygw_log_write(LT, "Executing script %s.", cmd->parameters[0]); + cmd->argv = argv; + if (tokenize_arguments(argstr, cmd->argv) == 0) + { + if (access(cmd->argv[0], X_OK) != 0) + { + if (access(cmd->argv[0], F_OK) != 0) + { + skygw_log_write(LE, "Cannot find file: %s", cmd->argv[0]); + } + else + { + skygw_log_write(LE, "Cannot execute file '%s'. Missing " + "execution permissions.", cmd->argv[0]); + } + externcmd_free(cmd); + cmd = NULL; + } + } + else + { + externcmd_free(cmd); + cmd = NULL; + } + } + else + { + free(cmd); + free(argv); + cmd = NULL; } return cmd; } @@ -120,13 +126,15 @@ EXTERNCMD* externcmd_allocate(char* argstr) */ void externcmd_free(EXTERNCMD* cmd) { - int i; - - for(i = 0;cmd->parameters[i] != NULL;i++) + if (cmd) { - free(cmd->parameters[i]); + for (int i = 0; cmd->argv[i]; i++) + { + free(cmd->argv[i]); + } + free(cmd->argv); + free(cmd); } - free(cmd); } /** @@ -138,29 +146,141 @@ int externcmd_execute(EXTERNCMD* cmd) { int rval = 0; pid_t pid; - + pid = fork(); - if(pid < 0) + if (pid < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s", - cmd->parameters[0],errno,strerror_r(errno, errbuf, sizeof(errbuf))); + skygw_log_write(LOGFILE_ERROR, "Failed to execute command '%s', fork failed: [%d] %s", + cmd->argv[0], errno, strerror_r(errno, errbuf, sizeof(errbuf))); rval = -1; } - else if(pid == 0) + else if (pid == 0) { /** Child process, execute command */ - execvp(cmd->parameters[0],cmd->parameters); - _exit(1); + execvp(cmd->argv[0], cmd->argv); + _exit(1); } else { - cmd->child = pid; - cmd->n_exec++; - LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd)); + cmd->child = pid; + cmd->n_exec++; + LOGIF(LD, skygw_log_write(LD, "[monitor_exec_cmd] Forked child process %d : %s.", pid, cmd)); } return rval; } +/** + * Substitute all occurrences of @c match with @c replace in the arguments for @c cmd. + * @param cmd External command + * @param match Match string + * @param replace Replacement string + * @return true if replacement was successful, false on error + */ +bool externcmd_substitute_arg(EXTERNCMD* cmd, const char* match, const char* replace) +{ + int err; + bool rval = true; + size_t errpos; + pcre2_code *re = pcre2_compile((PCRE2_SPTR) match, PCRE2_ZERO_TERMINATED, 0, &err, &errpos, NULL); + if (re) + { + for (int i = 0; cmd->argv[i] && rval; i++) + { + size_t size = strlen(cmd->argv[i]); + char* dest = malloc(size); + if (dest) + { + mxs_pcre2_result_t rc = mxs_pcre2_substitute(re, cmd->argv[i], replace, &dest, &size); + switch (rc) + { + case MXS_PCRE2_ERROR: + free(dest); + rval = false; + break; + case MXS_PCRE2_MATCH: + free(cmd->argv[i]); + cmd->argv[i] = dest; + break; + case MXS_PCRE2_NOMATCH: + free(dest); + break; + } + } + } + } + else + { + rval = false; + } + return rval; +} + +/** + * Get the name of the command being executed. + * + * This copies the command being executed into a new string. + * @param str Command string, optionally with arguments + * @return Command part of the string if arguments were defined + */ +char* get_command(const char* str) +{ + char* rval = NULL; + const char* start = str; + + while (*start && isspace(*start)) + { + start++; + } + + const char* end = start; + + while (*end && !isspace(*end)) + { + end++; + } + + size_t len = end - start; + + if (len > 0 && (rval = malloc(len + 1))) + { + memcpy(rval, start, len); + rval[len] = '\0'; + } + + return rval; +} + +/** + * Check if a command can be executed. + * + * Checks if the file being executed exists and if the current user has execution + * permissions on the file. + * @param argstr Command to check. Can contain arguments for the command. + * @return True if the file was found and the use has execution permissions to it. + */ +bool externcmd_can_execute(const char* argstr) +{ + bool rval = false; + char *command = get_command(argstr); + + if (command) + { + if (access(command, X_OK) == 0) + { + rval = true; + } + else if (access(command, F_OK) == 0) + { + skygw_log_write(LE, "The executable cannot be executed: %s", command); + } + else + { + skygw_log_write(LE, "The executable cannot be found: %s", command); + } + free(command); + } + return rval; +} diff --git a/server/core/gateway.c b/server/core/gateway.c index cd7e9f64f..f17e31fa6 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -307,10 +307,7 @@ static void sigusr1_handler (int i) LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Log file flush following reception of SIGUSR1\n"))); - skygw_log_rotate(LOGFILE_ERROR); - skygw_log_rotate(LOGFILE_MESSAGE); - skygw_log_rotate(LOGFILE_TRACE); - skygw_log_rotate(LOGFILE_DEBUG); + mxs_log_rotate(); } static void sigterm_handler (int i) { @@ -319,7 +316,7 @@ static void sigterm_handler (int i) { skygw_log_write_flush( LOGFILE_ERROR, "MaxScale received signal SIGTERM. Exiting."); - skygw_log_sync_all(); + mxs_log_flush_sync(); shutdown_server(); } @@ -331,7 +328,7 @@ sigint_handler (int i) skygw_log_write_flush( LOGFILE_ERROR, "MaxScale received signal SIGINT. Shutting down."); - skygw_log_sync_all(); + mxs_log_flush_sync(); shutdown_server(); fprintf(stderr, "\n\nShutting down MaxScale\n\n"); } @@ -412,7 +409,7 @@ sigfatal_handler(int i) } } - skygw_log_sync_all(); + mxs_log_flush_sync(); /* re-raise signal to enforce core dump */ fprintf(stderr, "\n\nWriting core dump\n"); @@ -825,7 +822,7 @@ static bool file_is_readable( absolute_pathname, eno, strerror_r(eno, errbuf, sizeof(errbuf))); - skygw_log_sync_all(); + mxs_log_flush_sync(); succp = false; } return succp; @@ -1077,19 +1074,16 @@ int main(int argc, char **argv) char* tmp_path; char* tmp_var; int option_index; - int logtofile = 0; /* Use shared memory or file */ + log_target_t log_target = LOG_TARGET_FS; int *syslog_enabled = &config_get_global_options()->syslog; /** Log to syslog */ int *maxscalelog_enabled = &config_get_global_options()->maxlog; /** Log with MaxScale */ ssize_t log_flush_timeout_ms = 0; sigset_t sigset; sigset_t sigpipe_mask; sigset_t saved_mask; - void (*exitfunp[4])(void) = {skygw_logmanager_exit, - datadir_cleanup, - write_footer, - NULL}; + void (*exitfunp[4])(void) = { mxs_log_finish, datadir_cleanup, write_footer, NULL }; - *syslog_enabled = 0; + *syslog_enabled = 1; *maxscalelog_enabled = 1; sigemptyset(&sigpipe_mask); @@ -1169,9 +1163,9 @@ int main(int argc, char **argv) case 'l': if (strncasecmp(optarg, "file", PATH_MAX) == 0) - logtofile = 1; + log_target = LOG_TARGET_FS; else if (strncasecmp(optarg, "shm", PATH_MAX) == 0) - logtofile = 0; + log_target = LOG_TARGET_SHMEM; else { char* logerr = "Configuration file argument " @@ -1698,13 +1692,8 @@ int main(int argc, char **argv) /** * Init Log Manager for MaxScale. - * The skygw_logmanager_init expects to take arguments as passed to main - * and proesses them with getopt, therefore we need to give it a dummy - * argv[0] */ { - char buf[1024]; - char *argv[8]; bool succp; if (mkdir(get_logdir(), 0777) != 0 && errno != EEXIST) @@ -1715,8 +1704,6 @@ int main(int argc, char **argv) goto return_main; } - argv[0] = "MaxScale"; - if (!(*syslog_enabled)) { printf("Syslog logging is disabled.\n"); @@ -1726,27 +1713,11 @@ int main(int argc, char **argv) { printf("MaxScale logging is disabled.\n"); } - logmanager_enable_syslog(*syslog_enabled); - logmanager_enable_maxscalelog(*maxscalelog_enabled); - if (logtofile) - { - argv[1] = "-l"; /*< write to syslog */ - /** Logs that should be syslogged */ - argv[2] = "LOGFILE_MESSAGE,LOGFILE_ERROR" - "LOGFILE_DEBUG,LOGFILE_TRACE"; - argv[3] = NULL; - succp = skygw_logmanager_init(get_logdir(), 3, argv); - } - else - { - argv[1] = "-s"; /*< store to shared memory */ - argv[2] = "LOGFILE_DEBUG,LOGFILE_TRACE"; /*< to shm */ - argv[3] = "-l"; /*< write to syslog */ - argv[4] = "LOGFILE_MESSAGE,LOGFILE_ERROR"; /*< to syslog */ - argv[5] = NULL; - succp = skygw_logmanager_init(get_logdir(), 5, argv); - } + mxs_log_set_syslog_enabled(*syslog_enabled); + mxs_log_set_maxscalelog_enabled(*maxscalelog_enabled); + + succp = mxs_log_init(NULL, get_logdir(), log_target); if (!succp) { @@ -2084,10 +2055,7 @@ static void log_flush_cb( LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "Started MaxScale log flusher."))); while (!do_exit) { - skygw_log_flush(LOGFILE_ERROR); - skygw_log_flush(LOGFILE_MESSAGE); - skygw_log_flush(LOGFILE_TRACE); - skygw_log_flush(LOGFILE_DEBUG); + mxs_log_flush(); nanosleep(&ts1, NULL); } LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, @@ -2375,7 +2343,7 @@ void set_log_augmentation(const char* value) if (!augmentation_set) { - skygw_log_set_augmentation(atoi(value)); + mxs_log_set_augmentation(atoi(value)); augmentation_set = true; } diff --git a/server/core/hashtable.c b/server/core/hashtable.c index dbcd56fd9..b7810bf56 100644 --- a/server/core/hashtable.c +++ b/server/core/hashtable.c @@ -148,6 +148,7 @@ hashtable_alloc_real( rval->vfreefn = nullfn; rval->n_readers = 0; rval->writelock = 0; + rval->n_elements = 0; spinlock_init(&rval->spin); if ((rval->entries = (HASHENTRIES **)calloc(rval->hashsize, sizeof(HASHENTRIES *))) == NULL) { @@ -296,6 +297,7 @@ hashtable_add(HASHTABLE *table, void *key, void *value) ptr->next = table->entries[hashkey % table->hashsize]; table->entries[hashkey % table->hashsize] = ptr; } + table->n_elements++; hashtable_write_unlock(table); return 1; @@ -362,6 +364,8 @@ HASHENTRIES *entry, *ptr; table->vfreefn(entry->value); free(entry); } + table->n_elements--; + assert(table->n_elements >= 0); hashtable_write_unlock(table); return 1; } @@ -772,3 +776,17 @@ char buf[40]; close(fd); return rval; } + +/** + * Return the number of elements added to the hashtable + * @param table Hashtable to measure + * @return Number of inserted elements or 0 if table is NULL + */ +int hashtable_size(HASHTABLE *table) +{ + assert(table); + spinlock_acquire(&table->spin); + int rval = table->n_elements; + spinlock_release(&table->spin); + return rval; +} diff --git a/server/core/maxkeys.c b/server/core/maxkeys.c index 39651b54c..21546fb49 100644 --- a/server/core/maxkeys.c +++ b/server/core/maxkeys.c @@ -35,10 +35,7 @@ int main(int argc, char **argv) { - int arg_count = 1; - char *home; char *keyfile; - char** arg_vector; int rval = 0; if (argc < 2) @@ -50,18 +47,8 @@ int main(int argc, char **argv) { keyfile = argv[1]; } - arg_vector = malloc(sizeof(char*) * (arg_count + 1)); - if (arg_vector == NULL) - { - fprintf(stderr,"Error: Memory allocation failed.\n"); - return 1; - } - - arg_vector[0] = "logmanager"; - arg_vector[1] = NULL; - skygw_logmanager_init(NULL, arg_count, arg_vector); - free(arg_vector); + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); if (secrets_writeKeys(keyfile)) { @@ -69,8 +56,8 @@ int main(int argc, char **argv) rval = 1; } - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); return rval; } diff --git a/server/core/maxpasswd.c b/server/core/maxpasswd.c index 96e00dd18..89d37caaf 100644 --- a/server/core/maxpasswd.c +++ b/server/core/maxpasswd.c @@ -42,9 +42,7 @@ main(int argc, char **argv) { char *enc; char *pw; - int arg_count = 1; char *home; - char** arg_vector; int rval = 0; if (argc != 3) @@ -53,18 +51,7 @@ main(int argc, char **argv) return 1; } - arg_vector = malloc(sizeof(char*) * (arg_count + 1)); - - if (arg_vector == NULL) - { - fprintf(stderr,"Error: Memory allocation failed.\n"); - return 1; - } - - arg_vector[0] = "logmanager"; - arg_vector[1] = NULL; - skygw_logmanager_init(NULL, arg_count, arg_vector); - free(arg_vector); + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); pw = calloc(81, sizeof(char)); @@ -87,7 +74,7 @@ main(int argc, char **argv) } free(pw); - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); return rval; } diff --git a/server/core/monitor.c b/server/core/monitor.c index f55b78e0c..3158f379a 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -84,6 +84,7 @@ MONITOR *mon; mon->write_timeout = DEFAULT_WRITE_TIMEOUT; mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT; mon->interval = MONITOR_INTERVAL; + mon->parameters = NULL; spinlock_init(&mon->lock); spinlock_acquire(&monLock); mon->next = allMonitors; @@ -118,6 +119,7 @@ MONITOR *ptr; ptr->next = mon->next; } spinlock_release(&monLock); + free_config_parameter(mon->parameters); free(mon->name); free(mon); } @@ -137,6 +139,23 @@ monitorStart(MONITOR *monitor, void* params) spinlock_release(&monitor->lock); } +/** + * Start all monitors + */ +void monitorStartAll() +{ + MONITOR *ptr; + + spinlock_acquire(&monLock); + ptr = allMonitors; + while (ptr) + { + monitorStart(ptr, ptr->parameters); + ptr = ptr->next; + } + spinlock_release(&monLock); +} + /** * Stop a given monitor * @@ -522,3 +541,22 @@ bool check_monitor_permissions(MONITOR* monitor) free(dpasswd); return rval; } + +/** + * Add parameters to the monitor + * @param monitor Monitor + * @param params Config parameters + */ +void monitorAddParameters(MONITOR *monitor, CONFIG_PARAMETER *params) +{ + while (params) + { + CONFIG_PARAMETER* clone = config_clone_param(params); + if (clone) + { + clone->next = monitor->parameters; + monitor->parameters = clone; + } + params = params->next; + } +} diff --git a/server/core/secrets.c b/server/core/secrets.c index 504ce3589..cc23d312a 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -1,413 +1,408 @@ -/* - * This file is distributed as part of the MariaDB Corporation MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright MariaDB Corporation Ab 2013-2014 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * Generate a random printable character - * - * @return A random printable character - */ -static unsigned char -secrets_randomchar() -{ - return (char)((random_jkiss() % ('~' - ' ')) + ' '); -} - -static int -secrets_random_str(unsigned char *output, int len) -{ -int i; - for ( i = 0; i < len; ++i ) - { - output[i] = secrets_randomchar(); - } - return 0; -} - -/** - * This routine reads data from a binary file named ".secrets" and extracts the AES encryption key - * and the AES Init Vector. - * If the path parameter is not null the custom path is interpreted as a folder - * containing the .secrets file. Otherwise the default location is used. - * @return The keys structure or NULL on error - */ -static MAXKEYS * -secrets_readKeys(char* path) -{ -char secret_file[PATH_MAX+1]; -char *home; -MAXKEYS *keys; -struct stat secret_stats; -int fd; -int len; -static int reported = 0; - if(path != NULL) - snprintf(secret_file, PATH_MAX, "%s/.secrets", path); - else - snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); - /* Try to access secrets file */ - if (access(secret_file, R_OK) == -1) - { - int eno = errno; - errno = 0; - if (eno == ENOENT) - { - if (!reported) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Encrypted password file %s can't be accessed " - "(%s). Password encryption is not used.", - secret_file, - strerror_r(eno, errbuf, sizeof(errbuf))))); - reported = 1; - } - } - else - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : access for secrets file " - "[%s] failed. Error %d, %s.", - secret_file, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - } - return NULL; - } - - /* open secret file */ - if ((fd = open(secret_file, O_RDONLY)) < 0) - { - int eno = errno; - errno = 0; - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed opening secret " - "file [%s]. Error %d, %s.", - secret_file, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - return NULL; - - } - - /* accessing file details */ - if (fstat(fd, &secret_stats) < 0) { - int eno = errno; - errno = 0; - close(fd); - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : fstat for secret file %s " - "failed. Error %d, %s.", - secret_file, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - return NULL; - } - - if (secret_stats.st_size != sizeof(MAXKEYS)) - { - int eno = errno; - errno = 0; - close(fd); - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Secrets file %s has " - "incorrect size. Error %d, %s.", - secret_file, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - return NULL; - } - if (secret_stats.st_mode != (S_IRUSR|S_IFREG)) - { - close(fd); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Ignoring secrets file " - "%s, invalid permissions.", - secret_file))); - return NULL; - } - - if ((keys = (MAXKEYS *)malloc(sizeof(MAXKEYS))) == NULL) - { - close(fd); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed " - "for key structure."))); - return NULL; - } - - /** - * Read all data from file. - * MAXKEYS (secrets.h) is struct for key, _not_ length-related macro. - */ - len = read(fd, keys, sizeof(MAXKEYS)); - - if (len != sizeof(MAXKEYS)) - { - int eno = errno; - errno = 0; - close(fd); - free(keys); - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Read from secrets file " - "%s failed. Read %d, expected %d bytes. Error %d, %s.", - secret_file, - len, - sizeof(MAXKEYS), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - return NULL; - } - - /* Close the file */ - if (close(fd) < 0) { - int eno = errno; - errno = 0; - free(keys); - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed closing the " - "secrets file %s. Error %d, %s.", - secret_file, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - return NULL; - } - ss_dassert(keys != NULL); - return keys; -} - -/** - * secrets_writeKeys - * - * This routine writes into a binary file the AES encryption key - * and the AES Init Vector - * - * @param secret_file The file with secret keys - * @return 0 on success and 1 on failure - */ -int secrets_writeKeys(char *path) -{ -int fd,randfd; -unsigned int randval; -MAXKEYS key; -char secret_file[PATH_MAX + 10]; - -if(strlen(path) > PATH_MAX) -{ - skygw_log_write(LOGFILE_ERROR,"Error: Pathname too long."); - return 1; -} - - snprintf(secret_file,PATH_MAX + 9,"%s/.secrets",path); - secret_file[PATH_MAX + 9] = '\0'; - - /* Open for writing | Create | Truncate the file for writing */ - if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed opening secret " - "file [%s]. Error %d, %s.", - secret_file, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - return 1; - } - - /* Open for writing | Create | Truncate the file for writing */ - if ((randfd = open("/dev/random", O_RDONLY)) < 0) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed opening /dev/random. Error %d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - close(fd); - return 1; - } - - if(read(randfd,(void*)&randval,sizeof(unsigned int)) < 1) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to read /dev/random."))); - close(fd); - close(randfd); - return 1; - } - - close(randfd); - secrets_random_str(key.enckey, MAXSCALE_KEYLEN); - secrets_random_str(key.initvector, MAXSCALE_IV_LEN); - - /* Write data */ - if (write(fd, &key, sizeof(key)) < 0) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed writing into " - "secret file [%s]. Error %d, %s.", - secret_file, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - close(fd); - return 1; - } - - /* close file */ - if (close(fd) < 0) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed closing the " - "secret file [%s]. Error %d, %s.", - secret_file, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - } - - if( chmod(secret_file, S_IRUSR) < 0) - { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to change the permissions of the" - "secret file [%s]. Error %d, %s.", - secret_file, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - } - - return 0; -} - -/** - * Decrypt a password that is stored inthe MaxScale configuration file. - * If the password is not encrypted, ie is not a HEX string, then the - * original is returned, this allows for backward compatibility with - * unencrypted password. - * - * Note the return is always a malloc'd string that the caller must free - * - * @param crypt The encrypted password - * @return The decrypted password - */ -char * -decryptPassword(char *crypt) -{ -MAXKEYS *keys; -AES_KEY aeskey; -unsigned char *plain; -char *ptr; -unsigned char encrypted[80]; -int enlen; - - keys = secrets_readKeys(NULL); - if (!keys) - return strdup(crypt); - /* - ** If the input is not a HEX string return the input - ** it probably was not encrypted - */ - for (ptr = crypt; *ptr; ptr++) - { - if (!isxdigit(*ptr)) - { - free(keys); - return strdup(crypt); - } - } - - enlen = strlen(crypt) / 2; - gw_hex2bin(encrypted, crypt, strlen(crypt)); - - if ((plain = (unsigned char *)malloc(80)) == NULL) - { - free(keys); - return NULL; - } - - AES_set_decrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey); - - AES_cbc_encrypt(encrypted, plain, enlen, &aeskey, keys->initvector, AES_DECRYPT); - free(keys); - - return (char *)plain; -} - -/** - * Encrypt a password that can be stored in the MaxScale configuration file. - * - * Note the return is always a malloc'd string that the caller must free - * @param path Path the the .secrets file - * @param password The password to encrypt - * @return The encrypted password - */ -char * -encryptPassword(char* path, char *password) -{ -MAXKEYS *keys; -AES_KEY aeskey; -int padded_len; -char *hex_output; -unsigned char padded_passwd[80]; -unsigned char encrypted[80]; - - if ((keys = secrets_readKeys(path)) == NULL) - return NULL; - - memset(padded_passwd, 0, 80); - strncpy((char *)padded_passwd, password, 79); - padded_len = ((strlen(password) / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE; - - AES_set_encrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey); - - AES_cbc_encrypt(padded_passwd, encrypted, padded_len, &aeskey, keys->initvector, AES_ENCRYPT); - hex_output = (char *)malloc(padded_len * 2); - gw_bin2hex(hex_output, encrypted, padded_len); - free(keys); - - return hex_output; -} +/* + * This file is distributed as part of the MariaDB Corporation MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright MariaDB Corporation Ab 2013-2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Generate a random printable character + * + * @return A random printable character + */ +static unsigned char +secrets_randomchar() +{ + return(char) ((random_jkiss() % ('~' - ' ')) + ' '); +} + +static int +secrets_random_str(unsigned char *output, int len) +{ + for (int i = 0; i < len; ++i) + { + output[i] = secrets_randomchar(); + } + return 0; +} + +/** + * This routine reads data from a binary file named ".secrets" and extracts the AES encryption key + * and the AES Init Vector. + * If the path parameter is not null the custom path is interpreted as a folder + * containing the .secrets file. Otherwise the default location is used. + * @return The keys structure or NULL on error + */ +static MAXKEYS * +secrets_readKeys(const char* path) +{ + char secret_file[PATH_MAX + 1]; + char *home; + MAXKEYS *keys; + struct stat secret_stats; + int fd; + int len; + static int reported = 0; + + if (path != NULL) + { + snprintf(secret_file, PATH_MAX, "%s/.secrets", path); + } + else + { + snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir()); + } + /* Try to access secrets file */ + if (access(secret_file, R_OK) == -1) + { + int eno = errno; + errno = 0; + if (eno == ENOENT) + { + if (!reported) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Encrypted password file %s can't be accessed " + "(%s). Password encryption is not used.", + secret_file, + strerror_r(eno, errbuf, sizeof(errbuf))))); + reported = 1; + } + } + else + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : access for secrets file " + "[%s] failed. Error %d, %s.", + secret_file, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + } + return NULL; + } + + /* open secret file */ + if ((fd = open(secret_file, O_RDONLY)) < 0) + { + int eno = errno; + errno = 0; + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Failed opening secret " + "file [%s]. Error %d, %s.", + secret_file, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + return NULL; + + } + + /* accessing file details */ + if (fstat(fd, &secret_stats) < 0) + { + int eno = errno; + errno = 0; + close(fd); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : fstat for secret file %s " + "failed. Error %d, %s.", + secret_file, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + return NULL; + } + + if (secret_stats.st_size != sizeof(MAXKEYS)) + { + int eno = errno; + errno = 0; + close(fd); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Secrets file %s has " + "incorrect size. Error %d, %s.", + secret_file, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + return NULL; + } + if (secret_stats.st_mode != (S_IRUSR | S_IFREG)) + { + close(fd); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Ignoring secrets file " + "%s, invalid permissions.", + secret_file))); + return NULL; + } + + if ((keys = (MAXKEYS *) malloc(sizeof(MAXKEYS))) == NULL) + { + close(fd); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Memory allocation failed " + "for key structure."))); + return NULL; + } + + /** + * Read all data from file. + * MAXKEYS (secrets.h) is struct for key, _not_ length-related macro. + */ + len = read(fd, keys, sizeof(MAXKEYS)); + + if (len != sizeof(MAXKEYS)) + { + int eno = errno; + errno = 0; + close(fd); + free(keys); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Read from secrets file " + "%s failed. Read %d, expected %d bytes. Error %d, %s.", + secret_file, + len, + sizeof(MAXKEYS), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + return NULL; + } + + /* Close the file */ + if (close(fd) < 0) + { + int eno = errno; + errno = 0; + free(keys); + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : Failed closing the " + "secrets file %s. Error %d, %s.", + secret_file, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))))); + return NULL; + } + ss_dassert(keys != NULL); + return keys; +} + +/** + * secrets_writeKeys + * + * This routine writes into a binary file the AES encryption key + * and the AES Init Vector + * + * @param secret_file The file with secret keys + * @return 0 on success and 1 on failure + */ +int secrets_writeKeys(const char *path) +{ + int fd, randfd; + unsigned int randval; + MAXKEYS key; + char secret_file[PATH_MAX + 10]; + + if (strlen(path) > PATH_MAX) + { + skygw_log_write(LOGFILE_ERROR, "Error: Pathname too long."); + return 1; + } + + snprintf(secret_file, PATH_MAX + 9, "%s/.secrets", path); + secret_file[PATH_MAX + 9] = '\0'; + + /* Open for writing | Create | Truncate the file for writing */ + if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed opening secret " + "file [%s]. Error %d, %s.", + secret_file, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); + return 1; + } + + /* Open for writing | Create | Truncate the file for writing */ + if ((randfd = open("/dev/random", O_RDONLY)) < 0) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed opening /dev/random. Error %d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); + close(fd); + return 1; + } + + if (read(randfd, (void*) &randval, sizeof(unsigned int)) < 1) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed to read /dev/random."))); + close(fd); + close(randfd); + return 1; + } + + close(randfd); + secrets_random_str(key.enckey, MAXSCALE_KEYLEN); + secrets_random_str(key.initvector, MAXSCALE_IV_LEN); + + /* Write data */ + if (write(fd, &key, sizeof(key)) < 0) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed writing into " + "secret file [%s]. Error %d, %s.", + secret_file, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); + close(fd); + return 1; + } + + /* close file */ + if (close(fd) < 0) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed closing the " + "secret file [%s]. Error %d, %s.", + secret_file, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); + } + + if (chmod(secret_file, S_IRUSR) < 0) + { + char errbuf[STRERROR_BUFLEN]; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : failed to change the permissions of the" + "secret file [%s]. Error %d, %s.", + secret_file, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))))); + } + + return 0; +} + +/** + * Decrypt a password that is stored inthe MaxScale configuration file. + * If the password is not encrypted, ie is not a HEX string, then the + * original is returned, this allows for backward compatibility with + * unencrypted password. + * + * Note the return is always a malloc'd string that the caller must free + * + * @param crypt The encrypted password + * @return The decrypted password + */ +char * +decryptPassword(const char *crypt) +{ + MAXKEYS *keys; + AES_KEY aeskey; + unsigned char *plain; + char *ptr; + unsigned char encrypted[80]; + int enlen; + + keys = secrets_readKeys(NULL); + if (!keys) + { + return strdup(crypt); + } + /* + ** If the input is not a HEX string return the input + ** it probably was not encrypted + */ + for (ptr = crypt; *ptr; ptr++) + { + if (!isxdigit(*ptr)) + { + free(keys); + return strdup(crypt); + } + } + + enlen = strlen(crypt) / 2; + gw_hex2bin(encrypted, crypt, strlen(crypt)); + + if ((plain = (unsigned char *) malloc(80)) == NULL) + { + free(keys); + return NULL; + } + + AES_set_decrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey); + + AES_cbc_encrypt(encrypted, plain, enlen, &aeskey, keys->initvector, AES_DECRYPT); + free(keys); + + return(char *) plain; +} + +/** + * Encrypt a password that can be stored in the MaxScale configuration file. + * + * Note the return is always a malloc'd string that the caller must free + * @param path Path the the .secrets file + * @param password The password to encrypt + * @return The encrypted password + */ +char * +encryptPassword(const char* path, const char *password) +{ + MAXKEYS *keys; + AES_KEY aeskey; + int padded_len; + char *hex_output; + unsigned char padded_passwd[80]; + unsigned char encrypted[80]; + + if ((keys = secrets_readKeys(path)) == NULL) + { + return NULL; + } + + memset(padded_passwd, 0, 80); + strncpy((char *) padded_passwd, password, 79); + padded_len = ((strlen(password) / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE; + + AES_set_encrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey); + + AES_cbc_encrypt(padded_passwd, encrypted, padded_len, &aeskey, keys->initvector, AES_ENCRYPT); + hex_output = (char *) malloc(padded_len * 2); + gw_bin2hex(hex_output, encrypted, padded_len); + free(keys); + + return hex_output; +} diff --git a/server/core/server.c b/server/core/server.c index 7818dd424..dad510c1c 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -74,7 +74,7 @@ SERVER *server; server->server_chk_top = CHK_NUM_SERVER; server->server_chk_tail = CHK_NUM_SERVER; #endif - server->name = strdup(servname); + server->name = strndup(servname, MAX_SERVER_NAME_LEN); server->protocol = strdup(protocol); server->port = port; server->status = SERVER_RUNNING; @@ -82,6 +82,7 @@ SERVER *server; server->rlag = -2; server->master_id = -1; server->depth = -1; + spinlock_init(&server->lock); server->persistent = NULL; server->persistmax = 0; spinlock_init(&server->persistlock); @@ -666,6 +667,7 @@ char *status = NULL; void server_set_status(SERVER *server, int bit) { + spinlock_acquire(&server->lock); server->status |= bit; /** clear error logged flag before the next failure */ @@ -673,6 +675,7 @@ server_set_status(SERVER *server, int bit) { server->master_err_is_logged = false; } + spinlock_release(&server->lock); } /** @@ -684,7 +687,9 @@ server_set_status(SERVER *server, int bit) void server_clear_status(SERVER *server, int bit) { + spinlock_acquire(&server->lock); server->status &= ~bit; + spinlock_release(&server->lock); } /** @@ -910,3 +915,54 @@ server_update_port(SERVER *server, unsigned short port) spinlock_release(&server_spin); } +static struct { + char *str; + unsigned int bit; +} ServerBits[] = { + { "running", SERVER_RUNNING }, + { "master", SERVER_MASTER }, + { "slave", SERVER_SLAVE }, + { "synced", SERVER_JOINED }, + { "ndb", SERVER_NDB }, + { "maintenance", SERVER_MAINT }, + { "maint", SERVER_MAINT }, + { NULL, 0 } +}; + +/** + * Map the server status bit + * + * @param str String representation + * @return bit value or 0 on error + */ +unsigned int +server_map_status(char *str) +{ +int i; + + for (i = 0; ServerBits[i].str; i++) + if (!strcasecmp(str, ServerBits[i].str)) + return ServerBits[i].bit; + return 0; +} + +/** + * Set the version string of the server. + * @param server Server to update + * @param string Version string + * @return True if the assignment of the version string was successful, false if + * memory allocation failed. + */ +bool server_set_version_string(SERVER* server, const char* string) +{ + bool rval = true; + spinlock_acquire(&server->lock); + free(server->server_string); + if ((server->server_string = strdup(string)) == NULL) + { + MXS_ERROR("Memory allocation failed."); + rval = false; + } + spinlock_release(&server->lock); + return rval; +} diff --git a/server/core/service.c b/server/core/service.c index 91f21aac6..cff398445 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -348,6 +348,7 @@ GWPROTOCOL *funcs; users_free(service->users); service->users = NULL; dcb_close(port->listener); + service->users = NULL; port->listener = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -384,6 +385,7 @@ GWPROTOCOL *funcs; service->users = NULL; dcb_close(port->listener); port->listener = NULL; + service->users = NULL; goto retblock; } } diff --git a/server/core/test/testadminusers.c b/server/core/test/testadminusers.c index cfa9be6ba..cdc7687ef 100644 --- a/server/core/test/testadminusers.c +++ b/server/core/test/testadminusers.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testdcb.c b/server/core/test/testdcb.c index 9668d8a23..7e63ce134 100644 --- a/server/core/test/testdcb.c +++ b/server/core/test/testdcb.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c index c1d53b9cc..17fb3cf99 100644 --- a/server/core/test/testfeedback.c +++ b/server/core/test/testfeedback.c @@ -28,6 +28,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #define FAILTEST(s) printf("TEST FAILED: " s "\n");return 1; #include #include diff --git a/server/core/test/testfilter.c b/server/core/test/testfilter.c index bf97d7897..1fee8557c 100644 --- a/server/core/test/testfilter.c +++ b/server/core/test/testfilter.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testgwbitmask.c b/server/core/test/testgwbitmask.c index 86bad1245..df446c600 100644 --- a/server/core/test/testgwbitmask.c +++ b/server/core/test/testgwbitmask.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testhash.c b/server/core/test/testhash.c index 60f759839..5af247989 100644 --- a/server/core/test/testhash.c +++ b/server/core/test/testhash.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include @@ -154,7 +161,6 @@ static bool do_hashtest( ss_dfprintf(stderr, "\t\t..done\n\nTest completed successfully.\n\n"); - CHK_HASHTABLE(h); hashtable_free(h); diff --git a/server/core/test/testhint.c b/server/core/test/testhint.c index 388d0f610..38bccc3e3 100644 --- a/server/core/test/testhint.c +++ b/server/core/test/testhint.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include @@ -37,7 +44,7 @@ * test1 Allocate table of users and mess around with it * */ -void skygw_log_sync_all(void); +int mxs_log_flush_sync(void); static int test1() { @@ -49,13 +56,13 @@ HINT *hint; char* name = strdup("name"); hint = hint_create_parameter(NULL, name, "value"); free(name); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL != hint, "New hint list should not be null"); ss_info_dassert(0 == strcmp("value", hint->value), "Hint value should be correct"); ss_info_dassert(0 != hint_exists(&hint, HINT_PARAMETER), "Hint of parameter type should exist"); ss_dfprintf(stderr, "\t..done\nFree hints."); if (NULL != hint) hint_free(hint); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_dfprintf(stderr, "\t..done\n"); return 0; diff --git a/server/core/test/testmemlog.c b/server/core/test/testmemlog.c index d1cad08ae..7d2f29e93 100644 --- a/server/core/test/testmemlog.c +++ b/server/core/test/testmemlog.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testmodutil.c b/server/core/test/testmodutil.c index 39632ab6d..a865560e3 100644 --- a/server/core/test/testmodutil.c +++ b/server/core/test/testmodutil.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testpoll.c b/server/core/test/testpoll.c index 61978b4f4..40ed00f0c 100644 --- a/server/core/test/testpoll.c +++ b/server/core/test/testpoll.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testserver.c b/server/core/test/testserver.c index 7e00b4f0b..57f4d4e3a 100644 --- a/server/core/test/testserver.c +++ b/server/core/test/testserver.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include @@ -48,7 +55,7 @@ char *status; ss_dfprintf(stderr, "testserver : creating server called MyServer"); server = server_alloc("MyServer", "HTTPD", 9876); - skygw_log_sync_all(); + mxs_log_flush_sync(); //ss_info_dassert(NULL != service, "New server with valid protocol and port must not be null"); //ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation"); @@ -56,32 +63,32 @@ char *status; ss_dfprintf(stderr, "\t..done\nTest Parameter for Server."); ss_info_dassert(NULL == serverGetParameter(server, "name"), "Parameter should be null when not set"); serverAddParameter(server, "name", "value"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 == strcmp("value", serverGetParameter(server, "name")), "Parameter should be returned correctly"); ss_dfprintf(stderr, "\t..done\nTesting Unique Name for Server."); ss_info_dassert(NULL == server_find_by_unique_name("uniquename"), "Should not find non-existent unique name."); server_set_unique_name(server, "uniquename"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(server == server_find_by_unique_name("uniquename"), "Should find by unique name."); ss_dfprintf(stderr, "\t..done\nTesting Status Setting for Server."); status = server_status(server); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 == strcmp("Running", status), "Status of Server should be Running by default."); if (NULL != status) free(status); server_set_status(server, SERVER_MASTER); status = server_status(server); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 == strcmp("Master, Running", status), "Should find correct status."); server_clear_status(server, SERVER_MASTER); free(status); status = server_status(server); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 == strcmp("Running", status), "Status of Server should be Running after master status cleared."); if (NULL != status) free(status); ss_dfprintf(stderr, "\t..done\nRun Prints for Server and all Servers."); printServer(server); printAllServers(); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_dfprintf(stderr, "\t..done\nFreeing Server."); ss_info_dassert(0 != server_free(server), "Free should succeed"); ss_dfprintf(stderr, "\t..done\n"); diff --git a/server/core/test/testservice.c b/server/core/test/testservice.c index 5151fce44..d58d37ac1 100644 --- a/server/core/test/testservice.c +++ b/server/core/test/testservice.c @@ -60,12 +60,12 @@ init_test_env(NULL); ss_dfprintf(stderr, "testservice : creating service called MyService with router nonexistent"); service = service_alloc("MyService", "non-existent"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL == service, "New service with invalid router should be null"); ss_info_dassert(0 == service_isvalid(service), "Service must not be valid after incorrect creation"); ss_dfprintf(stderr, "\t..done\nValid service creation, router testroute."); service = service_alloc("MyService", "testroute"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL != service, "New service with valid router must not be null"); ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation"); ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name"); @@ -73,16 +73,16 @@ init_test_env(NULL); ss_info_dassert(0 != serviceAddProtocol(service, "testprotocol", "localhost", 9876), "Add Protocol should succeed"); ss_info_dassert(0 != serviceHasProtocol(service, "testprotocol", 9876), "Service should have new protocol as requested"); serviceStartProtocol(service, "testprotocol", 9876); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_dfprintf(stderr, "\t..done\nStarting Service."); result = serviceStart(service); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 != result, "Start should succeed"); serviceStop(service); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed"); result = serviceStartAll(); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(0 != result, "Start all should succeed"); ss_dfprintf(stderr, "\t..done\nStopping Service."); serviceStop(service); diff --git a/server/core/test/testsession.c b/server/core/test/testsession.c index d471fdad4..bf068bd11 100644 --- a/server/core/test/testsession.c +++ b/server/core/test/testsession.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testspinlock.c b/server/core/test/testspinlock.c index ed8fa7214..3f2357d4a 100644 --- a/server/core/test/testspinlock.c +++ b/server/core/test/testspinlock.c @@ -27,6 +27,13 @@ * @endverbatim */ +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#if !defined(SS_DEBUG) +#define SS_DEBUG +#endif +#if defined(NDEBUG) +#undef NDEBUG +#endif #include #include #include diff --git a/server/core/test/testusers.c b/server/core/test/testusers.c index 4385d74eb..fcc933b1a 100644 --- a/server/core/test/testusers.c +++ b/server/core/test/testusers.c @@ -58,39 +58,39 @@ int result, count; ss_dfprintf(stderr, "testusers : Initialise the user table."); users = users_alloc(); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL != users, "Allocating user table should not return NULL.") ss_dfprintf(stderr, "\t..done\nAdd a user"); count = users_add(users, "username", "authorisation"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(1 == count, "Should add one user"); authdata = users_fetch(users, "username"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL != authdata, "Fetch valid user must not return NULL"); ss_info_dassert(0 == strcmp("authorisation", authdata), "User authorisation should be correct"); ss_dfprintf(stderr, "\t..done\nPrint users"); usersPrint(users); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_dfprintf(stderr, "\t..done\nUpdate a user"); count = users_update(users, "username", "newauth"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(1 == count, "Should update just one user"); authdata = users_fetch(users, "username"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(NULL != authdata, "Fetch valid user must not return NULL"); ss_info_dassert(0 == strcmp("newauth", authdata), "User authorisation should be correctly updated"); ss_dfprintf(stderr, "\t..done\nAdd another user"); count = users_add(users, "username2", "authorisation2"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(1 == count, "Should add one user"); ss_dfprintf(stderr, "\t..done\nDelete a user."); count = users_delete(users, "username"); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_info_dassert(1 == count, "Should delete just one user"); ss_dfprintf(stderr, "\t..done\nFree user table."); users_free(users); - skygw_log_sync_all(); + mxs_log_flush_sync(); ss_dfprintf(stderr, "\t..done\n"); return 0; diff --git a/server/include/externcmd.h b/server/include/externcmd.h index 1ab44f908..3e50d99a8 100644 --- a/server/include/externcmd.h +++ b/server/include/externcmd.h @@ -6,15 +6,21 @@ #include #include #include +#include + #define MAXSCALE_EXTCMD_ARG_MAX 256 typedef struct extern_cmd_t{ - char* parameters[MAXSCALE_EXTCMD_ARG_MAX]; /*< Command arguments */ + char** argv; /*< Argument vector for the command, first being the actual command + * being executed. */ int n_exec; /*< Number of times executed */ pid_t child; /*< PID of the child process */ }EXTERNCMD; +char* externcmd_extract_command(const char* argstr); EXTERNCMD* externcmd_allocate(char* argstr); void externcmd_free(EXTERNCMD* cmd); int externcmd_execute(EXTERNCMD* cmd); +bool externcmd_substitute_arg(EXTERNCMD* cmd, const char* re, const char* replace); +bool externcmd_can_execute(const char* argstr); #endif diff --git a/server/include/hashtable.h b/server/include/hashtable.h index 26e44b799..aabcc84b4 100644 --- a/server/include/hashtable.h +++ b/server/include/hashtable.h @@ -85,6 +85,7 @@ typedef struct hashtable { int n_readers; /**< Number of clients reading the table */ int writelock; /**< The table is locked by a writer */ bool ht_isflat; /**< Indicates whether hashtable is in stack or heap */ + int n_elements; /*< Number of added elements */ #if defined(SS_DEBUG) skygw_chk_t ht_chk_tail; #endif @@ -130,4 +131,5 @@ extern HASHITERATOR *hashtable_iterator(HASHTABLE *); extern void *hashtable_next(HASHITERATOR *); /**< Return the key of the hash table iterator */ extern void hashtable_iterator_free(HASHITERATOR *); +extern int hashtable_size(HASHTABLE *table); #endif diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index f6fb52596..fc134c6b7 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -120,6 +120,7 @@ extern unsigned int config_pollsleep(); CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); +void free_config_parameter(CONFIG_PARAMETER* p1); extern int config_truth_value(char *); extern double config_percentage_value(char *str); bool config_set_qualified_param( diff --git a/server/include/monitor.h b/server/include/monitor.h index 1841112ca..5c2bb1f69 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -21,6 +21,7 @@ #include #include #include +#include /** * @file monitor.h The interface to the monitor module @@ -140,6 +141,7 @@ typedef struct monitor { char* user; /*< Monitor username */ char* password; /*< Monitor password */ SPINLOCK lock; + CONFIG_PARAMETER* parameters; /*< configuration parameters */ MONITOR_SERVERS* databases; /*< List of databases the monitor monitors */ monitor_state_t state; /**< The state of the monitor */ int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */ @@ -160,9 +162,11 @@ extern void monitor_free(MONITOR *); extern MONITOR *monitor_find(char *); extern void monitorAddServer(MONITOR *, SERVER *); extern void monitorAddUser(MONITOR *, char *, char *); +extern void monitorAddParameters(MONITOR *monitor, CONFIG_PARAMETER *params); extern void monitorStop(MONITOR *); extern void monitorStart(MONITOR *, void*); extern void monitorStopAll(); +extern void monitorStartAll(); extern void monitorShowAll(DCB *); extern void monitorShow(DCB *, MONITOR *); extern void monitorList(DCB *); @@ -170,4 +174,5 @@ extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetNetworkTimeout(MONITOR *, int, int); extern RESULTSET *monitorGetList(); bool check_monitor_permissions(MONITOR* monitor); + #endif diff --git a/server/include/secrets.h b/server/include/secrets.h index cb2912827..e039de45c 100644 --- a/server/include/secrets.h +++ b/server/include/secrets.h @@ -24,8 +24,8 @@ * @verbatim * Revision History * - * Date Who Description - * 23/06/2013 Massimiliano Pinto Initial implementation + * Date Who Description + * 23/06/2013 Massimiliano Pinto Initial implementation * * @endverbatim */ @@ -40,18 +40,19 @@ #include -#define MAXSCALE_KEYLEN 32 -#define MAXSCALE_IV_LEN 16 +#define MAXSCALE_KEYLEN 32 +#define MAXSCALE_IV_LEN 16 /** * The key structure held in the secrets file */ -typedef struct maxkeys { - unsigned char enckey[MAXSCALE_KEYLEN]; - unsigned char initvector[MAXSCALE_IV_LEN]; +typedef struct maxkeys +{ + unsigned char enckey[MAXSCALE_KEYLEN]; + unsigned char initvector[MAXSCALE_IV_LEN]; } MAXKEYS; -extern int secrets_writeKeys(char *filename); -extern char *decryptPassword(char *); -extern char *encryptPassword(char*,char *); +extern int secrets_writeKeys(const char *filename); +extern char *decryptPassword(const char *); +extern char *encryptPassword(const char*, const char *); #endif diff --git a/server/include/server.h b/server/include/server.h index b3d43c98f..84151ccad 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -49,6 +49,8 @@ * @endverbatim */ +#define MAX_SERVER_NAME_LEN 1024 + /** * The server parameters used for weighting routing decissions * @@ -81,6 +83,7 @@ typedef struct server { #if defined(SS_DEBUG) skygw_chk_t server_chk_top; #endif + SPINLOCK lock; /**< Common access lock */ char *unique_name; /**< Unique name for the server */ char *name; /**< Server name/IP address*/ unsigned short port; /**< Port to listen on */ @@ -208,4 +211,6 @@ extern DCB *server_get_persistent(SERVER *, char *, const char *); extern void server_update_address(SERVER *, char *); extern void server_update_port(SERVER *, unsigned short); extern RESULTSET *serverGetList(); +extern unsigned int server_map_status(char *str); +extern bool server_set_version_string(SERVER* server, const char* string); #endif diff --git a/server/include/test_utils.h b/server/include/test_utils.h index 4b1d9bc6f..53bcbb379 100644 --- a/server/include/test_utils.h +++ b/server/include/test_utils.h @@ -12,15 +12,7 @@ void init_test_env(char *path) const char* logdir = path ? path : TEST_LOG_DIR; - char* argv[] = - { - "log_manager", - "-l", - "LOGFILE_ERROR", - NULL - }; - - skygw_logmanager_init(logdir, argc, argv); + mxs_log_init(NULL, logdir, LOG_TARGET_DEFAULT); poll_init(); hkinit(); } diff --git a/server/modules/filter/dbfwfilter.c b/server/modules/filter/dbfwfilter.c index 8f64139c8..37dc5419f 100644 --- a/server/modules/filter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter.c @@ -2213,7 +2213,7 @@ int main(int argc, char** argv) NULL }; - skygw_logmanager_init(argc_,argv_); + mxs_log_init(argc_,argv_); init_test_env(home); @@ -2231,7 +2231,7 @@ int main(int argc, char** argv) printf("Failed to parse rule. Read the error log for the reason of the failure.\n"); } - skygw_log_sync_all(); + mxs_log_flush_sync(); return 0; } diff --git a/server/modules/filter/test/harness_common.c b/server/modules/filter/test/harness_common.c index e657fbdd0..0b8510263 100644 --- a/server/modules/filter/test/harness_common.c +++ b/server/modules/filter/test/harness_common.c @@ -15,10 +15,9 @@ int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){ DCB* dcb; char cwd[1024]; char tmp[2048]; - char** optstr; if(!(argc == 2 && strcmp(argv[1],"-h") == 0)){ - skygw_logmanager_init(NULL,0,NULL); + mxs_log_init(NULL,NULL,LOG_TARGET_DEFAULT); } if(!(instance.head = calloc(1,sizeof(FILTERCHAIN)))) @@ -52,11 +51,7 @@ int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){ getcwd(cwd,sizeof(cwd)); sprintf(tmp,"%s",cwd); - optstr = (char**)malloc(sizeof(char*)*2); - optstr[0] = strdup("log_manager"); - optstr[1] = NULL; - skygw_logmanager_init(tmp, 1, optstr); - free(optstr); + mxs_log_init(NULL, tmp, LOG_TARGET_DEFAULT); rval = process_opts(argc,argv); diff --git a/server/modules/filter/test/harness_ui.c b/server/modules/filter/test/harness_ui.c index fa8d14e3d..b92182a37 100644 --- a/server/modules/filter/test/harness_ui.c +++ b/server/modules/filter/test/harness_ui.c @@ -11,8 +11,7 @@ int main(int argc, char** argv){ if(harness_init(argc,argv,&hinstance)){ printf("Error: Initialization failed.\n"); skygw_log_write(LOGFILE_ERROR,"Error: Initialization failed.\n"); - skygw_logmanager_done(); - skygw_logmanager_exit(); + mxs_log_finish(); return 1; } @@ -230,8 +229,7 @@ int main(int argc, char** argv){ free_buffers(); free_filters(); - skygw_logmanager_done(); - skygw_logmanager_exit(); + mxs_log_finish(); free(instance.head); return 0; diff --git a/server/modules/filter/test/harness_util.c b/server/modules/filter/test/harness_util.c index 5b3baf31e..16d10c8c4 100644 --- a/server/modules/filter/test/harness_util.c +++ b/server/modules/filter/test/harness_util.c @@ -35,8 +35,7 @@ int main(int argc,char** argv) if(harness_init(argc,argv,&inst) || inst->error){ printf("Error: Initialization failed.\n"); skygw_log_write(LOGFILE_ERROR,"Error: Initialization failed.\n"); - skygw_logmanager_done(); - skygw_logmanager_exit(); + mxs_log_finish(); return 1; } diff --git a/server/modules/include/maxinfo.h b/server/modules/include/maxinfo.h index 040433161..5f7cb6493 100644 --- a/server/modules/include/maxinfo.h +++ b/server/modules/include/maxinfo.h @@ -73,7 +73,12 @@ typedef enum MAXOP_LITERAL, MAXOP_PREDICATE, MAXOP_LIKE, - MAXOP_EQUAL + MAXOP_EQUAL, + MAXOP_FLUSH, + MAXOP_SET, + MAXOP_CLEAR, + MAXOP_SHUTDOWN, + MAXOP_RESTART } MAXINFO_OPERATOR; /** @@ -109,6 +114,11 @@ typedef struct maxinfo_tree { #define LT_FROM 7 #define LT_STAR 8 #define LT_VARIABLE 9 +#define LT_FLUSH 10 +#define LT_SET 11 +#define LT_CLEAR 12 +#define LT_SHUTDOWN 13 +#define LT_RESTART 14 /** diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index d4d79ed84..c986943f9 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -154,32 +154,15 @@ startMonitor(void *arg,void* opt) handle->use_priority = config_truth_value(params->value); else if(!strcmp(params->name,"script")) { - if(handle->script) - { - free(handle->script); - handle->script = NULL; - } - - if(access(params->value,X_OK) == 0) - { - handle->script = strdup(params->value); - } - else - { - script_error = true; - if(access(params->value,F_OK) == 0) - { - skygw_log_write(LE, - "Error: The file cannot be executed: %s", - params->value); - } - else - { - skygw_log_write(LE, - "Error: The file cannot be found: %s", - params->value); - } - } + if (externcmd_can_execute(params->value)) + { + free(handle->script); + handle->script = strdup(params->value); + } + else + { + script_error = true; + } } else if(!strcmp(params->name,"events")) { @@ -327,11 +310,10 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); - if (server_string) { - database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); - if (database->server->server_string) - strcpy(database->server->server_string, server_string); - } + if (server_string) + { + server_set_version_string(database->server, server_string); + } /* Check if the the Galera FSM shows this node is joined to the cluster */ if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0 diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 737e03f65..7029c4691 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -136,31 +136,15 @@ startMonitor(void *arg,void* opt) } else if(!strcmp(params->name,"script")) { - if(handle->script) - { - free(handle->script); - } - if(access(params->value,X_OK) == 0) - { - handle->script = strdup(params->value); - } - else - { - script_error = true; - if(access(params->value,F_OK) == 0) - { - skygw_log_write(LE, - "Error: The file cannot be executed: %s", - params->value); - } - else - { - skygw_log_write(LE, - "Error: The file cannot be found: %s", - params->value); - } - handle->script = NULL; - } + if (externcmd_can_execute(params->value)) + { + free(handle->script); + handle->script = strdup(params->value); + } + else + { + script_error = true; + } } else if(!strcmp(params->name,"events")) { @@ -312,11 +296,10 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); - if (server_string) { - database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); - if (database->server->server_string) - strcpy(database->server->server_string, server_string); - } + if (server_string) + { + server_set_version_string(database->server, server_string); + } /* get server_id form current node */ if (mysql_query(database->con, "SELECT @@server_id") == 0 diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index c5db84fa7..c3180d188 100644 --- a/server/modules/monitor/monitor_common.c +++ b/server/modules/monitor/monitor_common.c @@ -17,6 +17,7 @@ */ #include +#include monitor_event_t mon_name_to_event(char* tok); @@ -225,23 +226,32 @@ case NEW_DONOR_EVENT: } -void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) +/** + * Create a list of running servers + * @param start Monitored servers + * @param dest Destination where the string is formed + * @param len Length of @c dest + */ +void mon_append_node_names(MONITOR_SERVERS* start, char* dest, int len) { MONITOR_SERVERS* ptr = start; bool first = true; - int slen = strlen(str); - char arr[256]; - while(ptr && slen < len) + int slen = strlen(dest); + char arr[MAX_SERVER_NAME_LEN + 32]; // Some extra space for port + while (ptr && slen < len) { - if(!first) - { - strncat(str,",",len); - } - first = false; - sprintf(arr,"%s:%d",ptr->server->name,ptr->server->port); - strncat(str,arr,len); - ptr = ptr->next; - slen = strlen(str); + if(SERVER_IS_RUNNING(ptr->server)) + { + if (!first) + { + strncat(dest, ",", len); + } + first = false; + snprintf(arr, sizeof(arr), "%s:%d", ptr->server->name, ptr->server->port); + strncat(dest, arr, len); + slen = strlen(dest); + } + ptr = ptr->next; } } @@ -302,23 +312,26 @@ bool mon_print_fail_status( */ void monitor_launch_script(MONITOR* mon, MONITOR_SERVERS* ptr, char* script) { - char argstr[PATH_MAX + MON_ARG_MAX + 1]; - EXTERNCMD* cmd; + char nodelist[PATH_MAX + MON_ARG_MAX + 1] = {'\0'}; + char event[strlen(mon_get_event_name(ptr))]; + char initiator[strlen(ptr->server->name) + 24]; // Extra space for port - snprintf(argstr, PATH_MAX + MON_ARG_MAX, - "%s --event=%s --initiator=%s:%d --nodelist=", - script, - mon_get_event_name(ptr), - ptr->server->name, - ptr->server->port); + snprintf(initiator, sizeof(initiator), "%s:%d", ptr->server->name, ptr->server->port); + snprintf(event, sizeof(event), "%s", mon_get_event_name(ptr)); + mon_append_node_names(mon->databases, nodelist, PATH_MAX + MON_ARG_MAX); - mon_append_node_names(mon->databases, argstr, PATH_MAX + MON_ARG_MAX); - if ((cmd = externcmd_allocate(argstr)) == NULL) + EXTERNCMD* cmd = externcmd_allocate(script); + + if (cmd == NULL) { skygw_log_write(LE, "Failed to initialize script: %s", script); return; } + externcmd_substitute_arg(cmd, "[$]INITIATOR", initiator); + externcmd_substitute_arg(cmd, "[$]EVENT", event); + externcmd_substitute_arg(cmd, "[$]NODELIST", nodelist); + if (externcmd_execute(cmd)) { skygw_log_write(LOGFILE_ERROR, diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index e42be0e34..2b934667f 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -168,32 +168,18 @@ startMonitor(void *arg, void* opt) handle->detectStaleMaster = config_truth_value(params->value); else if(!strcmp(params->name,"detect_replication_lag")) handle->replicationHeartbeat = config_truth_value(params->value); - else if(!strcmp(params->name,"script")) - { - if(handle->script) - free(handle->script); - if(access(params->value,X_OK) == 0) - { - handle->script = strdup(params->value); - } - else - { - script_error = true; - if(access(params->value,F_OK) == 0) - { - skygw_log_write(LE, - "Error: The file cannot be executed: %s", - params->value); - } - else - { - skygw_log_write(LE, - "Error: The file cannot be found: %s", - params->value); - } - handle->script = NULL; - } - } + else if (!strcmp(params->name, "script")) + { + if (externcmd_can_execute(params->value)) + { + free(handle->script); + handle->script = strdup(params->value); + } + else + { + script_error = true; + } + } else if(!strcmp(params->name,"events")) { if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) @@ -659,10 +645,9 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); - if (server_string) { - database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); - if (database->server->server_string) - strcpy(database->server->server_string, server_string); + if (server_string) + { + server_set_version_string(database->server, server_string); } /* get server_id form current node */ diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index 02883c925..ea9de889f 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -127,29 +127,15 @@ startMonitor(void *arg,void* opt) { if(!strcmp(params->name,"script")) { - if(handle->script) - free(handle->script); - if(access(params->value,X_OK) == 0) - { - handle->script = strdup(params->value); - } - else - { - script_error = true; - if(access(params->value,F_OK) == 0) - { - skygw_log_write(LE, - "Error: The file cannot be executed: %s", - params->value); - } - else - { - skygw_log_write(LE, - "Error: The file cannot be found: %s", - params->value); - } - handle->script = NULL; - } + if (externcmd_can_execute(params->value)) + { + free(handle->script); + handle->script = strdup(params->value); + } + else + { + script_error = true; + } } else if(!strcmp(params->name,"events")) { @@ -277,11 +263,10 @@ char *server_string; /* get server version string */ server_string = (char *)mysql_get_server_info(database->con); - if (server_string) { - database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); - if (database->server->server_string) - strcpy(database->server->server_string, server_string); - } + if (server_string) + { + server_set_version_string(database->server, server_string); + } /* Check if the the SQL node is able to contact one or more data nodes */ if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0 diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 854ef1f28..5411b7dec 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -580,6 +580,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; inst->service->name))); if (service->users) { users_free(service->users); + service->users = NULL; } free(inst); @@ -658,6 +659,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; if (service->users) { users_free(service->users); + service->users = NULL; } if (service->dbref && service->dbref->server) { diff --git a/server/modules/routing/binlog/maxbinlogcheck.c b/server/modules/routing/binlog/maxbinlogcheck.c index 50e7e489a..8c8e0428f 100644 --- a/server/modules/routing/binlog/maxbinlogcheck.c +++ b/server/modules/routing/binlog/maxbinlogcheck.c @@ -86,8 +86,6 @@ return 1; } int main(int argc, char **argv) { - char** arg_vector; - int arg_count = 1; ROUTER_INSTANCE *inst; int fd; int ret; @@ -126,21 +124,9 @@ int main(int argc, char **argv) { num_args = optind; - arg_vector = malloc(sizeof(char*)*(arg_count + 1)); + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); - if(arg_vector == NULL) - { - fprintf(stderr,"Error: Memory allocation failed for log manager arg_vector.\n"); - return 1; - } - - arg_vector[0] = "logmanager"; - arg_vector[1] = NULL; - skygw_logmanager_init(NULL, arg_count, arg_vector); - - skygw_log_set_augmentation(0); - - free(arg_vector); + mxs_log_set_augmentation(0); if (!debug_out) skygw_log_disable(LOGFILE_DEBUG); @@ -151,8 +137,8 @@ int main(int argc, char **argv) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "Error: Memory allocation failed for ROUTER_INSTANCE"))); - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); return 1; } @@ -175,8 +161,8 @@ int main(int argc, char **argv) { "Failed to open binlog file %s: %s", path, strerror(errno)))); - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); free(inst); @@ -208,13 +194,13 @@ int main(int argc, char **argv) { close(inst->binlog_fd); - skygw_log_sync_all(); + mxs_log_flush_sync(); LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, "Check retcode: %i, Binlog Pos = %llu", ret, inst->binlog_position))); - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); free(inst); diff --git a/server/modules/routing/binlog/test/testbinlog.c b/server/modules/routing/binlog/test/testbinlog.c index da75005b4..ee4609517 100644 --- a/server/modules/routing/binlog/test/testbinlog.c +++ b/server/modules/routing/binlog/test/testbinlog.c @@ -73,11 +73,9 @@ static struct option long_options[] = { }; int main(int argc, char **argv) { - char** arg_vector; ROUTER_INSTANCE *inst; int ret; int rc; - int arg_count = 1; char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; CHANGE_MASTER_OPTIONS change_master; char query[255+1]=""; @@ -91,18 +89,7 @@ int main(int argc, char **argv) { roptions = strdup("server-id=3,heartbeat=200,binlogdir=/not_exists/my_dir,transaction_safety=1,master_version=5.6.99-common,master_hostname=common_server,master_uuid=xxx-fff-cccc-fff,master-id=999"); - arg_vector = malloc(sizeof(char*)*(arg_count + 1)); - - if(arg_vector == NULL) - { - fprintf(stderr,"Error: Memory allocation FAILED for log manager arg_vector.\n"); - return 1; - } - - arg_vector[0] = "logmanager"; - arg_vector[1] = NULL; - skygw_logmanager_init(NULL, arg_count,arg_vector); - free(arg_vector); + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); skygw_log_disable(LOGFILE_DEBUG); skygw_log_disable(LOGFILE_TRACE); @@ -140,8 +127,8 @@ int main(int argc, char **argv) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "Error: Memory allocation FAILED for ROUTER_INSTANCE"))); - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); return 1; } @@ -591,8 +578,8 @@ int main(int argc, char **argv) { return 1; } - skygw_log_sync_all(); - skygw_logmanager_done(); + mxs_log_flush_sync(); + mxs_log_finish(); free(inst); diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 894eec39e..51dbbf6cb 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -621,29 +621,48 @@ struct subcommand removeoptions[] = { static void flushlog(DCB *pdcb, char *logname) { - if (logname == NULL) + bool unrecognized = false; + bool deprecated = false; + + if (!strcasecmp(logname, "error")) { - } - else if (!strcasecmp(logname, "error")) - { - skygw_log_rotate(LOGFILE_ERROR); + deprecated = true; } else if (!strcasecmp(logname, "message")) { - skygw_log_rotate(LOGFILE_MESSAGE); + deprecated = true; } else if (!strcasecmp(logname, "trace")) { - skygw_log_rotate(LOGFILE_TRACE); + deprecated = true; } else if (!strcasecmp(logname, "debug")) { - skygw_log_rotate(LOGFILE_DEBUG); + deprecated = true; + } + else if (!strcasecmp(logname, "maxscale")) + { + ; // nop } else { - dcb_printf(pdcb, "Unexpected logfile name, expected " - "error, message, trace or debug.\n"); + unrecognized = true; + } + + if (unrecognized) + { + dcb_printf(pdcb, "Unexpected logfile name '%s', expected: 'maxscale'.\n", logname); + } + else + { + mxs_log_rotate(); + + if (deprecated) + { + dcb_printf(pdcb, + "'%s' is deprecated, currently there is only one log 'maxscale', " + "which was rotated.\n", logname); + } } } @@ -655,10 +674,7 @@ flushlog(DCB *pdcb, char *logname) static void flushlogs(DCB *pdcb) { - skygw_log_rotate(LOGFILE_ERROR); - skygw_log_rotate(LOGFILE_MESSAGE); - skygw_log_rotate(LOGFILE_TRACE); - skygw_log_rotate(LOGFILE_DEBUG); + mxs_log_rotate(); } @@ -678,8 +694,8 @@ struct subcommand flushoptions[] = { "logs", 0, flushlogs, - "Flush the content of all log files, close that logs, rename them and open a new log files", - "Flush the content of all log files, close that logs, rename them and open a new log files", + "Flush the content of all log files, close those logs, rename them and open a new log files", + "Flush the content of all log files, close those logs, rename them and open a new log files", {0, 0, 0} }, { @@ -1100,40 +1116,6 @@ restart_service(DCB *dcb, SERVICE *service) serviceRestart(service); } -static struct { - char *str; - unsigned int bit; -} ServerBits[] = { - { "running", SERVER_RUNNING }, - { "master", SERVER_MASTER }, - { "slave", SERVER_SLAVE }, - { "synced", SERVER_JOINED }, - { "ndb", SERVER_NDB }, - { "maintenance", SERVER_MAINT }, - { "maint", SERVER_MAINT }, - { NULL, 0 } -}; -/** - * Map the server status bit - * - * @param str String representation - * @return bit value or 0 on error - */ -static unsigned int -server_map_status(char *str) -{ - int i; - - for (i = 0; ServerBits[i].str; i++) - { - if (!strcasecmp(str, ServerBits[i].str)) - { - return ServerBits[i].bit; - } - } - return 0; -} - /** * Set the status bit of a server * @@ -1603,7 +1585,7 @@ static void enable_log_priority(DCB *dcb, char *arg1) if (priority != -1) { - mxs_log_enable_priority(priority); + mxs_log_set_priority_enabled(priority, true); } else { @@ -1621,7 +1603,7 @@ static void disable_log_priority(DCB *dcb, char *arg1) if (priority != -1) { - mxs_log_enable_priority(priority); + mxs_log_set_priority_enabled(priority, false); } else { diff --git a/server/modules/routing/maxinfo/maxinfo.c b/server/modules/routing/maxinfo/maxinfo.c index 04d74ca7c..edb13e94b 100644 --- a/server/modules/routing/maxinfo/maxinfo.c +++ b/server/modules/routing/maxinfo/maxinfo.c @@ -780,7 +780,7 @@ maxinfo_add_mysql_user(SERVICE *service) { "maxinfo: create hex_sha1_sha1_password failed for service user %s", service_user))); users_free(service->users); - + service->users = NULL; return 1; } diff --git a/server/modules/routing/maxinfo/maxinfo_exec.c b/server/modules/routing/maxinfo/maxinfo_exec.c index d51f81d0d..b3c59c1dd 100644 --- a/server/modules/routing/maxinfo/maxinfo_exec.c +++ b/server/modules/routing/maxinfo/maxinfo_exec.c @@ -54,7 +54,12 @@ static void exec_select(DCB *dcb, MAXINFO_TREE *tree); static void exec_show_variables(DCB *dcb, MAXINFO_TREE *filter); static void exec_show_status(DCB *dcb, MAXINFO_TREE *filter); static int maxinfo_pattern_match(char *pattern, char *str); - +static void exec_flush(DCB *dcb, MAXINFO_TREE *tree); +static void exec_set(DCB *dcb, MAXINFO_TREE *tree); +static void exec_clear(DCB *dcb, MAXINFO_TREE *tree); +static void exec_shutdown(DCB *dcb, MAXINFO_TREE *tree); +static void exec_restart(DCB *dcb, MAXINFO_TREE *tree); +void maxinfo_send_ok(DCB *dcb); /** * Execute a parse tree and return the result set or runtime error * @@ -72,6 +77,23 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree) case MAXOP_SELECT: exec_select(dcb, tree); break; + + case MAXOP_FLUSH: + exec_flush(dcb, tree); + break; + case MAXOP_SET: + exec_set(dcb, tree); + break; + case MAXOP_CLEAR: + exec_clear(dcb, tree); + break; + case MAXOP_SHUTDOWN: + exec_shutdown(dcb, tree); + break; + case MAXOP_RESTART: + exec_restart(dcb, tree); + break; + case MAXOP_TABLE: case MAXOP_COLUMNS: case MAXOP_LITERAL: @@ -274,6 +296,448 @@ char errmsg[120]; LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg))); } +/** + * Flush all logs to disk and rotate them. + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +void exec_flush_logs(DCB *dcb, MAXINFO_TREE *tree) +{ + mxs_log_rotate(); + maxinfo_send_ok(dcb); +} + +/** + * The table of flush commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} flush_commands[] = { + { "logs", exec_flush_logs}, + { NULL, NULL} +}; + +/** + * Execute a flush command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_flush(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; flush_commands[i].name; i++) + { + if (strcasecmp(flush_commands[i].name, tree->value) == 0) + { + (*flush_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported flush command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Set the server status. + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_set_server(DCB *dcb, MAXINFO_TREE *tree) +{ + SERVER* server = server_find_by_unique_name(tree->value); + char errmsg[120]; + + if (server) + { + int status = server_map_status(tree->right->value); + if (status != 0) + { + server_set_status(server, status); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->right->value) > 80) // Prevent buffer overrun + { + tree->right->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->right->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of set commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} set_commands[] = { + { "server", exec_set_server}, + { NULL, NULL} +}; + +/** + * Execute a set command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_set(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; set_commands[i].name; i++) + { + if (strcasecmp(set_commands[i].name, tree->value) == 0) + { + (*set_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported set command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Clear the server status. + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_clear_server(DCB *dcb, MAXINFO_TREE *tree) +{ + SERVER* server = server_find_by_unique_name(tree->value); + char errmsg[120]; + + if (server) + { + int status = server_map_status(tree->right->value); + if (status != 0) + { + server_clear_status(server, status); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->right->value) > 80) // Prevent buffer overrun + { + tree->right->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->right->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of clear commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} clear_commands[] = { + { "server", exec_clear_server}, + { NULL, NULL} +}; + +/** + * Execute a clear command parse tree and return the result set or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_clear(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; clear_commands[i].name; i++) + { + if (strcasecmp(clear_commands[i].name, tree->value) == 0) + { + (*clear_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported clear command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +extern void shutdown_server(); + +/** + * MaxScale shutdown + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_maxscale(DCB *dcb, MAXINFO_TREE *tree) +{ + shutdown_server(); + maxinfo_send_ok(dcb); +} + +/** + * Stop a monitor + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_monitor(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + MONITOR* monitor = monitor_find(tree->value); + if (monitor) + { + monitorStop(monitor); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'SHUTDOWN MONITOR'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * Stop a service + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_shutdown_service(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + SERVICE* service = service_find(tree->value); + if (service) + { + serviceStop(service); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'SHUTDOWN SERVICE'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of shutdown commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} shutdown_commands[] = { + { "maxscale", exec_shutdown_maxscale}, + { "monitor", exec_shutdown_monitor}, + { "service", exec_shutdown_service}, + { NULL, NULL} +}; + +/** + * Execute a shutdown command parse tree and return OK or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_shutdown(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; shutdown_commands[i].name; i++) + { + if (strcasecmp(shutdown_commands[i].name, tree->value) == 0) + { + (*shutdown_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported shutdown command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + +/** + * Restart a monitor + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_restart_monitor(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + MONITOR* monitor = monitor_find(tree->value); + if (monitor) + { + monitorStart(monitor, NULL); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'RESTART MONITOR'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * Restart a service + * @param dcb Client DCB + * @param tree Parse tree + */ +void exec_restart_service(DCB *dcb, MAXINFO_TREE *tree) +{ + char errmsg[120]; + if (tree && tree->value) + { + SERVICE* service = service_find(tree->value); + if (service) + { + serviceRestart(service); + maxinfo_send_ok(dcb); + } + else + { + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Invalid argument '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + } + } + else + { + sprintf(errmsg, "Missing argument for 'RESTART SERVICE'"); + maxinfo_send_error(dcb, 0, errmsg); + } +} + +/** + * The table of restart commands that are supported + */ +static struct +{ + char *name; + void (*func)(DCB *, MAXINFO_TREE *); +} restart_commands[] = { + { "monitor", exec_restart_monitor}, + { "service", exec_restart_service}, + { NULL, NULL} +}; + +/** + * Execute a restart command parse tree and return OK or runtime error + * + * @param dcb The DCB that connects to the client + * @param tree The parse tree for the query + */ +static void +exec_restart(DCB *dcb, MAXINFO_TREE *tree) +{ + int i; + char errmsg[120]; + + for (i = 0; restart_commands[i].name; i++) + { + if (strcasecmp(restart_commands[i].name, tree->value) == 0) + { + (*restart_commands[i].func)(dcb, tree->right); + return; + } + } + if (strlen(tree->value) > 80) // Prevent buffer overrun + { + tree->value[80] = 0; + } + sprintf(errmsg, "Unsupported restart command '%s'", tree->value); + maxinfo_send_error(dcb, 0, errmsg); + skygw_log_write(LE, errmsg); +} + /** * Return the current MaxScale version * @@ -764,3 +1228,25 @@ extern char *strcasestr(); return rval; } } + +/** + * Send an OK packet to the client. + * @param dcb The DCB that connects to the client + */ +void maxinfo_send_ok(DCB *dcb) +{ + static const char ok_packet[] ={ + 0x07, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 + }; + + GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet)); + + if (buffer) + { + memcpy(buffer->start, ok_packet, sizeof(ok_packet)); + dcb->func.write(dcb, buffer); + } +} diff --git a/server/modules/routing/maxinfo/maxinfo_parse.c b/server/modules/routing/maxinfo/maxinfo_parse.c index bdef7ef26..e63d6b2cf 100644 --- a/server/modules/routing/maxinfo/maxinfo_parse.c +++ b/server/modules/routing/maxinfo/maxinfo_parse.c @@ -50,7 +50,8 @@ static void free_tree(MAXINFO_TREE *); static char *fetch_token(char *, int *, char **); static MAXINFO_TREE *parse_column_list(char **sql); static MAXINFO_TREE *parse_table_name(char **sql); - +MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr, + PARSE_ERROR *parse_error); /** * Parse a SQL subset for the maxinfo plugin and return a parse tree @@ -111,6 +112,67 @@ MAXINFO_TREE *col, *table; table = parse_table_name(&ptr); return make_tree_node(MAXOP_SELECT, NULL, col, table); #endif + case LT_FLUSH: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + return make_tree_node(MAXOP_FLUSH, text, NULL, NULL); + + case LT_SHUTDOWN: + free(text); + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_SHUTDOWN, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) == NULL) + { + /** Possibly SHUTDOWN MAXSCALE */ + return tree; + } + tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) != NULL) + { + /** Unknown token after SHUTDOWN MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + return tree; + + case LT_RESTART: + free(text); + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_RESTART, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) == NULL) + { + /** Missing token for RESTART MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL); + + if ((ptr = fetch_token(ptr, &token, &text)) != NULL) + { + /** Unknown token after RESTART MONITOR|SERVICE */ + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + return NULL; + } + return tree; + + case LT_SET: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_SET, text, NULL, NULL); + return maxinfo_parse_literals(tree, 2, ptr, parse_error); + + case LT_CLEAR: + free(text); // not needed + ptr = fetch_token(ptr, &token, &text); + tree = make_tree_node(MAXOP_CLEAR, text, NULL, NULL); + return maxinfo_parse_literals(tree, 2, ptr, parse_error); + break; default: *parse_error = PARSE_SYNTAX_ERROR; return NULL; @@ -231,18 +293,24 @@ free_tree(MAXINFO_TREE *tree) /** * The set of keywords known to the tokeniser */ -static struct { - char *text; - int token; +static struct +{ + char *text; + int token; } keywords[] = { - { "show", LT_SHOW }, - { "select", LT_SELECT }, - { "from", LT_FROM }, - { "like", LT_LIKE }, - { "=", LT_EQUAL }, - { ",", LT_COMMA }, - { "*", LT_STAR }, - { NULL, 0 } + { "show", LT_SHOW}, + { "select", LT_SELECT}, + { "from", LT_FROM}, + { "like", LT_LIKE}, + { "=", LT_EQUAL}, + { ",", LT_COMMA}, + { "*", LT_STAR}, + { "flush", LT_FLUSH}, + { "set", LT_SET}, + { "clear", LT_CLEAR}, + { "shutdown", LT_SHUTDOWN}, + { "restart", LT_RESTART}, + { NULL, 0} }; /** @@ -322,3 +390,36 @@ int i; *token = LT_STRING; return s2; } + +/** + * Parse the remaining arguments as literals. + * @param tree Previous head of the parse tree + * @param min_args Minimum required number of arguments + * @param ptr Pointer to client command + * @param parse_error Pointer to parsing error to fill + * @return Parsed tree or NULL if parsing failed + */ +MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr, + PARSE_ERROR *parse_error) +{ + int token; + MAXINFO_TREE* node = tree; + char *text; + for(int i = 0; i < min_args; i++) + { + if((ptr = fetch_token(ptr, &token, &text)) == NULL || + (node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL) + { + *parse_error = PARSE_SYNTAX_ERROR; + free_tree(tree); + if(ptr) + { + free(text); + } + return NULL; + } + node = node->right; + } + + return tree; +} diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 9f9efb11b..359d98cdd 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -55,11 +55,11 @@ # define ss_dassert(exp) if(!(exp)){(skygw_log_write(LE,\ "debug assert %s:%d\n", \ (char*)__FILE__, \ - __LINE__));skygw_log_sync_all();assert(exp);} + __LINE__));mxs_log_flush_sync();assert(exp);} #define ss_info_dassert(exp,info) if(!(exp)){(skygw_log_write(LE,\ "debug assert %s:%d %s\n", \ (char*)__FILE__, \ - __LINE__,info));skygw_log_sync_all();assert(exp);} + __LINE__,info));mxs_log_flush_sync();assert(exp);} # define ss_debug(exp) exp # define ss_dfprintf fprintf # define ss_dfflush fflush