diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 1ee140255..000000000 Binary files a/.DS_Store and /dev/null differ diff --git a/CMakeLists.txt b/CMakeLists.txt index dcf65c091..fa801353c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ find_package(Git) find_package(CURL) # Build PCRE2 +# Read BuildPCRE2 for details about how to add pcre2 as a dependency to a target include(cmake/BuildPCRE2.cmake) # You can find the variables set by this in the FindCURL.cmake file diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 2dbd116ea..fa304852f 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -10,7 +10,7 @@ These are the changes introduced in the next MaxScale version. This is not the o ## MaxScale 1.1.1 -* Schemarouter now also allows for an upper limit to session commans. +* Schemarouter now also allows for an upper limit to session commands. * Schemarouter correctly handles SHOW DATABASES responses that span multiple buffers. * Readwritesplit and Schemarouter now allow disabling of the session command history. diff --git a/Documentation/Design-Documents/SchemaRouter-technical.md b/Documentation/Design-Documents/SchemaRouter-technical.md index fc6e48f81..6c42e9563 100644 --- a/Documentation/Design-Documents/SchemaRouter-technical.md +++ b/Documentation/Design-Documents/SchemaRouter-technical.md @@ -4,19 +4,19 @@ This document is designed with a developer's point-of-view in mind. It explains ## Source Files and Data Structures -The schemarouter router consists of the schemarouter.h header file located in the `server/modules/include/` directory and the schemarouter.c file located in the `server/modules/routing/schemarouter` directory. This router implements the router interface defined in the router.h file. The entry points and structures this router uses can be found in the header file. The two main structures in use are the router instace and router session structures. The router instance structure is defined in `struct router_instance` and the router session structure in `struct router_client_session`. +The schemarouter router consists of the schemarouter.h header file located in the `server/modules/include/` directory and the schemarouter.c file located in the `server/modules/routing/schemarouter` directory. This router implements the router interface defined in the router.h file. The entry points and structures this router uses can be found in the header file. The two main structures in use are the router instance and router session structures. The router instance structure is defined in `struct router_instance` and the router session structure in `struct router_client_session`. The definitions of the external functions and all the internal functions of the router can be found in the schemarouter.c file. ## Router Lifecycle -When MaxScale first starts, it creates all services and thus creates the router instances of all the routers. The functions involved in this stage are ModuleInit, which is called only once when MaxScale first starts, and createInstance, called for each individual instace of this router in all the configured services. These functions read configuraion values and initialize internal data. +When MaxScale first starts, it creates all services and thus creates the router instances of all the routers. The functions involved in this stage are ModuleInit, which is called only once when MaxScale first starts, and createInstance, called for each individual instance of this router in all the configured services. These functions read configuration values and initialize internal data. When a user connects to MaxScale, a new session is created and the newSession function is called. At this point the client session connects to all the backend servers and initializes the list of databases. After the session is created queries are routed to the router's routeQuery function. This is where most of the work regarding the resolution of query destinations is done. This router parses the incoming buffers for full SQL packets first and routes each of them individually. The main internal functions involved in routing the query are get_shard_route_target (detects if a query needs to be sent to all the servers or to a specific one), get_shard_target_name (parses the query and finds the name of the right server) and route_session_write (handles sending and and storing session commands). After this point the client's query has been sent to the backend server and the router waits for either an response or an error signaling that the backend server is not responding. -If a response is received the clientReply function is called and response is simply sent to the client and the router is then ready for more queries. If there is no response from the server and the connection to it is lost the handleError function is called. This function tries to find replacement servers for the failed ones and regenerates the list of databases. This also triggeres the sending of an error packet to the client that notifies that the server is not responding. +If a response is received the clientReply function is called and response is simply sent to the client and the router is then ready for more queries. If there is no response from the server and the connection to it is lost the handleError function is called. This function tries to find replacement servers for the failed ones and regenerates the list of databases. This also triggers the sending of an error packet to the client that notifies that the server is not responding. After the session ends the closeSession is called where the session is set to a closed state after which the freeSession is called where the final freeing of memory is done. After this point the router's session has gone through all the stages of its lifecycle. diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 14248e222..527590b28 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -50,7 +50,7 @@ ## Routers - [Read Write Split](Routers/ReadWriteSplit.md) - - [Read Connnection Router](Routers/ReadConnRoute.md) + - [Read Connection Router](Routers/ReadConnRoute.md) - [Schemarouter](Routers/SchemaRouter.md) ## Filters diff --git a/Documentation/Filters/RabbitMQ-Consumer-Client.md b/Documentation/Filters/RabbitMQ-Consumer-Client.md index 324d8623e..1efd39507 100644 --- a/Documentation/Filters/RabbitMQ-Consumer-Client.md +++ b/Documentation/Filters/RabbitMQ-Consumer-Client.md @@ -14,7 +14,7 @@ The **RabbitMQ Consumer Client** only has one command line argument. ## Installation -To install the RabbitMQ Consumer Client you ca either use the provided packages or you can compile it from source code. The source code is included as a part of the MaxScale source code and can be found in the `rabbtmq_consumer` folder. Please refer to the [README](../../rabbitmq_consumer/README) in the folder for more detailed instructions about installation and configuration. +To install the RabbitMQ Consumer Client you ca either use the provided packages or you can compile it from source code. The source code is included as a part of the MaxScale source code and can be found in the `rabbitmq_consumer` folder. Please refer to the [README](../../rabbitmq_consumer/README) in the folder for more detailed instructions about installation and configuration. ## Configuration diff --git a/Documentation/Filters/Regex-Filter.md b/Documentation/Filters/Regex-Filter.md index ce0f54358..db99debdb 100644 --- a/Documentation/Filters/Regex-Filter.md +++ b/Documentation/Filters/Regex-Filter.md @@ -68,7 +68,7 @@ user=john ### `log_file` -The optional log_file parameter defines a log file in which the filter writes all queries that are not mached and maching queries with their replacement queries. All sessions will log to this file so this should only be used for diagnostic purposes. +The optional log_file parameter defines a log file in which the filter writes all queries that are not matched and matching queries with their replacement queries. All sessions will log to this file so this should only be used for diagnostic purposes. ``` log_file=/tmp/regexfilter.log diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index a4af5529c..300a1db1c 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -22,6 +22,23 @@ connection failover| When a connection currently being used between MaxScale and backend database | A term used to refer to a database that sits behind MaxScale and is accessed by applications via MaxScale. filter | A module that can be placed between the client and the MaxScale router module. All client data passes through the filter module and may be examined or modified by the filter modules. Filters may be chained together to form processing pipelines. +# Table of Contents + +* [Configuration](#configuration) + * [Global Settings](#global-settings) + * [Service](#service) + * [Service and SSL](#service-and-ssl) + * [Server](#server) + * [Listener](#listener) + * [Filter](#filter) + * [Monitor](#monitor) + * [Protocol](#protocol) +* [Router Modules](#router-modules) +* [Monitor Modules](#monitor-modules) +* [Filter Modules](#filter-modules) +* [Reloading Configuration](#reloading-configuration) +* [Authentication](#authentication) +* [Error Reporting](#error-reporting) ## Configuration @@ -354,6 +371,12 @@ Enabling this feature will transform wildcard grants to individual database gran The retry_on_failure parameter controls whether MaxScale will try to restart failed services and accepts a boolean value. This functionality is enabled by default to prevent services being permanently disabled if the starting of the service failed due to a network outage. Disabling the restarting of the failed services will cause them to be permanently disabled if the services can't be started when MaxScale is started. +#### `log_auth_warnings` + +Enable or disable the logging of authentication failures and warnings. This parameter takes a boolean value. + +MaxScale normally suppresses warning messages about failed authentication. Enabling this option will log those messages into the message log with details about who tried to connect to MaxScale and from where. + #### `connection_timeout` The connection_timeout parameter is used to disconnect sessions to MaxScale that have been idle for too long. The session timeouts are disabled by default. To enable them, define the timeout in seconds in the service's configuration section. @@ -696,7 +719,7 @@ Default value is `1`. Read Timeout is the timeout in seconds for each attempt to Default value is `2`. Write Timeout is the timeout in seconds for each attempt to write to the server. There is a retry if necessary, so the total effective timeout value is two times the option value. That's for `mysql_real_connect` C API. -## Protocol Modules +## Protocol The protocols supported by MaxScale are implemented as external modules that are loaded dynamically into the MaxScale core. These modules reside in the directory `/usr/lib64/maxscale`. The location can be overridden with the `libdir=PATH` parameter under the `[maxscale]` section. It may also be set by passing the `-B PATH` or `--libdir=PATH` option on the MaxScale command line. @@ -955,7 +978,7 @@ filters=qla|fetch|from In addition to this, readwritesplit needs configuration for a listener, for all servers listed, and for each filter. Listener, server - and filter configurations are described in their own sections in this document. -An important parameter is the `max_slave_connections=50%` parameter. This sets the number of slaves each client connection will use. With the default values, client connections will only use a single slave for reads. For example, setting the parameter value to 100% will use all available slaves and read queries will be balanced evenly across all slaves. Changing the `max_slave_conections` parameter and `slave_selection_criteria` router option allows you to change the way MaxScale will balance reads. For more information about the `slave_selection_criteria` router option, please read the ReadWriteSplit documentation. +An important parameter is the `max_slave_connections=50%` parameter. This sets the number of slaves each client connection will use. With the default values, client connections will only use a single slave for reads. For example, setting the parameter value to 100% will use all available slaves and read queries will be balanced evenly across all slaves. Changing the `max_slave_connections` parameter and `slave_selection_criteria` router option allows you to change the way MaxScale will balance reads. For more information about the `slave_selection_criteria` router option, please read the ReadWriteSplit documentation. Below is a listener example for the "RWSplit Service" defined above: 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/Reference/Debug-And-Diagnostic-Support.md b/Documentation/Reference/Debug-And-Diagnostic-Support.md index 12298624c..e0b8ab505 100644 --- a/Documentation/Reference/Debug-And-Diagnostic-Support.md +++ b/Documentation/Reference/Debug-And-Diagnostic-Support.md @@ -1837,7 +1837,7 @@ MariaDB Corporation MaxScale /home/jdoe/bin/develop/log/skygw_msg1.log Tue Dec ### Trace log -Trace log includes information about available servers and their states, client sessions, queries being executed, routing decisions and other routing related data. Trace log can be found from the same directory with other logs but it is physically stored elsewhere, to OSs shared memory to reduce the latency caused by logging. The location of physical file is : /dev/shm//skygw_traceX.log where ‘X’ is the same sequence number as in the file name in the /var/log/maxscale directory. +Trace log includes information about available servers and their states, client sessions, queries being executed, routing decisions and other routing related data. Trace log can be found from the same directory with other logs but it is physically stored elsewhere, to OS's shared memory to reduce the latency caused by logging. The location of physical file is : /dev/shm//skygw_traceX.log where ‘X’ is the same sequence number as in the file name in the /var/log/maxscale directory. Individual trace log entry looks similar to those in other logs but there is some difference too. Some log entries include a number within square brackets to specify which client session they belong to. For example: diff --git a/Documentation/Reference/Hint-Syntax.md b/Documentation/Reference/Hint-Syntax.md index 4f5d17f30..ef7ccfd51 100644 --- a/Documentation/Reference/Hint-Syntax.md +++ b/Documentation/Reference/Hint-Syntax.md @@ -29,7 +29,7 @@ The client connection will need to have comments enabled. For example the `mysql For comment types, use either `-- ` (notice the whitespace) or `#` after the semicolon or `/* .. */` before the semicolon. All comment types work with routing hints. The MySQL manual doesn`t specify if comment blocks, i.e. `/* .. */`, should contain a w -hitespace character before or after the tags, so adding whitespace at both the start and the end is advised. +whitespace character before or after the tags, so adding whitespace at both the start and the end is advised. ## Hint body diff --git a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md index af6a897d3..873af6b68 100644 --- a/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md +++ b/Documentation/Reference/MaxScale-HA-with-Corosync-Pacemaker.md @@ -318,7 +318,7 @@ The script exit code for "status" is 0 Note: the MaxScale script is LSB compatible and returns the proper exit code for each action: -For additional informations; +For additional information; [http://www.linux-ha.org/wiki/LSB_Resource_Agents](http://www.linux-ha.org/wiki/LSB_Resource_Agents) diff --git a/Documentation/Reference/MaxScale-HA-with-lsyncd.md b/Documentation/Reference/MaxScale-HA-with-lsyncd.md index c9e6ac332..e8184ba6b 100644 --- a/Documentation/Reference/MaxScale-HA-with-lsyncd.md +++ b/Documentation/Reference/MaxScale-HA-with-lsyncd.md @@ -4,7 +4,7 @@ This document guides you in setting up multiple MaxScale instances and synchronizing the configuration files with lsyncd. Lsyncd is a rsync wrapper which can synchronize files across the network. The lsyncd daemon uses a configuration file to control the files to synchronize and the remote targets where these files are synchronized to. -Copying the configuration file and running the lsyncd daemon on all the hosts keeps all the configuration files in sync. Modifications in the configuration file on one of the hosts will be copied on the other hosts. This allows adinistrators to easily provide a highly available, disaster resistant MaxScale installation with up-to-date configuration files on all the hosts. +Copying the configuration file and running the lsyncd daemon on all the hosts keeps all the configuration files in sync. Modifications in the configuration file on one of the hosts will be copied on the other hosts. This allows administrators to easily provide a highly available, disaster resistant MaxScale installation with up-to-date configuration files on all the hosts. ### Requirements You will need: @@ -127,9 +127,9 @@ The most important part is the `sync` section which defines a target for synchro The `source` parameter tells lsyncd where to read the files from. This should be the location of the maxscale.cnf file. The `host` parameter defines the host where the files should be synchronized to and the user account lsyncd should use when synchronizing the files. The `targetdir` parameter defines the local directory on the remote target where the files should be synchronized to. This value should be the location on the remote host where the maxscale.cnf file is searched from. By default, this is the `/etc` folder. -The optional `ssh` parameter and its sub-parameter `port`define a custom port for the SSH connection. Most users do not need this parameterer. The `rsycn` parameter contains an arra of options that are passed to the rsycn executable. These should not be changed unless you specifically know what you are doing. For more information on the options passed to rsync read the rsync(1) manpage. +The optional `ssh` parameter and its sub-parameter `port`define a custom port for the SSH connection. Most users do not need this parameter. The `rsycn` parameter contains an array of options that are passed to the rsycn executable. These should not be changed unless you specifically know what you are doing. For more information on the options passed to rsync read the rsync(1) manpage. -You can add multiple remote targets by defining multiple `sync` sections. Here is an example with two sync sections defining different hosts that have MaxScale installed and whose configuration files should be kep in sync. +You can add multiple remote targets by defining multiple `sync` sections. Here is an example with two sync sections defining different hosts that have MaxScale installed and whose configuration files should be kept in sync. ``` settings{ @@ -167,7 +167,7 @@ rsync={ ## Starting Lsyncd -Starting lsyncd can be done from the command line or through a init script. To start syncd from the command like, execute the `lsyncd` command and pass the configuration file as the only parameter. +Starting lsyncd can be done from the command line or through a init script. To start lsyncd from the command like, execute the `lsyncd` command and pass the configuration file as the only parameter. By default lsyncd will search for the configuration file in `/etc/lsyncd.conf`. By placing the configuration file we created in the `/etc` folder, we can start lsyncd with the following command. diff --git a/Documentation/Routers/ReadConnRoute.md b/Documentation/Routers/ReadConnRoute.md index 50d5b80ae..c1994e439 100644 --- a/Documentation/Routers/ReadConnRoute.md +++ b/Documentation/Routers/ReadConnRoute.md @@ -1,6 +1,6 @@ # Readconnroute -This document provides anoverview of the **readconnroute** router module and its intended use case scenarios. It also displays all router configuration parameters with their descriptions. +This document provides an overview of the **readconnroute** router module and its intended use case scenarios. It also displays all router configuration parameters with their descriptions. ## Overview @@ -63,7 +63,7 @@ servers=server1,server2 weightby=serv_weight ``` -With this configuration and a heavy query load, the server *server1* will get most of the connections and about a third of the remaining queries are routed to the second server. With server weights, you can assing secondary servers that are only used when the primary server is under heavy load. +With this configuration and a heavy query load, the server *server1* will get most of the connections and about a third of the remaining queries are routed to the second server. With server weights, you can assign secondary servers that are only used when the primary server is under heavy load. Without the weightby parameter, each connection counts as a single connection. With a weighting parameter, a single connection received its weight from the server's own weighting parameter divided by the sum of all weighting parameters in all the configured servers. diff --git a/Documentation/Tutorials/Administration-Tutorial.md b/Documentation/Tutorials/Administration-Tutorial.md index b6ea00964..896c004e9 100644 --- a/Documentation/Tutorials/Administration-Tutorial.md +++ b/Documentation/Tutorials/Administration-Tutorial.md @@ -223,5 +223,5 @@ To bring the server back into service use the "clear server" command to clear th MaxScale> clear server dbserver3 maintenance MaxScale> ``` -Note that maintenance mode is not persistent, if MaxScale restarts when a node is in maintenance mode a new instance of MaxScale will not honour this mode. If multiple MaxScale instances are configured to use the node them maintenance mode must be set within each MaxScale instance. However if multiple services within one MaxScale instance are using the server then you only need set the maintenance mode once on the server for all services to take note of the mode change. +Note that maintenance mode is not persistent, if MaxScale restarts when a node is in maintenance mode a new instance of MaxScale will not honor this mode. If multiple MaxScale instances are configured to use the node them maintenance mode must be set within each MaxScale instance. However if multiple services within one MaxScale instance are using the server then you only need set the maintenance mode once on the server for all services to take note of the mode change. diff --git a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md index 18ca0f99c..d0f0b6594 100644 --- a/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/Galera-Cluster-Read-Write-Splitting-Tutorial.md @@ -154,7 +154,7 @@ This monitor module will assign one node within the Galera Cluster as the curren [Galera Monitor] type=monitor module=galeramon -diable_master_failback=1 +disable_master_failback=1 servers=dbserv1, dbserv2, dbserv3 user=maxscale passwd=96F99AA1315BDC3604B006F427DD9484 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/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md index 577731b3e..572d9482b 100644 --- a/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md +++ b/Documentation/Tutorials/MySQL-Replication-Read-Write-Splitting-Tutorial.md @@ -150,7 +150,7 @@ port=3306 socket=/tmp/ClusterMaster ``` -An address parameter may be given if the listener is required to bind to a particular network address when using hosts with multiple network addresses. The default behaviour is to listen on all network interfaces. +An address parameter may be given if the listener is required to bind to a particular network address when using hosts with multiple network addresses. The default behavior is to listen on all network interfaces. The next stage is the configuration is to define the server information. This defines how to connect to each of the servers within the cluster, again a section is created for each server, with the type set to server, the network address and port to connect to and the protocol to use to connect to the server. Currently the protocol module for all database connections in MySQLBackend. ``` diff --git a/Documentation/Tutorials/Nagios-Plugins.md b/Documentation/Tutorials/Nagios-Plugins.md index 1a90ff74e..bf91baaf3 100644 --- a/Documentation/Tutorials/Nagios-Plugins.md +++ b/Documentation/Tutorials/Nagios-Plugins.md @@ -29,7 +29,7 @@ While MaxScale resources and status can be monitored via CLI using maxadmin comm ![Nagios and MaxScale interaction](images/HowMaxScaleWorksWithNagios.png) -There are three nagios plugin scripts that MaxScale provides. +There are three Nagios plugin scripts that MaxScale provides. 1. check_maxscale_threads.pl: This command provides you the status of current running threads and events in the queue on MaxScale Server. The Performance data associated with this command current and historic wait time for threads and events @@ -38,7 +38,7 @@ Current resources are: modules, services, listeners, servers, sessions, filters. 3. check_maxscale_monitor.pl: This command provides you status of the configured monitor modules on MaxScale server. -In order to use these scripts on your Nagios Server, you need to copy them from the MaxScale binary package or download them from source tree on github. +In order to use these scripts on your Nagios Server, you need to copy them from the MaxScale binary package or download them from source tree on GitHub. # MaxScale Nagios Plugin Requirements diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md index 060b852c7..4ea40ef3a 100644 --- a/Documentation/Tutorials/Notification-Service.md +++ b/Documentation/Tutorials/Notification-Service.md @@ -53,7 +53,7 @@ MaxScale generates the feedback report containing following information: - MaxScale Version - An identifier of the MaxScale installation, i.e. the HEX encoding of SHA1 digest of the first network interface MAC address - Operating System (i.e Linux) - - Operating Suystem Distribution (i.e. CentOS release 6.5 (Final)) + - Operating System Distribution (i.e. CentOS release 6.5 (Final)) - All the modules in use in MaxScale and their API and version - MaxScale server UNIX_TIME at generation time diff --git a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md index 7ffff76c2..f5a6b57af 100644 --- a/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md +++ b/Documentation/Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md @@ -364,7 +364,7 @@ If the consumer.cnf file is not in the same directory as the binary file is, you # ./consumer -c path/to/file ``` -and start maxScale as well +and start MaxScale as well ## Step 6 - Test the filter and check collected data diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 32ab702ea..eb13ae659 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -8,3 +8,7 @@ else() message(STATUS "Could not find editline library. MaxAdmin will be built without it.") endif() install(TARGETS maxadmin DESTINATION ${MAXSCALE_BINDIR}) + +if(BUILD_TESTS) + add_subdirectory(test) +endif() diff --git a/client/maxadmin.c b/client/maxadmin.c index dec941d2f..6f8899163 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -466,7 +466,8 @@ int i, j, newline = 1; { if (newline == 1 && buf[j] == 'O') newline = 2; - else if (newline == 2 && buf[j] == 'K' && j == i - 1) + else if ((newline == 2 && buf[j] == 'K' && j == i - 1) || + (j == i - 2 && buf[j] == 'O' && buf[j + 1] == 'K')) { return 1; } diff --git a/client/test/CMakeLists.txt b/client/test/CMakeLists.txt new file mode 100644 index 000000000..e59eaf7f0 --- /dev/null +++ b/client/test/CMakeLists.txt @@ -0,0 +1,4 @@ +install(PROGRAMS maxadmin_test.sh DESTINATION ${CMAKE_BINARY_DIR}) +install(PROGRAMS maxadmin_stress.sh DESTINATION ${CMAKE_BINARY_DIR}) +add_test(TestMaxAdmin ${CMAKE_BINARY_DIR}/maxadmin_test.sh) +add_test(TestMaxAdminStress ${CMAKE_BINARY_DIR}/maxadmin_stress.sh) diff --git a/client/test/maxadmin_stress.sh b/client/test/maxadmin_stress.sh old mode 100644 new mode 100755 diff --git a/client/test/maxadmin_test.sh b/client/test/maxadmin_test.sh index c58c1512e..03976bc90 100755 --- a/client/test/maxadmin_test.sh +++ b/client/test/maxadmin_test.sh @@ -96,61 +96,45 @@ fi # # Test enable|disable log debug|trace|message|error # -maxadmin -pmariadb enable log debug >& /dev/null -if [ $? -eq "1" ]; then - echo "Enable debug log: Failed" - failure=`expr $failure + 1` -else - passed=`expr $passed + 1` - echo "Enable debug log: Passed" -fi -maxadmin -pmariadb enable log trace >& /dev/null -if [ $? -eq "1" ]; then - echo "Enable trace log: Failed" - failure=`expr $failure + 1` -else - passed=`expr $passed + 1` - echo "Enable trace log: Passed" -fi +for action in enable disable +do + maxadmin -pmariadb $action log debug >& /dev/null + if [ $? -eq "1" ]; then + echo "$action debug log: Failed" + failure=`expr $failure + 1` + else + passed=`expr $passed + 1` + echo "$action debug log: Passed" + fi -maxadmin -pmariadb enable log message >& /dev/null -if [ $? -eq "1" ]; then - echo "Enable message log: Failed" + maxadmin -pmariadb $action log trace >& /dev/null + if [ $? -eq "1" ]; then + echo "$action trace log: Failed" + failure=`expr $failure + 1` + else + passed=`expr $passed + 1` + echo "$action trace log: Passed" + fi + + maxadmin -pmariadb $action log message >& /dev/null + if [ $? -eq "1" ]; then + echo "$action message log: Failed" failure=`expr $failure + 1` -else + else passed=`expr $passed + 1` - echo "Enable message log: Passed" -fi + echo "$action message log: Passed" + fi -maxadmin -pmariadb enable log error >& /dev/null -if [ $? -eq "1" ]; then - echo "Enable error log: Failed" + maxadmin -pmariadb $action log error >& /dev/null + if [ $? -eq "1" ]; then + echo "$action error log: Failed" failure=`expr $failure + 1` -else + else passed=`expr $passed + 1` - echo "Enable error log: Passed" -fi - - - -maxadmin -pmariadb disable log debug >& /dev/null -if [ $? -eq "1" ]; then - echo "Disable debug log: Failed" - failure=`expr $failure + 1` -else - passed=`expr $passed + 1` - echo "Disable debug log: Passed" -fi - -maxadmin -pmariadb disable log trace >& /dev/null -if [ $? -eq "1" ]; then - echo "Disable trace log: Failed" - failure=`expr $failure + 1` -else - passed=`expr $passed + 1` - echo "Disable trace log: Passed" -fi + echo "$action error log: Passed" + fi +done # # Test restart monitor|service without, with invalid and with long invalid argument @@ -186,7 +170,7 @@ do done # -# Test set server qwerty master withaout, with invalid and with long invalid arg +# Test set server qwerty master without, with invalid and with long invalid arg # maxadmin -pmariadb set server qwerty >& /dev/null if [ $? -eq "1" ]; then diff --git a/cmake/BuildPCRE2.cmake b/cmake/BuildPCRE2.cmake index 706808798..f1b636be9 100644 --- a/cmake/BuildPCRE2.cmake +++ b/cmake/BuildPCRE2.cmake @@ -1,16 +1,22 @@ # Build the PCRE2 library from source +# +# This will add a 'pcre2' target to CMake which will generate the libpcre2-8.so +# dynamic library and the pcre2.h header. If your target requires PCRE2 you +# need to add a dependeny on the 'pcre2' target by adding add_dependencies( pcre2) +# to the CMakeLists.txt + set(PCRE_ROOT_DIR ${CMAKE_SOURCE_DIR}/pcre2/) set(PCRE_BUILD_DIR ${CMAKE_BINARY_DIR}/pcre2/) - +set(PCRE2_LIBRARIES ${CMAKE_BINARY_DIR}/pcre2/libpcre2-8.so CACHE STRING "PCRE2 dynamic libraries" FORCE) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PCRE_BUILD_DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${PCRE_ROOT_DIR} ${PCRE_BUILD_DIR}) -execute_process(COMMAND ${CMAKE_COMMAND} ${PCRE_BUILD_DIR} + +add_custom_target(pcre2 COMMAND ${CMAKE_COMMAND} ${PCRE_BUILD_DIR} -DBUILD_SHARED_LIBS=Y -DPCRE2_BUILD_PCRE2GREP=N -DPCRE2_BUILD_TESTS=N + COMMAND make WORKING_DIRECTORY ${PCRE_BUILD_DIR}) -execute_process(COMMAND make WORKING_DIRECTORY ${PCRE_BUILD_DIR}) -set(PCRE2_LIBRARIES ${CMAKE_BINARY_DIR}/pcre2/libpcre2-8.so CACHE STRING "PCRE2 dynamic libraries" FORCE) include_directories(${CMAKE_BINARY_DIR}/pcre2/) install(PROGRAMS ${PCRE2_LIBRARIES} DESTINATION ${MAXSCALE_LIBDIR}) 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 ca1785674..81b386d66 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -43,19 +43,79 @@ # define _GNU_SOURCE #endif +static const char LOGFILE_NAME_PREFIX[] = "error"; +static const char LOGFILE_NAME_SUFFIX[] = ".log"; + extern char *program_invocation_name; extern char *program_invocation_short_name; +typedef enum +{ + BB_READY = 0x00, + BB_FULL, + BB_CLEARED +} blockbuf_state_t; + +typedef enum +{ + FILEWRITER_INIT, + FILEWRITER_RUN, + FILEWRITER_DONE +} filewriter_state_t; + +/** + * UNINIT means zeroed memory buffer allocated for the struct. + * INIT means that struct members may have values, and memory may + * have been allocated. Done function must check and free it. + * RUN Struct is valid for run-time checking. + * DONE means that possible memory allocations have been released. + */ +typedef enum +{ + UNINIT = 0, + INIT, + RUN, + DONE +} flat_obj_state_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 +}; + #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 @@ -63,6 +123,12 @@ static int use_stdout = 0; */ int lm_enabled_logfiles_bitmask = 0; +/** + * Variable holding the enabled priorities information. + * Used from logging macros. + */ +int lm_enabled_priorities_bitmask = 0; + /** * Thread-specific struct variable for storing current session id and currently * enabled log files for the session. @@ -87,12 +153,6 @@ 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; - /** Forward declarations */ typedef struct filewriter filewriter_t; @@ -110,12 +170,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; @@ -129,7 +183,7 @@ struct filewriter flat_obj_state_t fwr_state; logmanager_t* fwr_logmgr; /** Physical files */ - skygw_file_t* fwr_file[LOGFILE_LAST + 1]; + skygw_file_t* fwr_file; /** fwr_logmes is for messages from log clients */ skygw_message_t* fwr_logmes; /** fwr_clientmes is for messages to log clients */ @@ -149,7 +203,6 @@ typedef struct blockbuf #if defined(SS_DEBUG) skygw_chk_t bb_chk_top; #endif - logfile_id_t bb_fileid; blockbuf_state_t bb_state; /**State of the block buffer*/ simple_mutex_t bb_mutex; /**< bb_buf_used, bb_isfull */ int bb_refcount; /**< protected by list mutex. #of clients */ @@ -174,17 +227,14 @@ struct logfile #endif flat_obj_state_t lf_state; bool lf_init_started; - bool lf_enabled; 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; - logfile_id_t lf_id; char* lf_filepath; /**< path to file used for logging */ char* lf_linkpath; /**< path to symlink file. */ - char* lf_name_prefix; - char* lf_name_suffix; + const char* lf_name_prefix; + const char* lf_name_suffix; int lf_name_seqno; char* lf_full_file_name; /**< complete log file name */ char* lf_full_link_name; /**< complete symlink name */ @@ -209,14 +259,6 @@ struct fnames_conf skygw_chk_t fn_chk_top; #endif flat_obj_state_t fn_state; - char* fn_debug_prefix; - char* fn_debug_suffix; - char* fn_trace_prefix; - char* fn_trace_suffix; - char* fn_msg_prefix; - char* fn_msg_suffix; - char* fn_err_prefix; - char* fn_err_suffix; char* fn_logpath; #if defined(SS_DEBUG) skygw_chk_t fn_chk_tail; @@ -237,8 +279,9 @@ struct logmanager /** fwr_clientmes is for messages to log clients */ skygw_message_t* lm_clientmes; fnames_conf_t lm_fnames_conf; - logfile_t lm_logfile[LOGFILE_LAST + 1]; + logfile_t lm_logfile; filewriter_t lm_filewriter; + log_target_t lm_target; #if defined(SS_DEBUG) skygw_chk_t lm_chk_tail; #endif @@ -251,7 +294,7 @@ struct logmanager typedef struct strpart { - char* sp_string; + const char* sp_string; struct strpart* sp_next; } strpart_t; @@ -259,10 +302,8 @@ typedef struct strpart /** Static function declarations */ static bool logfiles_init(logmanager_t* lmgr); static bool logfile_init(logfile_t* logfile, - logfile_id_t logfile_id, 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); @@ -271,40 +312,36 @@ 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, 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 char* fname_conf_get_prefix(fnames_conf_t* fn, logfile_id_t id); -static char* fname_conf_get_suffix(fnames_conf_t* fn, logfile_id_t id); static void* thr_filewriter_fun(void* data); -static logfile_t* logmanager_get_logfile(logmanager_t* lm, logfile_id_t id); +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(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(logfile_id_t id); +static blockbuf_t* blockbuf_init(); static void blockbuf_node_done(void* bb_data); static char* blockbuf_get_writepos(blockbuf_t** p_bb, - logfile_id_t id, size_t str_len, bool flush); static void blockbuf_register(blockbuf_t* bb); static void blockbuf_unregister(blockbuf_t* bb); -static bool logfile_set_enabled(logfile_id_t id, bool val); static char* add_slash(char* str); static bool check_file_and_path(char* filename, @@ -312,75 +349,28 @@ static bool check_file_and_path(char* filename, bool do_log); static bool file_is_symlink(char* filename); -static int skygw_log_disable_raw(logfile_id_t id, bool emergency); /*< no locking */ static int find_last_seqno(strpart_t* parts, int seqno, int seqnoidx); void flushall_logfiles(bool flush); bool thr_flushall_check(); -const char* get_suffix_default(void) -{ - return ".log"; -} - -const char* get_debug_prefix_default(void) -{ - return "debug"; -} - -const char* get_debug_suffix_default(void) -{ - return get_suffix_default(); -} - -const char* get_trace_prefix_default(void) -{ - return "trace"; -} - -const char* get_trace_suffix_default(void) -{ - return get_suffix_default(); -} - -const char* get_msg_prefix_default(void) -{ - return "messages"; -} - -const char* get_msg_suffix_default(void) -{ - return get_suffix_default(); -} - -const char* get_err_prefix_default(void) -{ - return "error"; -} - -const char* get_err_suffix_default(void) -{ - return get_suffix_default(); -} - -const char* get_logpath_default(void) -{ - return "/var/log/maxscale"; -} - -static bool logmanager_init_nomutex(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; @@ -395,7 +385,7 @@ static bool logmanager_init_nomutex(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; @@ -406,24 +396,22 @@ static bool logmanager_init_nomutex(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, 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; } /** @@ -435,10 +423,10 @@ static bool logmanager_init_nomutex(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 */ @@ -447,27 +435,27 @@ static bool logmanager_init_nomutex(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; } @@ -475,32 +463,34 @@ return_succp: /** * Initializes log managing routines in MariaDB Corporation MaxScale. * - * Parameters: - * @param argc number of arguments in argv array - * - * @param argv arguments array + * @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 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(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(argc, argv); - -return_succp: release_lock(&lmlock); - return succp; + return succ; } /** @@ -530,17 +520,12 @@ static void logmanager_done_nomutex(void) /** Free filewriter memory. */ filewriter_done(fwr); - for (i = LOGFILE_FIRST; i <= LOGFILE_LAST; i++) - { - lf = logmanager_get_logfile(lm, (logfile_id_t)i); - /** Release logfile memory */ - logfile_done(lf); - } + lf = logmanager_get_logfile(lm); + /** Release logfile memory */ + logfile_done(lf); + + closelog(); - if (syslog_id_str) - { - closelog(); - } /** Release messages and finally logmanager memory */ fnames_conf_done(&lm->lm_fnames_conf); skygw_message_done(lm->lm_clientmes); @@ -551,62 +536,48 @@ 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); } -static logfile_t* logmanager_get_logfile(logmanager_t* lmgr, logfile_id_t id) +static logfile_t* logmanager_get_logfile(logmanager_t* lmgr) { logfile_t* lf; CHK_LOGMANAGER(lmgr); - ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); - lf = &lmgr->lm_logfile[id]; + lf = &lmgr->lm_logfile; if (lf->lf_state == RUN) { @@ -639,7 +610,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); @@ -657,7 +628,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 @@ -669,11 +640,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; @@ -683,194 +654,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[LOGFILE_ERROR]; + 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, LOGFILE_ERROR, 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; } @@ -899,7 +859,7 @@ static void blockbuf_unregister(blockbuf_t* bb) CHK_BLOCKBUF(bb); ss_dassert(bb->bb_refcount >= 1); - lf = &lm->lm_logfile[bb->bb_fileid]; + lf = &lm->lm_logfile; CHK_LOGFILE(lf); /** * if this is the last client in a full buffer, send write request. @@ -932,7 +892,6 @@ static void blockbuf_unregister(blockbuf_t* bb) * */ static char* blockbuf_get_writepos(blockbuf_t** p_bb, - logfile_id_t id, size_t str_len, bool flush) { @@ -942,11 +901,11 @@ 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); - lf = &lm->lm_logfile[id]; + lf = &lm->lm_logfile; CHK_LOGFILE(lf); bb_list = &lf->lf_blockbuf_list; @@ -1012,7 +971,7 @@ static char* blockbuf_get_writepos(blockbuf_t** p_bb, /** * New node is created */ - if ((bb = blockbuf_init(id)) == NULL) + if ((bb = blockbuf_init()) == NULL) { return NULL; } @@ -1026,8 +985,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. @@ -1112,7 +1071,7 @@ static char* blockbuf_get_writepos(blockbuf_t** p_bb, * Create the first block buffer to logfile's blockbuf list. */ - if ((bb = blockbuf_init(id)) == NULL) + if ((bb = blockbuf_init()) == NULL) { return NULL; } @@ -1127,8 +1086,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. @@ -1193,13 +1152,12 @@ static void blockbuf_node_done(void* bb_data) } -static blockbuf_t* blockbuf_init(logfile_id_t id) +static blockbuf_t* blockbuf_init() { blockbuf_t* bb; if ((bb = (blockbuf_t *) calloc(1, sizeof (blockbuf_t)))) { - bb->bb_fileid = id; #if defined(SS_DEBUG) bb->bb_chk_top = CHK_NUM_BLOCKBUF; bb->bb_chk_tail = CHK_NUM_BLOCKBUF; @@ -1222,141 +1180,63 @@ static blockbuf_t* blockbuf_init(logfile_id_t id) } -int skygw_log_enable(logfile_id_t id) +static int skygw_log_enable(logfile_id_t id) { - bool err = 0; + bool rval = -1; - if (!logmanager_register(true)) + if (logmanager_register(true)) { - err = -1; - goto return_err; - } - CHK_LOGMANAGER(lm); + CHK_LOGMANAGER(lm); - if (logfile_set_enabled(id, true)) - { lm->lm_enabled_logfiles |= id; /** * Set global variable */ lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles; - } - logmanager_unregister(); -return_err: - return err; -} - -int skygw_log_disable(logfile_id_t id) /*< no locking */ -{ - int rc; - - rc = skygw_log_disable_raw(id, false); - - return rc; -} - -static int skygw_log_disable_raw(logfile_id_t id, bool emergency) /*< no locking */ -{ - bool err = 0; - - if (!logmanager_register(true)) - { - err = -1; - goto return_err; - } - CHK_LOGMANAGER(lm); - - if (emergency || logfile_set_enabled(id, false)) - { - lm->lm_enabled_logfiles &= ~id; - /** - * Set global variable - */ - lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles; - } - - logmanager_unregister(); -return_err: - return err; -} - - -static bool logfile_set_enabled(logfile_id_t id, bool val) -{ - bool rval = false; - - CHK_LOGMANAGER(lm); - - if (logmanager_is_valid_id(id)) - { - if (use_stdout == 0) - { - logfile_t *lf = logmanager_get_logfile(lm, id); - lf->lf_enabled = val; - - const char *name; - - switch (id) - { - default: - case LOGFILE_ERROR: - name = "LOGFILE_ERROR"; - break; - - case LOGFILE_MESSAGE: - name = "LOGFILE_MESSAGE"; - break; - - case LOGFILE_TRACE: - name = "LOGFILE_TRACE"; - break; - - case LOGFILE_DEBUG: - name = "LOGFILE_DEBUG"; - break; - } - - const char FORMAT[] = "The logging of %s messages is %s."; - const char *action; - - if (val) - { - action = "enabled"; - } - else - { - action = "disabled"; - } - - MXS_NOTICE(FORMAT, name, action); - } - - rval = true; - } - else - { - MXS_ERROR("Invalid logfile id %d.", id); - ss_dassert(!true); + logmanager_unregister(); + rval = 0; } return rval; } -void skygw_log_set_augmentation(int bits) +static int skygw_log_disable(logfile_id_t id) { - log_augmentation = bits & LOG_AUGMENTATION_MASK; + bool rval = -1; + + if (logmanager_register(true)) + { + CHK_LOGMANAGER(lm); + + lm->lm_enabled_logfiles &= ~id; + /** + * Set global variable + */ + lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles; + + logmanager_unregister(); + rval = 0; + } + + return rval; } -int skygw_log_get_augmentation() + +/** + * Set log augmentation. + * + * @param bits One of the log_augmentation_t constants. + */ +void mxs_log_set_augmentation(int bits) { - 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. @@ -1368,7 +1248,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, @@ -1377,252 +1257,20 @@ static int log_write(logfile_id_t id, const char* str, enum log_flush flush) { - int rv = 0; + int rv = -1; if (logmanager_register(true)) { CHK_LOGMANAGER(lm); - int attempts = 0; - int successes = 0; - - for (int i = LOGFILE_FIRST; i <= LOGFILE_LAST; i <<= 1) - { - /** - * If a particular log is enabled in general and it is enabled for - * the current session, log the stuff. - */ - if (LOG_IS_ENABLED(i) && ((i & id) != 0)) - { - ++attempts; - - if (logmanager_write_log((logfile_id_t)i, - flush, - prefix_len, - len, str) == 0) - { - ++successes; - } - } - } + rv = logmanager_write_log(priority, flush, prefix_len, len, str); logmanager_unregister(); - - // Only if logging was attempted and nothing succeeded, it is considered a failure. - if ((attempts != 0) && (successes == 0)) - { - rv = -1; - } - } - else - { - rv = -1; } 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] : "; - -static log_prefix_t logfile_to_prefix(logfile_id_t id) -{ - log_prefix_t prefix; - - // The id can be a bitmask, hence we choose the most "severe" one. - if (id & LOGFILE_ERROR) - { - prefix.text = PREFIX_ERROR; - prefix.len = sizeof(PREFIX_ERROR); - } - else if (id & LOGFILE_MESSAGE) - { - prefix.text = PREFIX_NOTICE; - prefix.len = sizeof(PREFIX_NOTICE); - } - else if (id & LOGFILE_TRACE) - { - prefix.text = PREFIX_INFO; - prefix.len = sizeof(PREFIX_INFO); - } - else if (id & LOGFILE_DEBUG) - { - prefix.text = PREFIX_DEBUG; - prefix.len = sizeof(PREFIX_DEBUG); - } - else - { - assert(!true); - - prefix.text = PREFIX_ERROR; - prefix.len = sizeof(PREFIX_ERROR); - } - - --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; - 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_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 (logmanager_register(false)) - { - CHK_LOGMANAGER(lm); - - if (logmanager_is_valid_id(id)) - { - logfile_t *lf = logmanager_get_logfile(lm, id); - CHK_LOGFILE(lf); - - logfile_flush(lf); - err = 0; - } - - logmanager_unregister(); - } - else - { - ss_dfprintf(stderr, "Can't register to logmanager, flushing failed.\n"); - } - - 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 (logmanager_register(false)) - { - CHK_LOGMANAGER(lm); - - if (logmanager_is_valid_id(id)) - { - logfile_t *lf = logmanager_get_logfile(lm, id); - 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; -} - - - - /** * @node Register as a logging client to logmanager. * @@ -1638,7 +1286,7 @@ int skygw_log_rotate(logfile_id_t id) */ static bool logmanager_register(bool writep) { - bool succp = true; + bool succ = true; acquire_lock(&lmlock); @@ -1652,8 +1300,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)); @@ -1672,23 +1320,26 @@ static bool logmanager_register(bool writep) if (lm == NULL) { - succp = logmanager_init_nomutex(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; } /** @@ -1719,43 +1370,17 @@ static void logmanager_unregister(void) * @node Initialize log file naming parameters from call arguments * or from default functions in cases where arguments are not provided. * - * Parameters: - * @param fn - - * - * - * @param argc - - * - * - * @param argv - - * - * - * @return pointer to object which is either in RUN or DONE state. + * @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. * + * @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, - 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" - "-a ............(\"skygw_debug\")\n" - "-b ............(\".log\")\n" - "-c ............(\"skygw_trace\")\n" - "-d ............(\".log\")\n" - "-e ............(\"skygw_msg\")\n" - "-f ............(\".log\")\n" - "-g ............(\"skygw_err\")\n" - "-i ............(\".log\")\n" - "-j ............(\"/tmp\")\n" - "-l .......(no default)\n" - "-m ............(argv[0])\n" - "-s .......(no default)\n" - "-o .......(write logs to stdout)\n"; + bool succ = false; /** * When init_started is set, clean must be done for it. @@ -1765,200 +1390,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_debug_prefix = strndup(optarg, MAX_PREFIXLEN); - break; - - case 'b': - fn->fn_debug_suffix = strndup(optarg, MAX_SUFFIXLEN); - break; - case 'c': - fn->fn_trace_prefix = strndup(optarg, MAX_PREFIXLEN); - break; - - case 'd': - fn->fn_trace_suffix = strndup(optarg, MAX_SUFFIXLEN); - break; - - case 'e': - fn->fn_msg_prefix = strndup(optarg, MAX_PREFIXLEN); - break; - - case 'f': - fn->fn_msg_suffix = strndup(optarg, MAX_SUFFIXLEN); - break; - - case 'g': - fn->fn_err_prefix = strndup(optarg, MAX_PREFIXLEN); - break; - - case 'i': - fn->fn_err_suffix = strndup(optarg, MAX_SUFFIXLEN); - break; - - case 'j': - fn->fn_logpath = strndup(optarg, MAX_PATHLEN); - break; - - case 'l': - /** record list of log file ids for syslogged */ - if (do_syslog) - { - if (syslog_id_str != NULL) - { - free (syslog_id_str); - } - syslog_id_str = optarg; - } - break; - - case 'm': - /** - * Identity string for syslog printing, needs '-l' - * to be effective. - */ - syslog_ident_str = optarg; - break; - - case 's': - /** record list of log file ids for later use */ - shmem_id_str = optarg; - break; - case 'h': - default: - fprintf(stderr, "\nSupported arguments are (default)\n%s\n", argstr); - goto return_conf_init; - } /** switch (opt) */ + log_config.use_stdout = false; + dir = logdir; } - /** If log file name is not specified in call arguments, use default. */ - fn->fn_debug_prefix = (fn->fn_debug_prefix == NULL) ? - strdup(get_debug_prefix_default()) : fn->fn_debug_prefix; - fn->fn_debug_suffix = (fn->fn_debug_suffix == NULL) ? - strdup(get_debug_suffix_default()) : fn->fn_debug_suffix; - fn->fn_trace_prefix = (fn->fn_trace_prefix == NULL) ? - strdup(get_trace_prefix_default()) : fn->fn_trace_prefix; - fn->fn_trace_suffix = (fn->fn_trace_suffix == NULL) ? - strdup(get_trace_suffix_default()) : fn->fn_trace_suffix; - fn->fn_msg_prefix = (fn->fn_msg_prefix == NULL) ? - strdup(get_msg_prefix_default()) : fn->fn_msg_prefix; - fn->fn_msg_suffix = (fn->fn_msg_suffix == NULL) ? - strdup(get_msg_suffix_default()) : fn->fn_msg_suffix; - fn->fn_err_prefix = (fn->fn_err_prefix == NULL) ? - strdup(get_err_prefix_default()) : fn->fn_err_prefix; - fn->fn_err_suffix = (fn->fn_err_suffix == NULL) ? - strdup(get_err_suffix_default()) : fn->fn_err_suffix; - fn->fn_logpath = (fn->fn_logpath == NULL) ? - strdup(get_logpath_default()) : fn->fn_logpath; - - /** Set identity string for syslog if it is not set in config.*/ - if (do_syslog) + else { - syslog_ident_str = - (syslog_ident_str == NULL ? - (argv == NULL ? strdup(program_invocation_short_name) : strdup(*argv)) : - syslog_ident_str); + log_config.use_stdout = true; + // TODO: Re-arrange things so that fn->fn_logpath can be NULL. + dir = "/tmp"; } - /* 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, - "Error log :\t%s/%s1%s\n" - "Message log :\t%s/%s1%s\n" - "Trace log :\t%s/%s1%s\n" - "Debug log :\t%s/%s1%s\n\n", - fn->fn_logpath, - fn->fn_err_prefix, - fn->fn_err_suffix, - fn->fn_logpath, - fn->fn_msg_prefix, - fn->fn_msg_suffix, - fn->fn_logpath, - fn->fn_trace_prefix, - fn->fn_trace_suffix, - fn->fn_logpath, - fn->fn_debug_prefix, - fn->fn_debug_suffix); -#endif - succp = true; - fn->fn_state = RUN; - CHK_FNAMES_CONF(fn); -return_conf_init: - if (!succp) + fn->fn_logpath = strdup(dir); + + 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; -} - -static char* fname_conf_get_prefix(fnames_conf_t* fn, logfile_id_t id) -{ - CHK_FNAMES_CONF(fn); - ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); - - switch (id) - { - case LOGFILE_DEBUG: - return strdup(fn->fn_debug_prefix); - break; - - case LOGFILE_TRACE: - return strdup(fn->fn_trace_prefix); - break; - - case LOGFILE_MESSAGE: - return strdup(fn->fn_msg_prefix); - break; - - case LOGFILE_ERROR: - return strdup(fn->fn_err_prefix); - break; - - default: - return NULL; - } -} - -static char* fname_conf_get_suffix(fnames_conf_t* fn, logfile_id_t id) -{ - CHK_FNAMES_CONF(fn); - ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); - - switch (id) - { - case LOGFILE_DEBUG: - return strdup(fn->fn_debug_suffix); - break; - - case LOGFILE_TRACE: - return strdup(fn->fn_trace_suffix); - break; - - case LOGFILE_MESSAGE: - return strdup(fn->fn_msg_suffix); - break; - - case LOGFILE_ERROR: - return strdup(fn->fn_err_suffix); - break; - - default: - return NULL; - } + return succ; } @@ -1969,7 +1423,7 @@ static char* fname_conf_get_suffix(fnames_conf_t* fn, logfile_id_t id) * 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 @@ -1982,64 +1436,16 @@ static char* fname_conf_get_suffix(fnames_conf_t* fn, logfile_id_t id) */ static bool logfiles_init(logmanager_t* lm) { - bool succp = true; - int lid = LOGFILE_FIRST; - int i = 0; - 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) + bool succ = logfile_init(&lm->lm_logfile, lm, store_shmem); + + if (!succ) { - openlog(syslog_ident_str, LOG_PID | LOG_NDELAY, LOG_USER); + fprintf(stderr, "*\n* Error : Initializing log files failed.\n"); } - /** - * Initialize log files, pass softlink flag if necessary. - */ - while (lid <= LOGFILE_LAST && succp) - { - /** - * 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_id_t(lid))) != 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_id_t(lid))) != NULL) - { - write_syslog = true; - } - else - { - write_syslog = false; - } - succp = logfile_init(&lm->lm_logfile[lid], - (logfile_id_t)lid, - lm, - store_shmem, - write_syslog); - - if (!succp) - { - fprintf(stderr, "*\n* Error : Initializing log files failed.\n"); - break; - } - - lid <<= 1; - i += 1; - } - return succp; + return succ; } static void logfile_flush(logfile_t* lf) @@ -2085,9 +1491,17 @@ 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 (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"); + succ = true; + // TODO: Refactor to get rid of the gotos. + goto return_succ; + } /** * sparts is an array but next pointers are used to walk through * the list of string parts. @@ -2153,8 +1567,8 @@ static bool logfile_create(logfile_t* lf) */ if (!writable) { - succp = false; - goto return_succp; + succ = false; + goto return_succ; } } @@ -2176,8 +1590,8 @@ static bool logfile_create(logfile_t* lf) */ if (!writable) { - succp = false; - goto return_succp; + succ = false; + goto return_succ; } } } @@ -2200,10 +1614,10 @@ static bool logfile_create(logfile_t* lf) } while (namecreatefail || nameconflicts); - succp = true; + succ = true; -return_succp: - return succp; +return_succ: + return succ; } /** @@ -2220,68 +1634,31 @@ return_succp: */ static bool logfile_open_file(filewriter_t* fw, logfile_t* lf) { - bool succp; - char* start_msg_str; - int err; + bool rv = true; - if (use_stdout) + if (log_config.use_stdout) { - fw->fwr_file[lf->lf_id] = skygw_file_alloc(lf->lf_full_file_name); - fw->fwr_file[lf->lf_id]->sf_file = stdout; + fw->fwr_file = skygw_file_alloc(lf->lf_full_file_name); + fw->fwr_file->sf_file = stdout; } else if (lf->lf_store_shmem) { /** Create symlink pointing to log file */ - fw->fwr_file[lf->lf_id] = skygw_file_init(lf->lf_full_file_name, lf->lf_full_link_name); + fw->fwr_file = skygw_file_init(lf->lf_full_file_name, lf->lf_full_link_name); } else { /** Create normal disk-resident log file */ - fw->fwr_file[lf->lf_id] = skygw_file_init(lf->lf_full_file_name, NULL); + fw->fwr_file = skygw_file_init(lf->lf_full_file_name, NULL); } - if (fw->fwr_file[lf->lf_id] == NULL) + if (fw->fwr_file == NULL) { fprintf(stderr, "Error : opening logfile %s failed.\n", lf->lf_full_file_name); - succp = false; - goto return_succp; + rv = false; } - if (use_stdout == 0) - { - if (lf->lf_enabled) - { - start_msg_str = strdup("---\tLogging is enabled.\n"); - } - else - { - start_msg_str = strdup("---\tLogging is disabled.\n"); - } - err = skygw_file_write(fw->fwr_file[lf->lf_id], - (void *)start_msg_str, - strlen(start_msg_str), - true); - - if (err != 0) - { - char errbuf[STRERROR_BUFLEN]; - fprintf(stderr, - "Error : writing to file %s failed due to %d, %s. " - "Exiting MaxScale.\n", - lf->lf_full_file_name, - err, - strerror_r(err, errbuf, sizeof(errbuf))); - succp = false; - goto return_succp; - } - - free(start_msg_str); - } - - succp = true; - -return_succp: - return succp; + return rv; } @@ -2314,7 +1691,7 @@ static char* form_full_file_name(strpart_t* parts, logfile_t* lf, int seqnoidx) { int file_sn; int link_sn = 0; - char* tmp = parts[0].sp_string; + const char* tmp = parts[0].sp_string; file_sn = find_last_seqno(parts, lf->lf_name_seqno, seqnoidx); @@ -2523,7 +1900,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) @@ -2532,10 +1909,10 @@ static bool file_is_symlink(char* filename) if (rc != -1 && S_ISLNK(b.st_mode)) { - succp = true; + succ = true; } } - return succp; + return succ; } @@ -2546,20 +1923,16 @@ static bool file_is_symlink(char* filename) * * Parameters: * @param logfile log file - * @param logfile_id identifier for 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, - logfile_id_t logfile_id, 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) @@ -2567,9 +1940,8 @@ static bool logfile_init(logfile_t* logfile, logfile->lf_chk_tail = CHK_NUM_LOGFILE; #endif logfile->lf_logmes = logmanager->lm_logmes; - logfile->lf_id = logfile_id; - logfile->lf_name_prefix = fname_conf_get_prefix(fn, logfile_id); - logfile->lf_name_suffix = fname_conf_get_suffix(fn, logfile_id); + logfile->lf_name_prefix = LOGFILE_NAME_PREFIX; + logfile->lf_name_suffix = LOGFILE_NAME_SUFFIX; logfile->lf_npending_writes = 0; logfile->lf_name_seqno = 1; logfile->lf_lmgr = logmanager; @@ -2577,9 +1949,7 @@ 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; - logfile->lf_enabled = logmanager->lm_enabled_logfiles & logfile_id; /** * If file is stored in shared memory in /dev/shm, a link * pointing to shm file is created and located to the file @@ -2597,8 +1967,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; @@ -2606,8 +1976,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); @@ -2618,9 +1988,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 @@ -2636,35 +2006,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_id), + 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_id), + 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; } /** @@ -2710,13 +2080,10 @@ static void logfile_done(logfile_t* lf) } } -static void logfile_free_memory( - logfile_t* lf) +static void logfile_free_memory(logfile_t* lf) { if (lf->lf_filepath != NULL) free(lf->lf_filepath); if (lf->lf_linkpath != NULL) free(lf->lf_linkpath); - if (lf->lf_name_prefix != NULL) free(lf->lf_name_prefix); - if (lf->lf_name_suffix != NULL) free(lf->lf_name_suffix); if (lf->lf_full_link_name != NULL) free(lf->lf_full_link_name); if (lf->lf_full_file_name != NULL) free(lf->lf_full_file_name); } @@ -2724,25 +2091,19 @@ static void logfile_free_memory( /** * @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; - logfile_id_t id; - int i; + bool succ = false; CHK_LOGMANAGER(logmanager); + assert(logmanager->lm_clientmes); + assert(logmanager->lm_logmes); fw->fwr_state = INIT; #if defined(SS_DEBUG) @@ -2751,47 +2112,33 @@ 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; } - - for (i = LOGFILE_FIRST; i <= LOGFILE_LAST; i <<= 1) - { - id = (logfile_id_t)i; - lf = logmanager_get_logfile(logmanager, id); - - if (!(succp = logfile_open_file(fw, lf))) - { - fprintf(stderr, - "Error : opening log file %s failed. Exiting " - "MaxScale\n", - lf->lf_full_file_name); - goto return_succp; - } - } /*< for */ - fw->fwr_state = RUN; - CHK_FILEWRITER(fw); - succp = true; - -return_succp: - if (!succp) + else { + fprintf(stderr, + "Error : opening log file %s failed. Exiting " + "MaxScale\n", + lf->lf_full_file_name); filewriter_done(fw); } + ss_dassert(fw->fwr_state == RUN || fw->fwr_state == DONE); - return succp; + return succ; } static void filewriter_done(filewriter_t* fw) { - int i; - logfile_id_t id; - switch (fw->fwr_state) { case RUN: @@ -2799,17 +2146,13 @@ static void filewriter_done(filewriter_t* fw) case INIT: fw->fwr_logmes = NULL; fw->fwr_clientmes = NULL; - for (i = LOGFILE_FIRST; i <= LOGFILE_LAST; i++) + if (log_config.use_stdout) { - id = (logfile_id_t)i; - if (use_stdout) - { - skygw_file_free(fw->fwr_file[id]); - } - else - { - skygw_file_close(fw->fwr_file[id], true); - } + skygw_file_free(fw->fwr_file); + } + else + { + skygw_file_close(fw->fwr_file, true); } fw->fwr_state = DONE; case DONE: @@ -2819,6 +2162,167 @@ static void filewriter_done(filewriter_t* fw) } } +static bool thr_flush_file(logmanager_t *lm, filewriter_t *fwr) +{ + /** + * Get file pointer of current logfile. + */ + bool do_flushall = thr_flushall_check(); + skygw_file_t *file = fwr->fwr_file; + logfile_t *lf = &lm->lm_logfile; + + /** + * read and reset logfile's flush- and rotateflag + */ + acquire_lock(&lf->lf_spinlock); + bool flush_logfile = lf->lf_flushflag; + bool rotate_logfile = lf->lf_rotateflag; + lf->lf_flushflag = false; + lf->lf_rotateflag = false; + release_lock(&lf->lf_spinlock); + /** + * Log rotation : + * Close current, and open a new file for the log. + */ + if (rotate_logfile) + { + bool succ; + + lf->lf_name_seqno += 1; /*< new sequence number */ + + if (!(succ = logfile_create(lf))) + { + lf->lf_name_seqno -= 1; /*< restore */ + } + else if ((succ = logfile_open_file(fwr, lf))) + { + if (log_config.use_stdout) + { + skygw_file_free(file); + } + else + { + skygw_file_close(file, false); /*< close old file */ + } + } + + if (!succ) + { + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "Error : Log rotation failed. " + "Creating replacement file %s " + "failed. Continuing " + "logging to existing file.", + lf->lf_full_file_name))); + } + return true; + } + /** + * get logfile's block buffer list + */ + mlist_t *bb_list = &lf->lf_blockbuf_list; +#if defined(SS_DEBUG) + simple_mutex_lock(&bb_list->mlist_mutex, true); + CHK_MLIST(bb_list); + simple_mutex_unlock(&bb_list->mlist_mutex); +#endif + mlist_node_t *node = bb_list->mlist_first; + + while (node != NULL) + { + int err = 0; + + CHK_MLIST_NODE(node); + blockbuf_t *bb = (blockbuf_t *)node->mlnode_data; + CHK_BLOCKBUF(bb); + + /** Lock block buffer */ + simple_mutex_lock(&bb->bb_mutex, true); + + blockbuf_state_t flush_blockbuf = bb->bb_state; + + if (bb->bb_buf_used != 0 && + ((flush_blockbuf == BB_FULL) || flush_logfile || do_flushall)) + { + /** + * buffer is at least half-full + * -> write to disk + */ + while (bb->bb_refcount > 0) + { + simple_mutex_unlock(&bb->bb_mutex); + simple_mutex_lock(&bb->bb_mutex, true); + } + err = skygw_file_write(file, + (void *)bb->bb_buf, + bb->bb_buf_used, + (flush_logfile || do_flushall)); + if (err) + { + char errbuf[STRERROR_BUFLEN]; + fprintf(stderr, + "Error : Write to %s log " + ": %s failed due to %d, " + "%s. Disabling the log.", + STRLOGNAME(LOGFILE_ERROR), + lf->lf_full_file_name, + err, + strerror_r(err, errbuf, sizeof(errbuf))); + /** Force log off */ + skygw_log_disable(LOGFILE_ERROR); + } + /** + * Reset buffer's counters and mark + * not full. + */ + bb->bb_buf_left = bb->bb_buf_size; + bb->bb_buf_used = 0; + memset(bb->bb_buf, 0, bb->bb_buf_size); + bb->bb_state = BB_CLEARED; +#if defined(SS_LOG_DEBUG) + sprintf(bb->bb_buf,"[block:%d]", atomic_add(&block_start_index, 1)); + bb->bb_buf_used += strlen(bb->bb_buf); + bb->bb_buf_left -= strlen(bb->bb_buf); +#endif + + } + /** Release lock to block buffer */ + simple_mutex_unlock(&bb->bb_mutex); + + size_t vn1; + size_t vn2; + /** Consistent lock-free read on the list */ + do + { + while ((vn1 = bb_list->mlist_versno) % 2 != 0) + { + continue; + } + node = node->mlnode_next; + vn2 = bb_list->mlist_versno; + } + while (vn1 != vn2 && node); + + } /* while (node != NULL) */ + + /** + * Writer's exit flag was set after checking it. + * Loop is restarted to ensure that all logfiles are + * flushed. + */ + + bool done = true; + + if (flushall_started_flag) + { + flushall_started_flag = false; + flushall_done_flag = true; + done = false; + } + + return done; +} /** * @node Writes block buffers of logfiles to physical log files on disk. @@ -2865,24 +2369,9 @@ static void filewriter_done(filewriter_t* fw) */ static void* thr_filewriter_fun(void* data) { - skygw_thread_t* thr; - filewriter_t* fwr; - skygw_file_t* file; - logfile_t* lf; + skygw_thread_t* thr = (skygw_thread_t *)data; + filewriter_t* fwr = (filewriter_t *)skygw_thread_get_data(thr); - mlist_t* bb_list; - blockbuf_t* bb; - mlist_node_t* node; - int i; - blockbuf_state_t flush_blockbuf; /**< flush single block buffer. */ - bool flush_logfile; /**< flush logfile */ - bool do_flushall = false; - bool rotate_logfile; /*< close current and open new file */ - size_t vn1; - size_t vn2; - - thr = (skygw_thread_t *)data; - fwr = (filewriter_t *)skygw_thread_get_data(thr); flushall_logfiles(false); CHK_FILEWRITER(fwr); @@ -2890,7 +2379,6 @@ static void* thr_filewriter_fun(void* data) /** Inform log manager about the state. */ skygw_message_send(fwr->fwr_clientmes); - while (!skygw_thread_must_exit(thr)) { /** @@ -2904,171 +2392,18 @@ static void* thr_filewriter_fun(void* data) } /** Process all logfiles which have buffered writes. */ - for (i = LOGFILE_FIRST; i <= LOGFILE_LAST; i <<= 1) + bool done = false; + + while (!done) { - retry_flush_on_exit: - /** - * Get file pointer of current logfile. - */ - - do_flushall = thr_flushall_check(); - file = fwr->fwr_file[i]; - lf = &lm->lm_logfile[(logfile_id_t)i]; - - /** - * read and reset logfile's flush- and rotateflag - */ - acquire_lock(&lf->lf_spinlock); - flush_logfile = lf->lf_flushflag; - rotate_logfile = lf->lf_rotateflag; - lf->lf_flushflag = false; - lf->lf_rotateflag = false; - release_lock(&lf->lf_spinlock); - /** - * Log rotation : - * Close current, and open a new file for the log. - */ - if (rotate_logfile) - { - bool succp; - - lf->lf_name_seqno += 1; /*< new sequence number */ - - if (!(succp = logfile_create(lf))) - { - lf->lf_name_seqno -= 1; /*< restore */ - } - else if ((succp = logfile_open_file(fwr, lf))) - { - if (use_stdout) - { - skygw_file_free (file); - } - else - { - skygw_file_close(file, false); /*< close old file */ - } - } - - if (!succp) - { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Log rotation failed. " - "Creating replacement file %s " - "failed. Continuing " - "logging to existing file.", - lf->lf_full_file_name))); - } - continue; - } - /** - * get logfile's block buffer list - */ - bb_list = &lf->lf_blockbuf_list; -#if defined(SS_DEBUG) - simple_mutex_lock(&bb_list->mlist_mutex, true); - CHK_MLIST(bb_list); - simple_mutex_unlock(&bb_list->mlist_mutex); -#endif - node = bb_list->mlist_first; - - while (node != NULL) - { - int err = 0; - - CHK_MLIST_NODE(node); - bb = (blockbuf_t *)node->mlnode_data; - CHK_BLOCKBUF(bb); - - /** Lock block buffer */ - simple_mutex_lock(&bb->bb_mutex, true); - - flush_blockbuf = bb->bb_state; - - if (bb->bb_buf_used != 0 && - ((flush_blockbuf == BB_FULL) || flush_logfile || do_flushall)) - { - /** - * buffer is at least half-full - * -> write to disk - */ - while (bb->bb_refcount > 0) - { - simple_mutex_unlock(&bb->bb_mutex); - simple_mutex_lock(&bb->bb_mutex, true); - } - err = skygw_file_write(file, - (void *)bb->bb_buf, - bb->bb_buf_used, - (flush_logfile || do_flushall)); - if (err) - { - char errbuf[STRERROR_BUFLEN]; - fprintf(stderr, - "Error : Write to %s log " - ": %s failed due to %d, " - "%s. Disabling the log.", - STRLOGNAME((logfile_id_t)i), - lf->lf_full_file_name, - err, - strerror_r(err, errbuf, sizeof(errbuf))); - /** Force log off */ - skygw_log_disable_raw((logfile_id_t)i, true); - } - /** - * Reset buffer's counters and mark - * not full. - */ - bb->bb_buf_left = bb->bb_buf_size; - bb->bb_buf_used = 0; - memset(bb->bb_buf, 0, bb->bb_buf_size); - bb->bb_state = BB_CLEARED; -#if defined(SS_LOG_DEBUG) - sprintf(bb->bb_buf,"[block:%d]", atomic_add(&block_start_index, 1)); - bb->bb_buf_used += strlen(bb->bb_buf); - bb->bb_buf_left -= strlen(bb->bb_buf); -#endif - - } - /** Release lock to block buffer */ - simple_mutex_unlock(&bb->bb_mutex); - - /** Consistent lock-free read on the list */ - do - { - while ((vn1 = bb_list->mlist_versno) % 2 != 0) - { - continue; - } - node = node->mlnode_next; - vn2 = bb_list->mlist_versno; - } - while (vn1 != vn2 && node); - - } /* while (node != NULL) */ - - /** - * Writer's exit flag was set after checking it. - * Loop is restarted to ensure that all logfiles are - * flushed. - */ - - if (flushall_started_flag) - { - flushall_started_flag = false; - flushall_done_flag = true; - i = LOGFILE_FIRST; - goto retry_flush_on_exit; - } + done = thr_flush_file(lm, fwr); if (!thr_flushall_check() && skygw_thread_must_exit(thr)) { flushall_logfiles(true); - i = LOGFILE_FIRST; - goto retry_flush_on_exit; + done = false; } - }/* for */ + } if (flushall_done_flag) { @@ -3105,15 +2440,7 @@ static void fnames_conf_done(fnames_conf_t* fn) static void fnames_conf_free_memory(fnames_conf_t* fn) { - if (fn->fn_debug_prefix != NULL) free(fn->fn_debug_prefix); - if (fn->fn_debug_suffix!= NULL) free(fn->fn_debug_suffix); - if (fn->fn_trace_prefix != NULL) free(fn->fn_trace_prefix); - if (fn->fn_trace_suffix!= NULL) free(fn->fn_trace_suffix); - if (fn->fn_msg_prefix != NULL) free(fn->fn_msg_prefix); - if (fn->fn_msg_suffix != NULL) free(fn->fn_msg_suffix); - if (fn->fn_err_prefix != NULL) free(fn->fn_err_prefix); - if (fn->fn_err_suffix != NULL) free(fn->fn_err_suffix); - if (fn->fn_logpath != NULL) free(fn->fn_logpath); + free(fn->fn_logpath); } /** @@ -3197,49 +2524,460 @@ 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) + log_config.do_highprecision = enabled; +} + +/** + * Enable/disable syslog logging. + * + * @param enabled True, if syslog logging should be enabled, false if it should be disabled. + */ +void mxs_log_set_syslog_enabled(bool enabled) +{ + log_config.do_syslog = enabled; +} + +/** + * Enable/disable maxscale log logging. + * + * @param enabled True, if syslog logging should be enabled, false if it should be disabled. + */ +void mxs_log_set_maxscalelog_enabled(bool enabled) +{ + log_config.do_maxscalelog = enabled; +} + + +/** + * Explicitly ensure that all pending log messages are flushed. + * + * @return 0 if the flushing was successfully initiated, otherwise -1. + * + * Note that the return value only indicates whether the flushing was + * successfully initiated, not whether the actual flushing has been + * performed. + */ +int mxs_log_flush() +{ + int err = -1; + + if (logmanager_register(false)) { - skygw_log_write(LOGFILE_TRACE,"Starting log flushing to disk."); + 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); - skygw_message_send(lm->lm_logmes); - skygw_message_wait(lm->lm_clientmes); + + if (skygw_message_send(lm->lm_logmes) == MES_RC_SUCCESS) + { + // TODO: Add error handling to skygw_message_wait. Now void. + skygw_message_wait(lm->lm_clientmes); + } + else + { + err = -1; + } + } + + return err; +} + +/** + * Rotate the log-file. That is, close the current one and create a new one + * with a larger sequence number. + * + * @return 0 if the rotating was successfully initiated, otherwise -1. + * + * Note that the return value only indicates whether the rotating was + * successfully initiated, not whether the actual rotation has been + * performed. + */ +int mxs_log_rotate() +{ + 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 logfile_id_t priority_to_file_id(int priority) +{ + switch (priority) + { + case LOG_DEBUG: + return LOGFILE_DEBUG; + case LOG_INFO: + return LOGFILE_TRACE; + case LOG_NOTICE: + return LOGFILE_MESSAGE; + case LOG_ERR: + case LOG_WARNING: + case LOG_CRIT: + case LOG_ALERT: + case LOG_EMERG: + default: + return LOGFILE_ERROR; + } +} + +static const char* priority_name(int priority) +{ + switch (priority) + { + case LOG_EMERG: + return "emercency"; + case LOG_ALERT: + return "alert"; + case LOG_CRIT: + return "critical"; + case LOG_ERR: + return "error"; + case LOG_WARNING: + return "warning"; + case LOG_NOTICE: + return "notice"; + case LOG_INFO: + return "informational"; + case LOG_DEBUG: + return "debug"; + default: + assert(!true); + return "unknown"; } } /** - * Toggle high precision logging - * @param val 0 for disabled, 1 for enabled + * 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. */ -void skygw_set_highp(int val) +int mxs_log_set_priority_enabled(int priority, bool enable) { - highprec = val; + int rv = -1; + const char* text = (enable ? "enable" : "disable"); + + if ((priority & ~LOG_PRIMASK) == 0) + { + logfile_id_t id = priority_to_file_id(priority); + int bit = (1 << priority); + + if (enable) + { + // TODO: Put behind spinlock. + lm_enabled_priorities_bitmask |= bit; + skygw_log_enable(id); + } + else + { + lm_enabled_priorities_bitmask &= ~bit; + skygw_log_disable(id); + } + + MXS_NOTICE("The logging of %s messages has been %sd.", priority_name(priority), text); + } + else + { + MXS_ERROR("Attempt to %s unknown syslog priority %d.", text, priority); + } + + return rv; } - -/** - * Toggle syslog logging - * @param val 0 for disabled, 1 for enabled - */ -void logmanager_enable_syslog(int val) +typedef struct log_prefix { - do_syslog = val; + 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; + } } /** - * Toggle syslog logging - * @param val 0 for disabled, 1 for enabled + * Log a message of a particular priority. + * + * @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. */ -void logmanager_enable_maxscalelog(int val) +int mxs_log_message(int priority, + const char* file, int line, const char* function, + const char* format, ...) { - do_maxscalelog = val; + int err = 0; + + assert((priority & ~LOG_PRIMASK) == 0); + + if ((priority & ~LOG_PRIMASK) == 0) // Check that the priority is ok, + { + logfile_id_t id = priority_to_id(priority); + + if (LOG_IS_ENABLED(id)) + { + 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_WARNING("Invalid syslog priority: %d", priority); + } + + return err; } diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h index cba349695..074f63a4b 100644 --- a/log_manager/log_manager.h +++ b/log_manager/log_manager.h @@ -16,7 +16,13 @@ * Copyright MariaDB Corporation Ab 2013-2014 */ #if !defined(LOG_MANAGER_H) -# define LOG_MANAGER_H +#define LOG_MANAGER_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif /* * We need a common.h file that is included by every component. @@ -25,12 +31,20 @@ #define STRERROR_BUFLEN 512 #endif -typedef enum +enum mxs_log_priorities { - BB_READY = 0x00, - BB_FULL, - BB_CLEARED -} blockbuf_state_t; + MXS_LOG_EMERG = (1 << LOG_EMERG), + MXS_LOG_ALERT = (1 << LOG_ALERT), + MXS_LOG_CRIT = (1 << LOG_CRIT), + MXS_LOG_ERR = (1 << LOG_ERR), + MXS_LOG_WARNING = (1 << LOG_WARNING), + MXS_LOG_NOTICE = (1 << LOG_NOTICE), + MXS_LOG_INFO = (1 << LOG_INFO), + MXS_LOG_DEBUG = (1 << LOG_DEBUG), + + MXS_LOG_MASK = (MXS_LOG_EMERG | MXS_LOG_ALERT | MXS_LOG_CRIT | MXS_LOG_ERR | + MXS_LOG_WARNING | MXS_LOG_NOTICE | MXS_LOG_INFO | MXS_LOG_DEBUG), +}; typedef enum { @@ -42,13 +56,12 @@ typedef enum LOGFILE_LAST = LOGFILE_DEBUG } logfile_id_t; - typedef enum { - FILEWRITER_INIT, - FILEWRITER_RUN, - FILEWRITER_DONE -} filewriter_state_t; + LOG_TARGET_DEFAULT = 0, + LOG_TARGET_FS = 1, // File system + LOG_TARGET_SHMEM = 2, // Shared memory +} log_target_t; /** * Thread-specific logging information. @@ -64,6 +77,11 @@ typedef struct log_info #define LT LOGFILE_TRACE #define LD LOGFILE_DEBUG +extern int lm_enabled_priorities_bitmask; +extern int lm_enabled_logfiles_bitmask; +extern ssize_t log_ses_count[]; +extern __thread log_info_t tls_log_info; + /** * Check if specified log type is enabled in general or if it is enabled * for the current session. @@ -72,17 +90,8 @@ typedef struct log_info (log_ses_count[id] > 0 && \ tls_log_info.li_enabled_logs & id)) ? true : false) - #define LOG_MAY_BE_ENABLED(id) (((lm_enabled_logfiles_bitmask & id) || \ log_ses_count[id] > 0) ? true : false) -/** - * Execute the given command if specified log is enabled in general or - * if there is at least one session for whom the log is enabled. - */ -#define LOGIF_MAYBE(id,cmd) if (LOG_MAY_BE_ENABLED(id)) \ - { \ - cmd; \ - } /** * Execute the given command if specified log is enabled in general or @@ -93,28 +102,6 @@ typedef struct log_info cmd; \ } -#if !defined(LOGIF) -#define LOGIF(id,cmd) if (lm_enabled_logfiles_bitmask & id) \ - { \ - cmd; \ - } -#endif - -/** - * UNINIT means zeroed memory buffer allocated for the struct. - * INIT means that struct members may have values, and memory may - * have been allocated. Done function must check and free it. - * RUN Struct is valid for run-time checking. - * DONE means that possible memory allocations have been released. - */ -typedef enum -{ - UNINIT = 0, - INIT, - RUN, - DONE -} flat_obj_state_t; - /** * LOG_AUGMENT_WITH_FUNCTION Each logged line is suffixed with [function-name]. */ @@ -124,75 +111,42 @@ 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 +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_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, ...) __attribute__((format(printf, 5, 6))); + +inline int mxs_log_id_to_priority(logfile_id_t id) { - LOG_FLUSH_NO = 0, - LOG_FLUSH_YES = 1 -}; - -EXTERN_C_BLOCK_BEGIN - -bool skygw_logmanager_init(int argc, char* argv[]); -void skygw_logmanager_done(void); -void skygw_logmanager_exit(void); - -/** - * 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); + 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(); - -EXTERN_C_BLOCK_END - -const char* get_trace_prefix_default(void); -const char* get_trace_suffix_default(void); -const char* get_msg_prefix_default(void); -const char* get_msg_suffix_default(void); -const char* get_err_prefix_default(void); -const char* get_err_suffix_default(void); -const char* get_logpath_default(void); +#define skygw_log_write_flush(id, format, ...) skygw_log_write(id, format, ##__VA_ARGS__) /** * 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. @@ -200,10 +154,14 @@ 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__) + +#if defined(__cplusplus) +} +#endif #endif /** LOG_MANAGER_H */ diff --git a/log_manager/test/CMakeLists.txt b/log_manager/test/CMakeLists.txt index bbcfd5791..e485e747e 100644 --- a/log_manager/test/CMakeLists.txt +++ b/log_manager/test/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(testlog testlog.c) -add_executable(testorder testorder.c) -target_link_libraries(testlog pthread log_manager utils) -target_link_libraries(testorder pthread log_manager utils) +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 a0e70bedc..6983067ea 100644 --- a/log_manager/test/testlog.c +++ b/log_manager/test/testlog.c @@ -17,21 +17,23 @@ */ -/** @file -@brief (brief description) +/** @file + @brief (brief description) */ #include #include #include +#include #include #include -typedef struct thread_st { - skygw_message_t* mes; - simple_mutex_t* mtx; - size_t* nactive; - pthread_t tid; +typedef struct thread_st +{ + skygw_message_t* mes; + simple_mutex_t* mtx; + size_t* nactive; + pthread_t tid; } thread_t; static void* thr_run(void* data); @@ -51,584 +53,692 @@ 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) + +static logfile_id_t id_to_priority(logfile_id_t id) +{ + switch (id) + { + case LOGFILE_ERROR: + return LOG_ERR; + + case LOGFILE_MESSAGE: + return LOG_NOTICE; + + case LOGFILE_TRACE: + return LOG_INFO; + + case LOGFILE_DEBUG: + return LOG_DEBUG; + + default: + assert(!true); + return LOG_ERR; + } +} + +static void skygw_log_enable(logfile_id_t id) +{ + mxs_log_set_priority_enabled(id_to_priority(id), true); +} + +static void skygw_log_disable(logfile_id_t id) +{ + mxs_log_set_priority_enabled(id_to_priority(id), false); +} + int main(int argc, char* argv[]) { - int err = 0; - char* logstr; - - int i; - bool succp; - skygw_message_t* mes; - simple_mutex_t* mtx; - size_t nactive; - thread_t** thr = NULL; - time_t t; - struct tm tm; - char c; - int nthr = 0; - int log_argc = 0; - char** log_argv = NULL; + int err = 0; + char* logstr; - while ((c = getopt(argc, argv, "t:")) != -1) - { - switch (c) { - case 't': - nthr = atoi(optarg); - break; + int i; + bool succp; + skygw_message_t* mes; + simple_mutex_t* mtx; + size_t nactive; + thread_t** thr = NULL; + time_t t; + struct tm tm; + char c; + int nthr = N_THR; - default: - break; - } - } - - if (nthr <= 0) - { - fprintf(stderr, "Thread count argument is zero or " - "negative. Exiting.\n"); + while ((c = getopt(argc, argv, "t:")) != -1) + { + switch (c) { + case 't': + nthr = atoi(optarg); + if (nthr <= 0) + { err = 1; - goto return_err; + } + break; + + default: + err = 1; + break; } + } - thr = (thread_t **)calloc(1, nthr*sizeof(thread_t*)); - - if (thr == NULL) - { - fprintf(stderr, "Failed to allocate memory for thread " - "structure. Exiting.\n"); - err = 1; - goto return_err; - - } - i = atexit(skygw_logmanager_exit); - - if (i != 0) { - fprintf(stderr, "Couldn't register exit function.\n"); - } - - succp = skygw_logmanager_init( log_argc, log_argv); - if(!succp) - fprintf(stderr, "Log manager initialization failed.\n"); - ss_dassert(succp); + if (err != 0) + { + fprintf(stderr, USAGE, argv[0], N_THR); + err = 1; + goto return_err; + } - t = time(NULL); - tm = *(localtime(&t)); - err = skygw_log_write_flush(LOGFILE_ERROR, - "%04d %02d/%02d %02d.%02d.%02d", - tm.tm_year+1900, - tm.tm_mon+1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec); - - skygw_logmanager_init( log_argc, log_argv); - logstr = ("First write with flush."); - err = skygw_log_write_flush(LOGFILE_ERROR, logstr); + printf("Using %d threads.\n", nthr); - logstr = ("Second write with flush."); - err = skygw_log_write_flush(LOGFILE_ERROR, logstr); + thr = (thread_t **)calloc(1, nthr*sizeof(thread_t*)); - logstr = ("Third write, no flush."); - err = skygw_log_write(LOGFILE_ERROR, logstr); + if (thr == NULL) + { + fprintf(stderr, "Failed to allocate memory for thread " + "structure. Exiting.\n"); + err = 1; + goto return_err; + } + i = atexit(mxs_log_finish); - logstr = ("Fourth write, no flush. Next flush only."); - err = skygw_log_write(LOGFILE_ERROR, logstr); + if (i != 0) + { + fprintf(stderr, "Couldn't register exit function.\n"); + } - err = skygw_log_flush(LOGFILE_ERROR); - - logstr = "My name is %s %d years and %d months."; + succp = mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + + if (!succp) + { + fprintf(stderr, "Log manager initialization failed.\n"); + } + ss_dassert(succp); + + t = time(NULL); + tm = *(localtime(&t)); + err = skygw_log_write_flush(LOGFILE_ERROR, + "%04d %02d/%02d %02d.%02d.%02d", + tm.tm_year+1900, + tm.tm_mon+1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec); + + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + logstr = ("First write with flush."); + err = skygw_log_write_flush(LOGFILE_ERROR, "%s", logstr); + + logstr = ("Second write with flush."); + err = skygw_log_write_flush(LOGFILE_ERROR, "%s", logstr); + + logstr = ("Third write, no flush."); + err = skygw_log_write(LOGFILE_ERROR, "%s", logstr); + + logstr = ("Fourth write, no flush. Next flush only."); + err = skygw_log_write(LOGFILE_ERROR, "%s", logstr); + + err = mxs_log_flush(); + + logstr = "My name is %s %d years and %d months."; #if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); + skygw_log_enable(LOGFILE_TRACE); #endif - err = skygw_log_write(LOGFILE_TRACE, logstr, "TraceyTracey", 3, 7); - skygw_log_flush(LOGFILE_TRACE); + err = skygw_log_write(LOGFILE_TRACE, logstr, "TraceyTracey", 3, 7); + mxs_log_flush(); #if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); + skygw_log_enable(LOGFILE_TRACE); #endif - logstr = "My name is Tracey Tracey 47 years and 7 months."; - err = skygw_log_write(LOGFILE_TRACE, logstr); + logstr = "My name is Tracey Tracey 47 years and 7 months."; + err = skygw_log_write(LOGFILE_TRACE, "%s", logstr); #if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); -#endif - logstr = "My name is Stacey %s"; - err = skygw_log_write_flush(LOGFILE_TRACE, logstr, " "); - skygw_logmanager_done(); -#if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); + skygw_log_enable(LOGFILE_TRACE); #endif - logstr = "My name is Philip"; - err = skygw_log_write(LOGFILE_TRACE, logstr); + logstr = "My name is Stacey %s"; + err = skygw_log_write_flush(LOGFILE_TRACE, logstr, " "); + mxs_log_finish(); #if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); + skygw_log_enable(LOGFILE_TRACE); #endif - logstr = "Philip."; - err = skygw_log_write(LOGFILE_TRACE, logstr); + logstr = "My name is Philip"; + err = skygw_log_write(LOGFILE_TRACE, "%s", logstr); #if !defined(SS_DEBUG) - skygw_log_enable(LOGFILE_TRACE); + skygw_log_enable(LOGFILE_TRACE); #endif - logstr = "Ph%dlip."; - err = skygw_log_write(LOGFILE_TRACE, logstr, 1); - - skygw_logmanager_init( log_argc, log_argv); - logstr = ("A terrible error has occurred!"); - err = skygw_log_write_flush(LOGFILE_ERROR, logstr); + logstr = "Philip."; + err = skygw_log_write(LOGFILE_TRACE, "%s", logstr); +#if !defined(SS_DEBUG) + skygw_log_enable(LOGFILE_TRACE); +#endif + logstr = "Ph%dlip."; + err = skygw_log_write(LOGFILE_TRACE, logstr, 1); - logstr = ("Hi, how are you?"); - err = skygw_log_write(LOGFILE_MESSAGE, logstr); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + logstr = ("A terrible error has occurred!"); + err = skygw_log_write_flush(LOGFILE_ERROR, "%s", logstr); - logstr = ("I'm doing fine!"); - err = skygw_log_write(LOGFILE_MESSAGE, logstr); + logstr = ("Hi, how are you?"); + err = skygw_log_write(LOGFILE_MESSAGE, "%s", logstr); - 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 operators & to both parts of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the address of the i-th element beyond a."); - err = skygw_log_write(LOGFILE_ERROR, logstr); + logstr = ("I'm doing fine!"); + err = skygw_log_write(LOGFILE_MESSAGE, "%s", logstr); - 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, 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(); + 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 operators & to both parts " + "of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the " + "address of the i-th element beyond a."); + err = skygw_log_write(LOGFILE_ERROR, "%s", logstr); + + 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, 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, "%s", logstr); + mxs_log_finish(); #if defined(TEST1) - - mes = skygw_message_init(); - mtx = simple_mutex_init(NULL, "testmtx"); - /** Test starts */ + mes = skygw_message_init(); + mtx = simple_mutex_init(NULL, "testmtx"); + /** Test starts */ - fprintf(stderr, "\nStarting test #1 \n"); - - /** 1 */ - for (i=0; imes = mes; - thr[i]->mtx = mtx; - thr[i]->nactive = &nactive; - } - nactive = nthr; + fprintf(stderr, "\nStarting test #1 \n"); - for (i=0; itid = p; - } + /** 1 */ + for (i = 0; i < nthr; i++) + { + thr[i] = (thread_t*)calloc(1, sizeof(thread_t)); + thr[i]->mes = mes; + thr[i]->mtx = mtx; + thr[i]->nactive = &nactive; + } + nactive = nthr; - do { - skygw_message_wait(mes); - simple_mutex_lock(mtx, true); - if (nactive > 0) { - simple_mutex_unlock(mtx); - continue; - } - break; - } while(true); + for (i = 0; i < nthr; i++) + { + pthread_t p; + pthread_create(&p, NULL, thr_run, thr[i]); + thr[i]->tid = p; + } - for (i=0; itid, NULL); - } - /** This is to release memory */ - skygw_logmanager_done(); - - simple_mutex_unlock(mtx); - - for (i=0; i 0) + { + simple_mutex_unlock(mtx); + continue; } + break; + } while(true); + + for (i = 0; i < nthr; i++) + { + pthread_join(thr[i]->tid, NULL); + } + /** This is to release memory */ + mxs_log_finish(); + + simple_mutex_unlock(mtx); + + for (i = 0; i < nthr; i++) + { + free(thr[i]); + } #endif #if defined(TEST2) - fprintf(stderr, "\nStarting test #2 \n"); + fprintf(stderr, "\nStarting test #2 \n"); - /** 2 */ - for (i=0; imes = mes; - thr[i]->mtx = mtx; - thr[i]->nactive = &nactive; + /** 2 */ + for (i = 0; i < nthr; i++) + { + thr[i] = (thread_t*)calloc(1, sizeof(thread_t)); + thr[i]->mes = mes; + thr[i]->mtx = mtx; + thr[i]->nactive = &nactive; + } + nactive = nthr; + + fprintf(stderr, + "\nLaunching %d threads, each iterating %d times.", + nthr, + NITER); + + for (i = 0; i < nthr; i++) + { + pthread_t p; + pthread_create(&p, NULL, thr_run_morelog, thr[i]); + thr[i]->tid = p; + } + + fprintf(stderr, ".. done"); + + fprintf(stderr, "\nStarting to wait threads.\n"); + + do + { + skygw_message_wait(mes); + simple_mutex_lock(mtx, true); + if (nactive > 0) + { + simple_mutex_unlock(mtx); + continue; } - nactive = nthr; + break; + } while(true); - fprintf(stderr, - "\nLaunching %d threads, each iterating %d times.", - nthr, - NITER); - - for (i=0; itid = p; - } + for (i = 0; i < nthr; i++) + { + pthread_join(thr[i]->tid, NULL); + } + /** This is to release memory */ + mxs_log_finish(); - fprintf(stderr, ".. done"); + simple_mutex_unlock(mtx); - fprintf(stderr, "\nStarting to wait threads.\n"); - - do { - skygw_message_wait(mes); - simple_mutex_lock(mtx, true); - if (nactive > 0) { - simple_mutex_unlock(mtx); - continue; - } - break; - } while(true); + fprintf(stderr, "\nFreeing thread memory."); - for (i=0; itid, NULL); - } - /** This is to release memory */ - skygw_logmanager_done(); - - simple_mutex_unlock(mtx); + for (i = 0; i < nthr; i++) + { + free(thr[i]); + } - fprintf(stderr, "\nFreeing thread memory."); - - for (i=0; imtx, true); - *td->nactive -= 1; - simple_mutex_unlock(td->mtx); - skygw_message_send(td->mes); - return NULL; + 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, "%s", logstr); + if (err != 0) + { + TEST_ERROR("Error, log write failed."); + } + ss_dassert(err == 0); + mxs_log_finish(); + mxs_log_init(NULL, "/tmp", LOG_TARGET_FS); + logstr = ("Testing. One, two, three, four\n"); + err = skygw_log_write(LOGFILE_ERROR, "%s", logstr); + if (err != 0) + { + TEST_ERROR("Error, log write failed."); + } + ss_dassert(err == 0); + 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, "%s", logstr); + if (err != 0) + { + TEST_ERROR("Error, log write failed."); + } + ss_dassert(err == 0); + 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); + skygw_message_send(td->mes); + return NULL; } -static int nstr( - char** str_arr) +static int nstr(char** str_arr) { - int i; + int i; - for (i=0; str_arr[i] != NULL; i++) { - } - return i; + for (i = 0; str_arr[i] != NULL; i++) + { + } + return i; } char* logs[] = { - "foo", - "bar", - "done", - "critical test logging", - "longer test l o g g g i n g", - "reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line", - "shoorter one", - "two", - "scrap : 834nuft984pnw8ynup4598yp8wup8upwn48t5gpn45", - "more the same : f98uft5p8ut2p44449upnt5", - "asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd98987", - NULL + "foo", + "bar", + "done", + "critical test logging", + "longer test l o g g g i n g", + "reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally looooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line", + "shoorter one", + "two", + "scrap : 834nuft984pnw8ynup4598yp8wup8upwn48t5gpn45", + "more the same : f98uft5p8ut2p44449upnt5", + "asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd98987", + NULL }; - -static void* thr_run_morelog( - void* data) +static void* thr_run_morelog(void* data) { - thread_t* td = (thread_t *)data; - int err; - int i; - int nmsg; + thread_t* td = (thread_t *)data; + int err; + int i; + int nmsg; - nmsg = nstr(logs); - - for (i=0; imtx, true); - *td->nactive -= 1; - simple_mutex_unlock(td->mtx); - skygw_message_send(td->mes); - return NULL; + } + + simple_mutex_lock(td->mtx, true); + *td->nactive -= 1; + simple_mutex_unlock(td->mtx); + skygw_message_send(td->mes); + return NULL; } diff --git a/log_manager/test/testorder.c b/log_manager/test/testorder.c index 7ca7696bb..4bb23074e 100644 --- a/log_manager/test/testorder.c +++ b/log_manager/test/testorder.c @@ -23,92 +23,124 @@ #include #include +static logfile_id_t id_to_priority(logfile_id_t id) +{ + switch (id) + { + case LOGFILE_ERROR: + return LOG_ERR; + + case LOGFILE_MESSAGE: + return LOG_NOTICE; + + case LOGFILE_TRACE: + return LOG_INFO; + + case LOGFILE_DEBUG: + return LOG_DEBUG; + + default: + assert(!true); + return LOG_ERR; + } +} + +static void skygw_log_enable(logfile_id_t id) +{ + mxs_log_set_priority_enabled(id_to_priority(id), true); +} + +static void skygw_log_disable(logfile_id_t id) +{ + mxs_log_set_priority_enabled(id_to_priority(id), false); +} + int main(int argc, char** argv) { - int iterations = 0, i, interval = 10; - int block_size; - int succp, err = 0; - char cwd[1024]; - char tmp[2048]; - char *message; - char** optstr; - long msg_index = 1; - struct timespec ts1; - ts1.tv_sec = 0; - - memset(cwd,0,1024); - if( argc <4){ - fprintf(stderr, - "Log Manager Log Order Test\n" - "Writes an ascending number into the error log to determine if log writes are in order.\n" - "Usage:\t testorder \n"); - return 1; - } - - block_size = atoi(argv[3]); - if(block_size < 1 || block_size > 1024){ - fprintf(stderr,"Message size too small or large, must be at least 1 byte long and must not exceed 1024 bytes."); - return 1; - } + int iterations = 0, i, interval = 10; + int block_size; + int succp, err = 0; + char cwd[1024]; + char tmp[2048]; + char *message; + long msg_index = 1; + struct timespec ts1; + ts1.tv_sec = 0; - - 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..."); - return 1; - } - - memset(tmp,0,1024); - - sprintf(tmp,"%s",cwd); - optstr[0] = strdup("log_manager"); - optstr[1] = strdup("-j"); - optstr[2] = strdup(tmp); - optstr[3] = NULL; - - iterations = atoi(argv[1]); - interval = atoi(argv[2]); - - succp = skygw_logmanager_init( 3, optstr); - if(!succp) - fprintf(stderr,"Error, log manager initialization failed.\n"); - ss_dassert(succp); - - skygw_log_disable(LOGFILE_TRACE); - skygw_log_disable(LOGFILE_MESSAGE); - skygw_log_disable(LOGFILE_DEBUG); - - for(i = 0;i 8192){ - fprintf(stderr,"Error: Message too long"); - break; - } - memset(message + strlen(message), ' ', msgsize); - memset(message + block_size - 1,'\0',1); - if(interval > 0 && i % interval == 0){ - err = skygw_log_write_flush(LOGFILE_ERROR, message); - }else{ - err = skygw_log_write(LOGFILE_ERROR, message); + memset(cwd, 0, 1024); + if (argc < 4) + { + fprintf(stderr, + "Log Manager Log Order Test\n" + "Writes an ascending number into the error log to determine if log writes are in order.\n" + "Usage:\t testorder \n"); + return 1; } - if(err){ - fprintf(stderr,"Error: log_manager returned %d",err); - break; - } - ts1.tv_nsec = 100*1000000; - nanosleep(&ts1, NULL); - } - skygw_log_flush(LOGFILE_ERROR); - skygw_logmanager_done(); - free(message); - free(optstr[0]); - free(optstr[1]); - free(optstr[2]); - free(optstr[3]); - free(optstr); - return 0; + block_size = atoi(argv[3]); + if (block_size < 1 || block_size > 1024) + { + fprintf(stderr,"Message size too small or large, must be at least 1 byte long and " + "must not exceed 1024 bytes."); + return 1; + } + + if (getcwd(cwd, sizeof(cwd)) == NULL || + (message = (char*)malloc(sizeof(char) * block_size)) == NULL) + { + fprintf(stderr,"Fatal Error, exiting..."); + return 1; + } + + memset(tmp, 0, 1024); + + sprintf(tmp, "%s", cwd); + + iterations = atoi(argv[1]); + interval = atoi(argv[2]); + + succp = mxs_log_init(NULL, tmp, LOG_TARGET_FS); + + if (!succp) + { + fprintf(stderr,"Error, log manager initialization failed.\n"); + } + ss_dassert(succp); + + skygw_log_disable(LOGFILE_TRACE); + skygw_log_disable(LOGFILE_MESSAGE); + skygw_log_disable(LOGFILE_DEBUG); + + for (i = 0; i < iterations; i++) + { + sprintf(message, "message|%ld", msg_index++); + int msgsize = block_size - strlen(message); + if (msgsize < 0 || msgsize > 8192) + { + fprintf(stderr,"Error: Message too long"); + break; + } + memset(message + strlen(message), ' ', msgsize); + memset(message + block_size - 1, '\0', 1); + if (interval > 0 && i % interval == 0) + { + err = skygw_log_write_flush(LOGFILE_ERROR, "%s", message); + } + else + { + err = skygw_log_write(LOGFILE_ERROR, "%s", message); + } + if (err) + { + fprintf(stderr,"Error: log_manager returned %d",err); + break; + } + ts1.tv_nsec = 100 * 1000000; + nanosleep(&ts1, NULL); + } + + mxs_log_flush(); + mxs_log_finish(); + free(message); + return 0; } diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index ab41845b4..8cefd370d 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -61,10 +61,6 @@ #include #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - #define QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (tthd = thd; init_embedded_mysql(mysql, client_flags); failp = check_embedded_connection(mysql, db); if (failp) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Call to check_embedded_connection failed. " - "Exiting."))); - goto return_err_with_thd; + MXS_ERROR("Call to check_embedded_connection failed."); + goto return_err_with_thd; } thd->clear_data_list(); /** Check that we are calling the client functions in right order */ if (mysql->status != MYSQL_STATUS_READY) { set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Invalid status %d in embedded server. " - "Exiting."))); + MXS_ERROR("Invalid status %d in embedded server.", + mysql->status); goto return_err_with_thd; } /** Clear result variables */ @@ -373,19 +361,15 @@ static bool create_parse_tree( failp = thd->set_db(virtual_db, strlen(virtual_db)); if (failp) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to set database in thread context."))); + MXS_ERROR("Failed to set database in thread context."); } failp = parse_sql(thd, &parser_state, NULL); if (failp) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [readwritesplit:create_parse_tree] failed to " - "create parse tree.", - pthread_self()))); + MXS_DEBUG("%lu [readwritesplit:create_parse_tree] failed to " + "create parse tree.", + pthread_self()); } return_here: return failp; @@ -444,18 +428,12 @@ static skygw_query_type_t resolve_query_type( if (sql_command_flags[lex->sql_command] & CF_IMPLICT_COMMIT_BEGIN) { - skygw_log_write( - LOGFILE_TRACE, - "Implicit COMMIT before executing the " - "next command."); + MXS_INFO("Implicit COMMIT before executing the next command."); } else if (sql_command_flags[lex->sql_command] & CF_IMPLICIT_COMMIT_END) { - skygw_log_write( - LOGFILE_TRACE, - "Implicit COMMIT after executing the " - "next command."); + MXS_INFO("Implicit COMMIT after executing the next command."); } } @@ -470,10 +448,8 @@ static skygw_query_type_t resolve_query_type( { if (LOG_IS_ENABLED(LOGFILE_TRACE)) { - skygw_log_write( - LOGFILE_TRACE, - "Disable autocommit : implicit START TRANSACTION" - " before executing the next command."); + MXS_INFO("Disable autocommit : implicit START TRANSACTION" + " before executing the next command."); } type |= QUERY_TYPE_DISABLE_AUTOCOMMIT; type |= QUERY_TYPE_BEGIN_TRX; @@ -645,12 +621,10 @@ static skygw_query_type_t resolve_query_type( Item::Type itype; itype = item->type(); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] Item %s:%s", - pthread_self(), - item->name, - STRITEMTYPE(itype)))); + MXS_DEBUG("%lu [resolve_query_type] Item %s:%s", + pthread_self(), + item->name, + STRITEMTYPE(itype)); if (itype == Item::SUBSELECT_ITEM) { continue; @@ -711,50 +685,40 @@ static skygw_query_type_t resolve_query_type( * belongs to this category. */ func_qtype |= QUERY_TYPE_WRITE; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype FUNC_SP, stored proc " - "or unknown function.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype FUNC_SP, stored proc " + "or unknown function.", + pthread_self()); break; case Item_func::UDF_FUNC: func_qtype |= QUERY_TYPE_WRITE; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype UDF_FUNC, user-defined " - "function.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype UDF_FUNC, user-defined " + "function.", + pthread_self()); break; case Item_func::NOW_FUNC: func_qtype |= QUERY_TYPE_LOCAL_READ; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype NOW_FUNC, could be " - "executed in MaxScale.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype NOW_FUNC, could be " + "executed in MaxScale.", + pthread_self()); break; /** System session variable */ case Item_func::GSYSVAR_FUNC: func_qtype |= QUERY_TYPE_SYSVAR_READ; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype GSYSVAR_FUNC, system " - "variable read.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype GSYSVAR_FUNC, system " + "variable read.", + pthread_self()); break; /** User-defined variable read */ case Item_func::GUSERVAR_FUNC: func_qtype |= QUERY_TYPE_USERVAR_READ; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype GUSERVAR_FUNC, user " - "variable read.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype GUSERVAR_FUNC, user " + "variable read.", + pthread_self()); break; /** User-defined variable modification */ case Item_func::SUSERVAR_FUNC: @@ -764,12 +728,10 @@ static skygw_query_type_t resolve_query_type( * 15.9.14 */ func_qtype |= QUERY_TYPE_GSYSVAR_WRITE; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype SUSERVAR_FUNC, user " - "variable write.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype SUSERVAR_FUNC, user " + "variable write.", + pthread_self()); break; case Item_func::UNKNOWN_FUNC: @@ -787,21 +749,17 @@ static skygw_query_type_t resolve_query_type( * type, for example, rand(), soundex(), * repeat() . */ - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "functype UNKNOWN_FUNC, " - "typically some system function.", - pthread_self()))); + MXS_DEBUG("%lu [resolve_query_type] " + "functype UNKNOWN_FUNC, " + "typically some system function.", + pthread_self()); break; default: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [resolve_query_type] " - "Functype %d.", - pthread_self(), - ftype))); - break; + MXS_DEBUG("%lu [resolve_query_type] " + "Functype %d.", + pthread_self(), + ftype); + break; } /**< switch */ /**< Set new query type */ type |= func_qtype; @@ -1208,7 +1166,7 @@ inline void add_str(char** buf, int* buflen, int* bufsize, char* str) *bufsize = (*bufsize) * 2 + isize; char *tmp = (char*)realloc(*buf,(*bufsize)* sizeof(char)); if(tmp == NULL){ - skygw_log_write_flush (LE,"Error: memory reallocation failed"); + MXS_ERROR("Error: memory reallocation failed."); free(*buf); *buf = NULL; *bufsize = 0; @@ -1251,7 +1209,7 @@ char* skygw_get_affected_fields(GWBUF* buf) lex->current_select = lex->all_selects_list; if((where = (char*)malloc(sizeof(char)*1)) == NULL) { - skygw_log_write_flush(LE,"Error: Memory allocation failed."); + MXS_ERROR("Memory allocation failed."); return NULL; } *where = '\0'; @@ -1449,13 +1407,11 @@ parsing_info_t* parsing_info_init( ss_dassert(mysql != NULL); if (mysql == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : call to mysql_real_connect failed due %d, %s.", - mysql_errno(mysql), - mysql_error(mysql)))); + MXS_ERROR("Call to mysql_real_connect failed due %d, %s.", + mysql_errno(mysql), + mysql_error(mysql)); - goto retblock; + goto retblock; } /** Set methods and authentication to mysql */ mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw"); @@ -1647,6 +1603,11 @@ retblock: skygw_query_op_t query_classifier_get_operation(GWBUF* querybuf) { + if (!query_is_parsed(querybuf)) + { + parse_query(querybuf); + } + LEX* lex = get_lex(querybuf); skygw_query_op_t operation = QUERY_OP_UNDEFINED; if(lex){ @@ -1687,6 +1648,9 @@ skygw_query_op_t query_classifier_get_operation(GWBUF* querybuf) case SQLCOM_CHANGE_DB: operation = QUERY_OP_CHANGE_DB; break; + case SQLCOM_LOAD: + operation = QUERY_OP_LOAD; + break; default: operation = QUERY_OP_UNDEFINED; diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index d59f82fad..e49022647 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -75,7 +75,8 @@ typedef enum { QUERY_OP_CREATE_INDEX = (1 << 8), QUERY_OP_DROP_TABLE = (1 << 9), QUERY_OP_DROP_INDEX = (1 << 10), - QUERY_OP_CHANGE_DB = (1 << 11) + QUERY_OP_CHANGE_DB = (1 << 11), + QUERY_OP_LOAD = (1 << 12) }skygw_query_op_t; typedef struct parsing_info_st { diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index 44b66e461..66c56eca7 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -10,5 +10,5 @@ endif() add_subdirectory(canonical_tests) add_executable(classify classify.c) -target_link_libraries(classify query_classifier fullcore) +target_link_libraries(classify query_classifier ${CURL_LIBRARIES} utils log_manager pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++ fullcore) add_test(Internal-TestQueryClassifier classify ${CMAKE_CURRENT_SOURCE_DIR}/input.sql ${CMAKE_CURRENT_SOURCE_DIR}/expected.sql) diff --git a/query_classifier/test/canonical_tests/CMakeLists.txt b/query_classifier/test/canonical_tests/CMakeLists.txt index 1dafc428e..925a3dd4f 100644 --- a/query_classifier/test/canonical_tests/CMakeLists.txt +++ b/query_classifier/test/canonical_tests/CMakeLists.txt @@ -7,7 +7,7 @@ else() file(COPY ${ERRMSG} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() endif() -add_executable(canonizer canonizer.c) +add_executable(canonizer canonizer.c ${CMAKE_SOURCE_DIR}/server/core/random_jkiss.c) target_link_libraries(canonizer pthread query_classifier z dl ssl aio crypt crypto rt m ${EMBEDDED_LIB} fullcore stdc++) add_test(NAME Internal-TestCanonicalQuery COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/canontest.sh ${CMAKE_CURRENT_BINARY_DIR}/test.log diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 28dd5a873..9687ee699 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,18 +1,20 @@ if(BUILD_TESTS OR BUILD_TOOLS) - add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c maxscale_pcre2.c) + add_library(fullcore STATIC random_jkiss.c adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c maxscale_pcre2.c) if(WITH_JEMALLOC) target_link_libraries(fullcore ${JEMALLOC_LIBRARIES}) elseif(WITH_TCMALLOC) target_link_libraries(fullcore ${TCMALLOC_LIBRARIES}) endif() target_link_libraries(fullcore ${CURL_LIBRARIES} utils log_manager pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ssl aio rt crypt dl crypto inih z m stdc++) + add_dependencies(fullcore pcre2) endif() add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c monitor.c adminusers.c secrets.c filter.c modutil.c hint.c - housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c maxscale_pcre2.c) + housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c random_jkiss.c maxscale_pcre2.c) +add_dependencies(maxscale pcre2) if(WITH_JEMALLOC) target_link_libraries(maxscale ${JEMALLOC_LIBRARIES}) @@ -23,11 +25,11 @@ endif() target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++) install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR}) -add_executable(maxkeys maxkeys.c secrets.c utils.c gwdirs.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) +add_executable(maxkeys maxkeys.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) target_link_libraries(maxkeys utils pthread crypt crypto) install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR}) -add_executable(maxpasswd maxpasswd.c secrets.c utils.c gwdirs.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) +add_executable(maxpasswd maxpasswd.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) target_link_libraries(maxpasswd utils pthread crypt crypto) install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR}) diff --git a/server/core/adminusers.c b/server/core/adminusers.c index 81df4bf4f..310d741b7 100644 --- a/server/core/adminusers.c +++ b/server/core/adminusers.c @@ -30,10 +30,6 @@ #include #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; /** * @file adminusers.c - Administration user account management @@ -163,19 +159,13 @@ char fname[1024], *home, *cpasswd; fname[1023] = '\0'; if (users == NULL) { - LOGIF(LM, - (skygw_log_write(LOGFILE_MESSAGE, - "Create initial password file."))); + MXS_NOTICE("Create initial password file."); if ((users = users_alloc()) == NULL) return ADMIN_ERR_NOMEM; if ((fp = fopen(fname, "w")) == NULL) { - LOGIF(LE, - (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to create password file %s.", - fname))); + MXS_ERROR("Unable to create password file %s.", fname); return ADMIN_ERR_PWDFILEOPEN; } fclose(fp); @@ -188,10 +178,7 @@ char fname[1024], *home, *cpasswd; users_add(users, uname, cpasswd); if ((fp = fopen(fname, "a")) == NULL) { - LOGIF(LE, - (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Unable to append to password file %s.", - fname))); + MXS_ERROR("Unable to append to password file %s.", fname); return ADMIN_ERR_FILEAPPEND; } fprintf(fp, "%s:%s\n", uname, cpasswd); @@ -223,19 +210,14 @@ char* admin_remove_user( int n_deleted; if (!admin_search_user(uname)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Couldn't find user %s. Removing user failed", - uname))); - return ADMIN_ERR_USERNOTFOUND; + MXS_ERROR("Couldn't find user %s. Removing user failed.", uname); + return ADMIN_ERR_USERNOTFOUND; } if (admin_verify(uname, passwd) == 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Authentication failed, wrong user/password " - "combination. Removing user failed."))); - return ADMIN_ERR_AUTHENTICATION; + MXS_ERROR("Authentication failed, wrong user/password " + "combination. Removing user failed."); + return ADMIN_ERR_AUTHENTICATION; } @@ -243,11 +225,9 @@ char* admin_remove_user( n_deleted = users_delete(users, uname); if (n_deleted == 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Deleting the only user is forbidden. Add new " - "user before deleting the one."))); - return ADMIN_ERR_DELLASTUSER; + MXS_ERROR("Deleting the only user is forbidden. Add new " + "user before deleting the one."); + return ADMIN_ERR_DELLASTUSER; } /** * Open passwd file and remove user from the file. @@ -262,13 +242,11 @@ char* admin_remove_user( if ((fp = fopen(fname, "r")) == NULL) { int err = errno; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to open password file %s : errno %d.\n" - "Removing user from file failed; it must be done " - "manually.", - fname, - err))); + MXS_ERROR("Unable to open password file %s : errno %d.\n" + "Removing user from file failed; it must be done " + "manually.", + fname, + err); return ADMIN_ERR_PWDFILEOPEN; } /** @@ -277,13 +255,11 @@ char* admin_remove_user( if ((fp_tmp = fopen(fname_tmp, "w")) == NULL) { int err = errno; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to open tmp file %s : errno %d.\n" - "Removing user from passwd file failed; it must be done " - "manually.", - fname_tmp, - err))); + MXS_ERROR("Unable to open tmp file %s : errno %d.\n" + "Removing user from passwd file failed; it must be done " + "manually.", + fname_tmp, + err); fclose(fp); return ADMIN_ERR_TMPFILEOPEN; } @@ -293,13 +269,11 @@ char* admin_remove_user( */ if (fgetpos(fp, &rpos) != 0) { int err = errno; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to process passwd file %s : errno %d.\n" - "Removing user from file failed, and must be done " - "manually.", - fname, - err))); + MXS_ERROR("Unable to process passwd file %s : errno %d.\n" + "Removing user from file failed, and must be done " + "manually.", + fname, + err); fclose(fp); fclose(fp_tmp); unlink(fname_tmp); @@ -313,26 +287,21 @@ char* admin_remove_user( * Unmatching lines are copied to tmp file. */ if (strncmp(uname, fusr, strlen(uname)+1) != 0) { - if(fsetpos(fp, &rpos) != 0){ /** one step back */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to set stream position. "))); - - } - fgets(line, LINELEN, fp); - fputs(line, fp_tmp); + if(fsetpos(fp, &rpos) != 0){ /** one step back */ + MXS_ERROR("Unable to set stream position. "); + } + fgets(line, LINELEN, fp); + fputs(line, fp_tmp); } if (fgetpos(fp, &rpos) != 0) { int err = errno; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to process passwd file %s : " - "errno %d.\n" - "Removing user from file failed, and must be " - "done manually.", - fname, - err))); + MXS_ERROR("Unable to process passwd file %s : " + "errno %d.\n" + "Removing user from file failed, and must be " + "done manually.", + fname, + err); fclose(fp); fclose(fp_tmp); unlink(fname_tmp); @@ -345,14 +314,12 @@ char* admin_remove_user( */ if (rename(fname_tmp, fname)) { int err = errno; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to rename new passwd file %s : errno " - "%d.\n" - "Rename it to %s manually.", - fname_tmp, - err, - fname))); + MXS_ERROR("Unable to rename new passwd file %s : errno " + "%d.\n" + "Rename it to %s manually.", + fname_tmp, + err, + fname); unlink(fname_tmp); fclose(fp_tmp); return ADMIN_ERR_PWDFILEACCESS; diff --git a/server/core/buffer.c b/server/core/buffer.c index 8de0f6851..fe50f0303 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -35,7 +35,9 @@ * 15/07/2014 Mark Riddoch Addition of properties * 28/08/2014 Mark Riddoch Adition of tail pointer to speed * the gwbuf_append process - * + * 09/11/2015 Martin Brampton Add buffer tracing (conditional compilation), + * accessed by "show buffers" maxadmin command + * * @endverbatim */ #include @@ -47,15 +49,23 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; +#if defined(BUFFER_TRACE) +#include +#include + +static HASHTABLE *buffer_hashtable = NULL; +#endif static buffer_object_t* gwbuf_remove_buffer_object( GWBUF* buf, buffer_object_t* bufobj); +#if defined(BUFFER_TRACE) +static void gwbuf_add_to_hashtable(GWBUF *buf); +static int bhashfn (void *key); +static int bcmpfn (void *key1, void *key2); +static void gwbuf_remove_from_hashtable(GWBUF *buf); +#endif /** * Allocate a new gateway buffer structure of size bytes. @@ -114,14 +124,114 @@ retblock: if (rval == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); } +#if defined(BUFFER_TRACE) + else + { + gwbuf_add_to_hashtable(rval); + } +#endif return rval; } +#if defined(BUFFER_TRACE) +/** + * Store a trace of buffer creation + * + * @param buf The buffer to record + */ +static void +gwbuf_add_to_hashtable(GWBUF *buf) +{ + void *array[16]; + size_t size, i, total; + char **strings; + char *tracetext; + + size = backtrace (array, 16); + strings = backtrace_symbols (array, size); + total = (2 * size) + 1; + for (i = 0; i < size; i++) + { + total += strlen(strings[i]); + } + tracetext = (char *)malloc(total); + if (tracetext) + { + char *ptr = tracetext; + for (i = 0; i < size; i++) + { + sprintf(ptr, "\t%s\n", strings[i]); + ptr += (strlen(strings[i]) + 2); + } + free (strings); + + if (NULL == buffer_hashtable) + { + buffer_hashtable = hashtable_alloc(10000, bhashfn, bcmpfn); + hashtable_memory_fns(buffer_hashtable,NULL,NULL,NULL,(HASHMEMORYFN)free); + } + hashtable_add(buffer_hashtable, buf, (void *)tracetext); + } +} + +/** + * Hash a buffer (address) to an integer + * + * @param key The pointer to the buffer + */ +static int +bhashfn(void *key) +{ + return (int)((uintptr_t) key % INT_MAX); +} + +/** + * Compare two buffer keys (pointers) + * + * @param key1 The pointer to the first buffer + * @param key2 The pointer to the second buffer + */ +static int +bcmpfn(void *key1, void *key2) +{ + return key1 == key2 ? 0 : 1; +} + +/** + * Remove a buffer from the store of buffer traces + * + * @param buf The buffer to be removed + */ +static void +gwbuf_remove_from_hashtable(GWBUF *buf) +{ + hashtable_delete(buffer_hashtable, buf); +} + +/** + * Print all buffer traces via a given print DCB + * + * @param pdcb Print DCB for output + */ +void +dprintAllBuffers(void *pdcb) +{ + void *buf; + char *backtrace; + HASHITERATOR *buffers = hashtable_iterator(buffer_hashtable); + while (NULL != (buf = hashtable_next(buffers))) + { + dcb_printf((DCB *)pdcb, "Buffer: %p\n", (void *)buf); + backtrace = hashtable_fetch(buffer_hashtable, buf); + dcb_printf((DCB *)pdcb, "%s", backtrace); + } + hashtable_iterator_free(buffers); +} +#endif + /** * Free a gateway buffer * @@ -162,6 +272,9 @@ BUF_PROPERTY *prop; buf->hint = buf->hint->next; hint_free(h); } +#if defined(BUFFER_TRACE) + gwbuf_remove_from_hashtable(buf); +#endif free(buf); } @@ -184,10 +297,8 @@ GWBUF *rval; { ss_dassert(rval != NULL); char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); return NULL; } @@ -201,6 +312,9 @@ GWBUF *rval; rval->tail = rval; rval->next = NULL; CHK_GWBUF(rval); +#if defined(BUFFER_TRACE) + gwbuf_add_to_hashtable(rval); +#endif return rval; } @@ -249,10 +363,8 @@ GWBUF *gwbuf_clone_portion( { ss_dassert(clonebuf != NULL); char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); return NULL; } atomic_add(&buf->sbuf->refcount, 1); @@ -268,6 +380,9 @@ GWBUF *gwbuf_clone_portion( clonebuf->next = NULL; clonebuf->tail = clonebuf; CHK_GWBUF(clonebuf); +#if defined(BUFFER_TRACE) + gwbuf_add_to_hashtable(clonebuf); +#endif return clonebuf; } @@ -504,10 +619,8 @@ void gwbuf_add_buffer_object( if (newb == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); return; } newb->bo_id = id; @@ -595,10 +708,8 @@ BUF_PROPERTY *prop; { ss_dassert(prop != NULL); char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } prop->name = strdup(name); diff --git a/server/core/config.c b/server/core/config.c index 457e257af..0596cd6bd 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 @@ -73,65 +73,60 @@ #include #include #include -#include #include #include #define PCRE2_CODE_UNIT_WIDTH 8 #include -/** According to the PCRE manual, this should be a multiple of 3 */ -#define MAXSCALE_PCRE_BUFSZ 24 - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - 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; } /** @@ -142,213 +137,252 @@ char *ptr; */ char* config_clean_string_list(char* str) { - char *tmp; - - if((tmp = malloc(sizeof(char)*(strlen(str) + 1))) != NULL) + char *dest; + size_t destsize = strlen(str) + 1; + if ((dest = malloc(destsize)) != NULL) { - char *ptr; - int match[MAXSCALE_PCRE_BUFSZ]; - pcre* re; - const char *re_err; - int err_offset,rval; + pcre2_code* re; + pcre2_match_data* data; + int re_err; + size_t err_offset; - - tmp[0] = '\0'; - - if((re = pcre_compile("\\s*+([^,]*[^,\\s])",0,&re_err,&err_offset,NULL)) == NULL) + if ((re = pcre2_compile((PCRE2_SPTR) "[[:space:],]*([^,]+)\\b[[:space:],]*", PCRE2_ZERO_TERMINATED, 0, + &re_err, &err_offset, NULL)) == NULL || + (data = pcre2_match_data_create_from_pattern(re, NULL)) == NULL) { - skygw_log_write(LE,"[%s] Error: Regular expression compilation failed at %d: %s", - __FUNCTION__,err_offset,re_err); - free(tmp); + PCRE2_UCHAR errbuf[STRERROR_BUFLEN]; + pcre2_get_error_message(re_err, errbuf, sizeof(errbuf)); + MXS_ERROR("[%s] Regular expression compilation failed at %d: %s", + __FUNCTION__, (int)err_offset, errbuf); + pcre2_code_free(re); + free(dest); return NULL; } - ptr = str; - - while((rval = pcre_exec(re,NULL,ptr,strlen(ptr),0,0,(int*)&match,MAXSCALE_PCRE_BUFSZ)) > 1) + const char *replace = "$1,"; + int rval = 0; + while ((rval = pcre2_substitute(re, (PCRE2_SPTR) str, PCRE2_ZERO_TERMINATED, 0, + PCRE2_SUBSTITUTE_GLOBAL, data, NULL, + (PCRE2_SPTR) replace, PCRE2_ZERO_TERMINATED, + (PCRE2_UCHAR*) dest, &destsize)) == PCRE2_ERROR_NOMEMORY) { - const char* 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]]; + char* tmp = realloc(dest, destsize * 2); + if (tmp == NULL) + { + free(dest); + dest = NULL; + break; + } + dest = tmp; + destsize *= 2; } - pcre_free(re); + + /** Remove the trailing comma */ + if (dest && dest[strlen(dest) - 1] == ',') + { + dest[strlen(dest) - 1] = '\0'; + } + + pcre2_code_free(re); + pcre2_match_data_free(data); } else { - skygw_log_write(LE,"[%s] Error: Memory allocation failed.",__FUNCTION__); + MXS_ERROR("[%s] Memory allocation failed.", __FUNCTION__); } - return tmp; + return dest; } + /** * 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) + { + MXS_ERROR("[%s] Memory allocation failed.", __FUNCTION__); + return 0; + } + strcat(tmp, ","); + strcat(tmp, value); + if ((p1->value = config_clean_string_list(tmp)) == NULL) + { + p1->value = tmp; + MXS_ERROR("[%s] 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; + MXS_ERROR("%s", 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; } /** @@ -359,1132 +393,1112 @@ 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."); + MXS_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) + { + MXS_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); - } + 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) + { + ((SERVICE*) obj->element)->log_auth_warnings = (bool) truthval; + } 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 */ + { + MXS_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++; + MXS_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++; + MXS_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++; + MXS_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) + { + MXS_ERROR("Certificate authority file for service '%s' " + "not found: %s", + obj->object, ssl_ca_cert); + error_count++; + } + + if (access(ssl_cert, F_OK) != 0) + { + MXS_ERROR("Server certificate file for service '%s' not found: %s", + obj->object, + ssl_cert); + error_count++; + } + + if (access(ssl_key, F_OK) != 0) + { + MXS_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) + { + MXS_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 */ + MXS_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) + { + MXS_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) + { + MXS_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) + { + MXS_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) + { + MXS_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) + { + MXS_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 + { + MXS_ERROR("Parameter was NULL"); + } + } + } + } /*< if (rw_split) */ + } /*< if (router) */ + else + { + obj->element = NULL; + MXS_ERROR("No router defined for service '%s'.", 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; + MXS_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) + { + MXS_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 + { + MXS_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) + { + MXS_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)) + { + MXS_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 + { + MXS_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 + { + MXS_ERROR("Listener '%s', " + "service '%s' not found. " + "Listener will not execute.", + obj->object, service); + error_count++; + } + } + } + else + { + MXS_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 + { + MXS_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))); - } + MXS_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) + { + MXS_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) + { + MXS_ERROR("Monitor '%s' defines a " + "username with no password.", + obj->object); + error_count++; + } + } + else + { + obj->element = NULL; + MXS_ERROR("Monitor '%s' is missing a " + "require module parameter.", + obj->object); + error_count++; + } + } + else if (strcmp(type, "server") != 0 && strcmp(type, "filter") != 0) + { + MXS_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) + { + MXS_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; + } } /** @@ -1495,7 +1509,7 @@ CONFIG_PARAMETER *p1, *p2; int config_threadcount() { - return gateway.n_threads; + return gateway.n_threads; } /** @@ -1507,7 +1521,7 @@ config_threadcount() unsigned int config_nbpolls() { - return gateway.n_nbpoll; + return gateway.n_nbpoll; } /** @@ -1519,7 +1533,7 @@ config_nbpolls() unsigned int config_pollsleep() { - return gateway.pollsleep; + return gateway.pollsleep; } /** @@ -1530,133 +1544,154 @@ config_pollsleep() FEEDBACK_CONF * config_get_feedback_data() { - return &feedback; + return &feedback; } -static struct { - char *logname; - logfile_id_t logfile; +static struct +{ + char* name; + int priority; + char* replacement; } lognames[] = { - { "log_messages", LOGFILE_MESSAGE }, - { "log_trace", LOGFILE_TRACE }, - { "log_debug", LOGFILE_DEBUG }, - { NULL, 0 } + { "log_messages", LOG_NOTICE, "log_notice" }, // Deprecated + { "log_trace", LOG_INFO, "log_info" }, // Deprecated + { "log_debug", LOG_DEBUG, NULL }, + { "log_warning", LOG_WARNING, NULL }, + { "log_notice", LOG_NOTICE, NULL }, + { "log_info", LOG_INFO, NULL }, + { 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 + { + MXS_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 + { + MXS_WARNING("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 + { + MXS_ERROR("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 + { + MXS_ERROR("Invalid timeout value for 'auth_write_timeout': %s", value); + } + } + else + { + for (i = 0; lognames[i].name; i++) + { + if (strcasecmp(name, lognames[i].name) == 0) + { + if (lognames[i].replacement) + { + MXS_WARNING("In the configuration file the use of '%s' is deprecated, " + "use '%s' instead.", + lognames[i].name, lognames[i].replacement); + } + + mxs_log_set_priority_enabled(lognames[i].priority, config_truth_value((char*)value)); + } + } + } + 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; } /** @@ -1665,37 +1700,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); + } } /** @@ -1704,681 +1752,683 @@ 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) + { + MXS_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"); + + 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) + { + service->log_auth_warnings = (bool)truthval; + } 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) + { + MXS_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) + { + MXS_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 + { + MXS_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; + MXS_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 + { + MXS_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) + { + MXS_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) + { + MXS_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", - 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) + { + MXS_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; + } + MXS_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", @@ -2391,251 +2441,286 @@ 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)) + { + MXS_NOTICE("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) + { + MXS_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 + { + MXS_INFO("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; + MXS_ERROR("Memory allocation failed when adding configuration parameters."); + return; } nptr->name = strdup(key); @@ -2671,8 +2756,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); @@ -2698,8 +2782,7 @@ bool config_has_duplicate_sections(const char* config) if (hashtable_add(hash, section, "") == 0) { - skygw_log_write(LE, "Error: Duplicate section found: %s", - section); + MXS_ERROR("Duplicate section found: %s", section); rval = true; } } @@ -2709,15 +2792,15 @@ bool config_has_duplicate_sections(const char* config) else { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, "Error: Failed to open file '%s': %s", config, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to open file '%s': %s", config, + strerror_r(errno, errbuf, sizeof(errbuf))); rval = true; } } else { - skygw_log_write(LE, "Error: Failed to allocate enough memory when checking" - " for duplicate sections in configuration file."); + MXS_ERROR("Failed to allocate enough memory when checking" + " for duplicate sections in configuration file."); rval = true; } @@ -2766,9 +2849,9 @@ int maxscale_getline(char** dest, int* size, FILE* file) } else { - skygw_log_write(LE, "Error: Failed to reallocate memory from %d" - " bytes to %d bytes when reading from file.", - *size, *size * 2); + MXS_ERROR("Failed to reallocate memory from %d" + " bytes to %d bytes when reading from file.", + *size, *size * 2); destptr[offset - 1] = '\0'; *dest = destptr; return -1; diff --git a/server/core/dbusers.c b/server/core/dbusers.c index bfb5bdc2e..1eca857ed 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, \ @@ -105,10 +121,6 @@ #define ERROR_NO_SHOW_DATABASES "%s: Unable to load database grant information, \ MaxScale authentication will proceed without including database permissions. \ To correct this GRANT SHOW DATABASES ON *.* privilege to the user %s." -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; static int getUsers(SERVICE *service, USERS *users); static int uh_cmpfun( void* v1, void* v2); @@ -137,6 +149,60 @@ 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. + * @param userhost User host address + * @param wildcardhost Host address in the grant + * @return True if the host address matches + */ +bool host_matches_singlechar_wildcard(const char* user, const char* wild) +{ + while (*user != '\0' && *wild != '\0') + { + if (*user != *wild && *wild != '_') + { + return false; + } + user++; + wild++; + } + return true; +} + /** * Load the user/passwd form mysql.user table into the service users' hashtable * environment. @@ -222,20 +288,16 @@ HASHTABLE *oldresources; /* digest compare */ if (oldusers != NULL && memcmp(oldusers->cksum, newusers->cksum, SHA_DIGEST_LENGTH) == 0) { /* same data, nothing to do */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [replace_mysql_users] users' tables not switched, checksum is the same", - pthread_self()))); + MXS_DEBUG("%lu [replace_mysql_users] users' tables not switched, checksum is the same", + pthread_self()); /* free the new table */ users_free(newusers); i = 0; } else { /* replace the service with effective new data */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [replace_mysql_users] users' tables replaced, checksum differs", - pthread_self()))); + MXS_DEBUG("%lu [replace_mysql_users] users' tables replaced, checksum differs", + pthread_self()); service->users = newusers; } @@ -252,6 +314,53 @@ HASHTABLE *oldresources; return i; } +/** + * Check if the IP address is a valid MySQL IP address. The IP address can contain + * single or multi-character wildcards as used by MySQL. + * @param host IP address to check + * @return True if the address is a valid, MySQL type IP address + */ +bool is_ipaddress(const char* host) +{ + while (*host != '\0') + { + if (!isdigit(*host) && *host != '.' && *host != '_' && *host != '%') + { + return false; + } + host++; + } + return true; +} + +/** + * Check if an IP address has single-character wildcards. A single-character + * wildcard is represented by an underscore in the MySQL hostnames. + * @param host Hostname to check + * @return True if the hostname is a valid IP address with a single character wildcard + */ +bool host_has_singlechar_wildcard(const char *host) +{ + const char* chrptr = host; + bool retval = false; + + while (*chrptr != '\0') + { + if (!isdigit(*chrptr) && *chrptr != '.') + { + if (*chrptr == '_') + { + retval = true; + } + else + { + return false; + } + } + chrptr++; + } + return retval; +} /** * Add a new MySQL user with host, password and netmask into the service users table @@ -308,16 +417,18 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p if (strcmp(host, "%") == 0) { strcpy(ret_ip, "0.0.0.0"); key.netmask = 0; + } else if (strnlen(host, MYSQL_HOST_MAXLEN + 1) <= MYSQL_HOST_MAXLEN && + is_ipaddress(host) && + host_has_singlechar_wildcard(host)) { + strcpy(key.hostname, host); + strcpy(ret_ip, "0.0.0.0"); + key.netmask = 0; } else { /* hostname without % wildcards has netmask = 32 */ key.netmask = normalize_hostname(host, ret_ip); if (key.netmask == -1) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : strdup() failed in normalize_hostname for %s@%s", - user, - host))); + MXS_ERROR("strdup() failed in normalize_hostname for %s@%s", user, host); } } @@ -375,25 +486,21 @@ addDatabases(SERVICE *service, MYSQL *con) return -1; if (mysql_query(con, get_showdbs_priv_query)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); - return -1; + MXS_ERROR("Loading database names for service %s encountered " + "error: %s.", + service->name, + mysql_error(con)); + return -1; } result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); - return -1; + MXS_ERROR("Loading database names for service %s encountered " + "error: %s.", + service->name, + mysql_error(con)); + return -1; } /* Result has only one row */ @@ -404,10 +511,7 @@ addDatabases(SERVICE *service, MYSQL *con) } else { ndbs = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - ERROR_NO_SHOW_DATABASES, - service->name, service_user))); + MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user); } /* free resut set */ @@ -419,34 +523,30 @@ addDatabases(SERVICE *service, MYSQL *con) } if (mysql_query(con, "SHOW DATABASES")) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading database names for service %s encountered " + "error: %s.", + service->name, + mysql_error(con)); - return -1; + return -1; } result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading database names for service %s encountered " + "error: %s.", + service->name, + mysql_error(con)); - return -1; + return -1; } /* insert key and value "" */ while ((row = mysql_fetch_row(result))) { if(resource_add(service->resources, row[0], "")) { - skygw_log_write(LOGFILE_DEBUG,"%s: Adding database %s to the resouce hash.",service->name,row[0]); + MXS_DEBUG("%s: Adding database %s to the resouce hash.", service->name, row[0]); } } @@ -480,25 +580,21 @@ getDatabases(SERVICE *service, MYSQL *con) return -1; if (mysql_query(con, get_showdbs_priv_query)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error when querying database privileges: %s.", - service->name, - mysql_error(con)))); - return -1; + MXS_ERROR("Loading database names for service %s encountered " + "error when querying database privileges: %s.", + service->name, + mysql_error(con)); + return -1; } result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error when storing result set of database privilege query: %s.", - service->name, - mysql_error(con)))); - return -1; + MXS_ERROR("Loading database names for service %s encountered " + "error when storing result set of database privilege query: %s.", + service->name, + mysql_error(con)); + return -1; } /* Result has only one row */ @@ -509,10 +605,7 @@ getDatabases(SERVICE *service, MYSQL *con) } else { ndbs = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - ERROR_NO_SHOW_DATABASES, - service->name, service_user))); + MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user); } /* free resut set */ @@ -524,27 +617,23 @@ getDatabases(SERVICE *service, MYSQL *con) } if (mysql_query(con, "SHOW DATABASES")) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error when executing SHOW DATABASES query: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading database names for service %s encountered " + "error when executing SHOW DATABASES query: %s.", + service->name, + mysql_error(con)); - return -1; + return -1; } result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading database names for service %s encountered " - "error when storing the result set: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading database names for service %s encountered " + "error when storing the result set: %s.", + service->name, + mysql_error(con)); - return -1; + return -1; } /* Now populate service->resources hashatable with db names */ @@ -552,7 +641,7 @@ getDatabases(SERVICE *service, MYSQL *con) /* insert key and value "" */ while ((row = mysql_fetch_row(result))) { - skygw_log_write(LOGFILE_DEBUG,"%s: Adding database %s to the resouce hash.",service->name,row[0]); + MXS_DEBUG("%s: Adding database %s to the resouce hash.", service->name, row[0]); resource_add(service->resources, row[0], ""); } @@ -581,7 +670,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; @@ -632,30 +722,22 @@ getAllUsers(SERVICE *service, USERS *users) if (con == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : mysql_init: %s", - mysql_error(con)))); + MXS_ERROR("mysql_init: %s", mysql_error(con)); goto cleanup; } /** Set read, write and connect timeout values */ if (gw_mysql_set_timeouts(con)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set timeout values for backend " - "connection."))); + MXS_ERROR("Failed to set timeout values for backend connection."); mysql_close(con); goto cleanup; } if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set external connection. " - "It is needed for backend server connections."))); + MXS_ERROR("Failed to set external connection. " + "It is needed for backend server connections."); mysql_close(con); goto cleanup; } @@ -678,11 +760,9 @@ getAllUsers(SERVICE *service, USERS *users) if (server == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to get user data from backend database " - "for service [%s]. Missing server information.", - service->name))); + MXS_ERROR("Unable to get user data from backend database " + "for service [%s]. Missing server information.", + service->name); mysql_close(con); goto cleanup; } @@ -701,30 +781,22 @@ getAllUsers(SERVICE *service, USERS *users) if (con == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : mysql_init: %s", - mysql_error(con)))); + MXS_ERROR("mysql_init: %s", mysql_error(con)); goto cleanup; } /** Set read, write and connect timeout values */ if (gw_mysql_set_timeouts(con)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set timeout values for backend " - "connection."))); + MXS_ERROR("Failed to set timeout values for backend connection."); mysql_close(con); goto cleanup; } if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set external connection. " - "It is needed for backend server connections."))); + MXS_ERROR("Failed to set external connection. " + "It is needed for backend server connections."); mysql_close(con); goto cleanup; } @@ -747,25 +819,30 @@ getAllUsers(SERVICE *service, USERS *users) if (server == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to get user data from backend database " - "for service [%s]. Missing server information.", - service->name))); + MXS_ERROR("Unable to get user data from backend database " + "for service [%s]. Missing server information.", + service->name); 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( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); mysql_close(con); goto cleanup; } else { @@ -774,12 +851,9 @@ getAllUsers(SERVICE *service, USERS *users) * try counting users from mysql.user without DB names. */ if (mysql_query(con, MYSQL_USERS_COUNT)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); mysql_close(con); goto cleanup; } @@ -789,12 +863,9 @@ getAllUsers(SERVICE *service, USERS *users) result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); mysql_close(con); goto cleanup; } @@ -806,20 +877,14 @@ getAllUsers(SERVICE *service, USERS *users) mysql_free_result(result); if (!nusers) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Counting users for service %s returned 0", - service->name))); + MXS_ERROR("Counting users for service %s returned 0.", service->name); 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)) { @@ -832,13 +897,11 @@ getAllUsers(SERVICE *service, USERS *users) if (1142 != mysql_errno(con)) { /* This is an error we cannot handle, return */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users with dbnames for service [%s] encountered " - "error: [%s], MySQL errno %i", - service->name, - mysql_error(con), - mysql_errno(con)))); + MXS_ERROR("Loading users with dbnames for service [%s] encountered " + "error: [%s], MySQL errno %i", + service->name, + mysql_error(con), + mysql_errno(con)); mysql_close(con); @@ -849,10 +912,7 @@ getAllUsers(SERVICE *service, USERS *users) * try loading users from mysql.user without DB names. */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - ERROR_NO_SHOW_DATABASES, - service->name, service_user))); + MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user); /* check for root user select */ if(service->enable_root) { @@ -862,13 +922,11 @@ getAllUsers(SERVICE *service, USERS *users) } if (mysql_query(con, users_query)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s], code %i", - service->name, - mysql_error(con), - mysql_errno(con)))); + MXS_ERROR("Loading users for service [%s] encountered " + "error: [%s], code %i", + service->name, + mysql_error(con), + mysql_errno(con)); mysql_close(con); @@ -877,30 +935,25 @@ getAllUsers(SERVICE *service, USERS *users) /* users successfully loaded but without db grants */ - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Loading users from [mysql.user] without access to [mysql.db] for " - "service [%s]. MaxScale Authentication with DBname on connect " - "will not consider database grants.", - service->name))); + MXS_NOTICE("Loading users from [mysql.user] without access to [mysql.db] for " + "service [%s]. MaxScale Authentication with DBname on connect " + "will not consider database grants.", + service->name); } } else { /* * users successfully loaded with db grants. */ - skygw_log_write(LOGFILE_DEBUG,"[%s] Loading users with db grants.",service->name); + MXS_DEBUG("[%s] Loading users with db grants.",service->name); db_grants = 1; } result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading users for service %s encountered error: %s.", + service->name, + mysql_error(con)); mysql_free_result(result); mysql_close(con); @@ -912,12 +965,9 @@ getAllUsers(SERVICE *service, USERS *users) if (users_data == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation for user data failed due to " - "%d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation for user data failed due to %d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); mysql_free_result(result); mysql_close(con); @@ -947,16 +997,14 @@ getAllUsers(SERVICE *service, USERS *users) if (row[2] != NULL) { /* detect mysql_old_password (pre 4.1 protocol) */ if (strlen(row[2]) == 16) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%s: The user %s@%s has on old password in the " - "backend database. MaxScale does not support these " - "old passwords. This user will not be able to connect " - "via MaxScale. Update the users password to correct " - "this.", - service->name, - row[0], - row[1]))); + MXS_ERROR("%s: The user %s@%s has on old password in the " + "backend database. MaxScale does not support these " + "old passwords. This user will not be able to connect " + "via MaxScale. Update the users password to correct " + "this.", + service->name, + row[0], + row[1]); continue; } @@ -980,28 +1028,26 @@ getAllUsers(SERVICE *service, USERS *users) havedb = true; if(service->strip_db_esc) { strip_escape_chars(dbnm); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "[%s]: %s -> %s", - service->name, - row[5], - dbnm))); + MXS_DEBUG("[%s]: %s -> %s", + service->name, + row[5], + dbnm); } } if(havedb && wildcard_db_grant(dbnm) && service->optimize_wildcard) { rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); + MXS_INFO("%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc); } else { rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL); } - LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG,"%s: Adding user:%s host:%s anydb:%s db:%s.", - service->name,row[0],row[1],row[4], - havedb ? dbnm : NULL))); + MXS_DEBUG("%s: Adding user:%s host:%s anydb:%s db:%s.", + service->name,row[0],row[1],row[4], + havedb ? dbnm : NULL); } else { /* we don't have dbgrants, simply set ANY DB for the user */ rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL); @@ -1023,22 +1069,17 @@ getAllUsers(SERVICE *service, USERS *users) strcpy(dbgrant, "no db"); /* Log the user being added with its db grants */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG|LOGFILE_TRACE, - "%s: User %s@%s for database %s added to " - "service user table.", - service->name, - row[0], - row[1], - dbgrant))); + MXS_INFO("%s: User %s@%s for database %s added to service user table.", + service->name, + row[0], + row[1], + dbgrant); } else { /* Log the user being added (without db grants) */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG|LOGFILE_TRACE, - "%s: User %s@%s added to service user table.", - service->name, - row[0], - row[1]))); + MXS_INFO("%s: User %s@%s added to service user table.", + service->name, + row[0], + row[1]); } /* Append data in the memory area for SHA1 digest */ @@ -1048,19 +1089,21 @@ getAllUsers(SERVICE *service, USERS *users) } else if(rc == -1) { /** Duplicate user*/ - LOGIF(LT,(skygw_log_write(LT, - "Duplicate MySQL user found for service [%s]: %s@%s%s%s", - service->name, - row[0],row[1],havedb?" for database: ":"", - havedb ?dbnm:""))); + if (service->log_auth_warnings) + { + MXS_NOTICE("Duplicate MySQL user found for service" + " [%s]: %s@%s%s%s", service->name, row[0], + row[1], havedb ? " for database: " : "", + havedb ? dbnm : ""); + } } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR|LOGFILE_TRACE, - "Warning: Failed to add user %s@%s for service [%s]. " - "This user will be unavailable via MaxScale.", - row[0], - row[1], - service->name))); + if (service->log_auth_warnings) + { + MXS_NOTICE("Warning: Failed to add user %s@%s" + " for service [%s]. This user will be " + "unavailable via MaxScale.", row[0], + row[1], service->name); + } } } @@ -1125,7 +1168,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; @@ -1153,30 +1196,22 @@ getUsers(SERVICE *service, USERS *users) con = mysql_init(NULL); if (con == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : mysql_init: %s", - mysql_error(con)))); + MXS_ERROR("mysql_init: %s", mysql_error(con)); return -1; } /** Set read, write and connect timeout values */ if (gw_mysql_set_timeouts(con)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set timeout values for backend " - "connection."))); - mysql_close(con); - return -1; + MXS_ERROR("Failed to set timeout values for backend connection."); + mysql_close(con); + return -1; } if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set external connection. " - "It is needed for backend server connections."))); - mysql_close(con); - return -1; + MXS_ERROR("Failed to set external connection. " + "It is needed for backend server connections."); + mysql_close(con); + return -1; } /** * Attempt to connect to one of the databases database or until we run @@ -1207,13 +1242,11 @@ getUsers(SERVICE *service, USERS *users) server->server->port, NULL, 0) != NULL)) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Dbusers : Loading data from backend database with " - "Master role [%s:%i] for service [%s]", - server->server->name, - server->server->port, - service->name))); + MXS_DEBUG("Dbusers : Loading data from backend database with " + "Master role [%s:%i] for service [%s]", + server->server->name, + server->server->port, + service->name); } else { /* load data from other servers via loop */ server = service->dbref; @@ -1229,15 +1262,13 @@ getUsers(SERVICE *service, USERS *users) NULL, 0) == NULL)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failure loading users data from backend " - "[%s:%i] for service [%s]. MySQL error %i, %s", - server->server->name, - server->server->port, - service->name, - mysql_errno(con), - mysql_error(con)))); + MXS_ERROR("Failure loading users data from backend " + "[%s:%i] for service [%s]. MySQL error %i, %s", + server->server->name, + server->server->port, + service->name, + mysql_errno(con), + mysql_error(con)); server = server->next; } @@ -1250,13 +1281,11 @@ getUsers(SERVICE *service, USERS *users) } if (server != NULL) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Dbusers : Loading data from backend database " - "[%s:%i] for service [%s]", - server->server->name, - server->server->port, - service->name))); + MXS_DEBUG("Loading data from backend database " + "[%s:%i] for service [%s]", + server->server->name, + server->server->port, + service->name); } } @@ -1264,41 +1293,44 @@ getUsers(SERVICE *service, USERS *users) if (server == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to get user data from backend database " - "for service [%s]. Failed to connect to any of the backend databases.", - service->name))); - mysql_close(con); - return -1; - } + MXS_ERROR("Unable to get user data from backend database " + "for service [%s]. Failed to connect to any of the backend databases.", + 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( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); - mysql_close(con); - return -1; + /* This is an error we cannot handle, return */ + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); + mysql_close(con); + return -1; } else { /* * We have got ER_TABLEACCESS_DENIED_ERROR * try counting users from mysql.user without DB names. */ if (mysql_query(con, MYSQL_USERS_COUNT)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); - mysql_close(con); - return -1; + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); + mysql_close(con); + return -1; } } } @@ -1306,14 +1338,11 @@ getUsers(SERVICE *service, USERS *users) result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s].", - service->name, - mysql_error(con)))); - mysql_close(con); - return -1; + MXS_ERROR("Loading users for service [%s] encountered error: [%s].", + service->name, + mysql_error(con)); + mysql_close(con); + return -1; } row = mysql_fetch_row(result); @@ -1323,21 +1352,13 @@ getUsers(SERVICE *service, USERS *users) mysql_free_result(result); if (!nusers) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Counting users for service %s returned 0", - service->name))); - mysql_close(con); - 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; + MXS_ERROR("Counting users for service %s returned 0.", service->name); + mysql_close(con); + return -1; } + 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)) { /* @@ -1349,27 +1370,21 @@ getUsers(SERVICE *service, USERS *users) if (1142 != mysql_errno(con)) { /* This is an error we cannot handle, return */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users with dbnames for service [%s] encountered " - "error: [%s], MySQL errno %i", - service->name, - mysql_error(con), - mysql_errno(con)))); + MXS_ERROR("Loading users with dbnames for service [%s] encountered " + "error: [%s], MySQL errno %i", + service->name, + mysql_error(con), + mysql_errno(con)); - mysql_close(con); - - return -1; + mysql_close(con); + return -1; } else { /* * We have got ER_TABLEACCESS_DENIED_ERROR * try loading users from mysql.user without DB names. */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - ERROR_NO_SHOW_DATABASES, - service->name, service_user))); + MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user); /* check for root user select */ if(service->enable_root) { @@ -1379,27 +1394,22 @@ getUsers(SERVICE *service, USERS *users) } if (mysql_query(con, users_query)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service [%s] encountered " - "error: [%s], code %i", - service->name, - mysql_error(con), - mysql_errno(con)))); + MXS_ERROR("Loading users for service [%s] encountered " + "error: [%s], code %i", + service->name, + mysql_error(con), + mysql_errno(con)); - mysql_close(con); - - return -1; + mysql_close(con); + return -1; } /* users successfully loaded but without db grants */ - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Loading users from [mysql.user] without access to [mysql.db] for " - "service [%s]. MaxScale Authentication with DBname on connect " - "will not consider database grants.", - service->name))); + MXS_NOTICE("Loading users from [mysql.user] without access to [mysql.db] for " + "service [%s]. MaxScale Authentication with DBname on connect " + "will not consider database grants.", + service->name); } } else { /* @@ -1412,32 +1422,24 @@ getUsers(SERVICE *service, USERS *users) result = mysql_store_result(con); if (result == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Loading users for service %s encountered " - "error: %s.", - service->name, - mysql_error(con)))); + MXS_ERROR("Loading users for service %s encountered error: %s.", + service->name, + mysql_error(con)); - mysql_free_result(result); - mysql_close(con); - - return -1; + mysql_free_result(result); + mysql_close(con); + return -1; } users_data = (char *)calloc(nusers, (users_data_row_len * sizeof(char)) + 1); if (users_data == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation for user data failed due to " - "%d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation for user data failed due to %d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); mysql_free_result(result); mysql_close(con); - return -1; } @@ -1445,11 +1447,9 @@ getUsers(SERVICE *service, USERS *users) /* load all mysql database names */ dbnames = getDatabases(service, con); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "Loaded %d MySQL Database Names for service [%s]", - dbnames, - service->name))); + MXS_DEBUG("Loaded %d MySQL Database Names for service [%s]", + dbnames, + service->name); } else { service->resources = NULL; } @@ -1477,17 +1477,15 @@ getUsers(SERVICE *service, USERS *users) if (row[2] != NULL) { /* detect mysql_old_password (pre 4.1 protocol) */ if (strlen(row[2]) == 16) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%s: The user %s@%s has on old password in the " - "backend database. MaxScale does not support these " - "old passwords. This user will not be able to connect " - "via MaxScale. Update the users password to correct " - "this.", - service->name, - row[0], - row[1]))); - continue; + MXS_ERROR("%s: The user %s@%s has on old password in the " + "backend database. MaxScale does not support these " + "old passwords. This user will not be able to connect " + "via MaxScale. Update the users password to correct " + "this.", + service->name, + row[0], + row[1]); + continue; } if (strlen(row[2]) > 1) @@ -1510,12 +1508,10 @@ getUsers(SERVICE *service, USERS *users) havedb = true; if(service->strip_db_esc) { strip_escape_chars(dbnm); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "[%s]: %s -> %s", - service->name, - row[5], - dbnm))); + MXS_DEBUG("[%s]: %s -> %s", + service->name, + row[5], + dbnm); } } @@ -1524,7 +1520,8 @@ getUsers(SERVICE *service, USERS *users) if(service->optimize_wildcard) { rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); - skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc); + MXS_INFO("%s: Converted '%s' to %d individual database grants.", + service->name, row[5], rc); } else { @@ -1558,22 +1555,18 @@ getUsers(SERVICE *service, USERS *users) strcpy(dbgrant, "no db"); /* Log the user being added with its db grants */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG|LOGFILE_TRACE, - "%s: User %s@%s for database %s added to " - "service user table.", - service->name, - row[0], - row[1], - dbgrant))); + MXS_INFO("%s: User %s@%s for database %s added to " + "service user table.", + service->name, + row[0], + row[1], + dbgrant); } else { - /* Log the user being added (without db grants) */ - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG|LOGFILE_TRACE, - "%s: User %s@%s added to service user table.", - service->name, - row[0], - row[1]))); + /* Log the user being added (without db grants) */ + MXS_INFO("%s: User %s@%s added to service user table.", + service->name, + row[0], + row[1]); } /* Append data in the memory area for SHA1 digest */ @@ -1583,19 +1576,20 @@ getUsers(SERVICE *service, USERS *users) } else if(rc == -1) { /** Duplicate user*/ - LOGIF(LE,(skygw_log_write(LT|LE, - "Warning: Duplicate MySQL user found for service [%s]: %s@%s%s%s", - service->name, - row[0],row[1],db_grants?" for database: ":"", - db_grants ?row[5]:""))); + if (service->log_auth_warnings) + { + MXS_WARNING("Duplicate MySQL user found for " + "service [%s]: %s@%s%s%s", service->name, row[0], + row[1], db_grants ? " for database: " : "", + db_grants ? row[5] : ""); + } } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR|LOGFILE_TRACE, - "Warning: Failed to add user %s@%s for service [%s]. " - "This user will be unavailable via MaxScale.", - row[0], - row[1], - service->name))); + if (service->log_auth_warnings) + { + MXS_WARNING("Failed to add user %s@%s for" + " service [%s]. This user will be unavailable" + " via MaxScale.", row[0], row[1], service->name); + } } } @@ -1719,7 +1713,17 @@ static int uh_cmpfun( void* v1, void* v2) { if (hu1->user == NULL || hu2->user == NULL) return 0; - if (strcmp(hu1->user, hu2->user) == 0 && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr) && (hu1->netmask >= hu2->netmask)) { + /** If the stored user has the unmodified address stored, that means we were not able + * to resolve it at the time we loaded the users. We need to check if the + * address contains wildcards and if the user's address matches that. */ + + const bool wildcard_host = strlen(hu2->hostname) > 0 && strlen(hu1->hostname) > 0; + + if ((strcmp(hu1->user, hu2->user) == 0) && + /** Check for wildcard hostnames */ + ((wildcard_host && host_matches_singlechar_wildcard(hu1->hostname, hu2->hostname)) || + /** If no wildcard hostname is stored, check for network address. */ + (!wildcard_host && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr) && (hu1->netmask >= hu2->netmask)))) { /* if no database name was passed, auth is ok */ if (hu1->resource == NULL || (hu1->resource && !strlen(hu1->resource))) { @@ -1806,11 +1810,16 @@ static void *uh_keydup(void* key) { return NULL; } + ss_dassert(strnlen(rval->hostname, MYSQL_HOST_MAXLEN + 1) <= MYSQL_HOST_MAXLEN); + strncpy(rval->hostname, current_key->hostname, MYSQL_HOST_MAXLEN); + rval->hostname[MYSQL_HOST_MAXLEN] = '\0'; + memcpy(&rval->ipv4, ¤t_key->ipv4, sizeof(struct sockaddr_in)); memcpy(&rval->netmask, ¤t_key->netmask, sizeof(int)); if (current_key->resource) rval->resource = strdup(current_key->resource); + else rval->resource = NULL; return (void *) rval; } @@ -2047,33 +2056,24 @@ static int gw_mysql_set_timeouts(MYSQL* handle) MYSQL_OPT_READ_TIMEOUT, (void *)&cnf->auth_read_timeout))) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set read timeout for backend " - "connection."))); - goto retblock; + MXS_ERROR("Failed to set read timeout for backend connection."); + goto retblock; } if ((rc = mysql_options(handle, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&cnf->auth_conn_timeout))) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set connect timeout for backend " - "connection."))); - goto retblock; + MXS_ERROR("Failed to set connect timeout for backend connection."); + goto retblock; } if ((rc = mysql_options(handle, MYSQL_OPT_WRITE_TIMEOUT, (void *)&cnf->auth_write_timeout))) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : failed to set write timeout for backend " - "connection."))); - goto retblock; + MXS_ERROR("Failed to set write timeout for backend connection."); + goto retblock; } retblock: @@ -2331,9 +2331,8 @@ int add_wildcard_users(USERS *users, char* name, char* host, char* password, cha if((err = regcomp(&re,restr,REG_ICASE|REG_NOSUB))) { regerror(err,&re,errbuf,1024); - skygw_log_write(LOGFILE_ERROR,"Error: Failed to compile regex " - "when resolving wildcard database grants: %s", - errbuf); + MXS_ERROR("Failed to compile regex when resolving wildcard database grants: %s", + errbuf); free(restr); return 0; } @@ -2378,7 +2377,7 @@ bool check_service_permissions(SERVICE* service) if(service->dbref == NULL) { - skygw_log_write(LE,"%s: Error: Service is missing the servers parameter.",service->name); + MXS_ERROR("%s: Service is missing the servers parameter.", service->name); return false; } @@ -2386,8 +2385,7 @@ bool check_service_permissions(SERVICE* service) if (serviceGetUser(service, &user, &password) == 0) { - skygw_log_write(LE, - "%s: Error: Service is missing the user credentials for authentication.", + MXS_ERROR("%s: Service is missing the user credentials for authentication.", service->name); return false; } @@ -2396,7 +2394,7 @@ bool check_service_permissions(SERVICE* service) if((mysql = mysql_init(NULL)) == NULL) { - skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + MXS_ERROR("[%s] MySQL connection initialization failed.", __FUNCTION__); free(dpasswd); return false; } @@ -2410,14 +2408,14 @@ bool check_service_permissions(SERVICE* service) { int my_errno = mysql_errno(mysql); - skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" - " checking authentication user credentials and permissions: %d %s", - service->name, - server->server->unique_name, - server->server->name, - server->server->port, - my_errno, - mysql_error(mysql)); + MXS_ERROR("%s: Failed to connect to server %s(%s:%d) when" + " checking authentication user credentials and permissions: %d %s", + service->name, + server->server->unique_name, + server->server->name, + server->server->port, + my_errno, + mysql_error(mysql)); mysql_close(mysql); free(dpasswd); @@ -2429,25 +2427,25 @@ bool check_service_permissions(SERVICE* service) { if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) { - skygw_log_write(LE,"%s: Error: User '%s' is missing SELECT privileges" - " on mysql.user table. MySQL error message: %s", - service->name,user,mysql_error(mysql)); + MXS_ERROR("%s: User '%s' is missing SELECT privileges" + " on mysql.user table. MySQL error message: %s", + service->name,user,mysql_error(mysql)); rval = false; } else { - skygw_log_write(LE,"%s: Error: Failed to query from mysql.user table." - " MySQL error message: %s", - service->name,mysql_error(mysql)); + MXS_ERROR("%s: Error: Failed to query from mysql.user table." + " MySQL error message: %s", + service->name,mysql_error(mysql)); } } else { if((res = mysql_use_result(mysql)) == NULL) { - skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for" - " permissions to the mysql.user table: %s", - service->name,mysql_error(mysql)); + MXS_ERROR("%s: Error: Result retrieval failed when checking for" + " permissions to the mysql.user table: %s", + service->name,mysql_error(mysql)); mysql_close(mysql); free(dpasswd); return true; @@ -2458,21 +2456,22 @@ bool check_service_permissions(SERVICE* service) { if(mysql_errno(mysql) == ER_TABLEACCESS_DENIED_ERROR) { - skygw_log_write(LE,"%s: Warning: User '%s' is missing SELECT privileges on mysql.db table. Database name will be ignored in authentication. MySQL error message: %s", - service->name,user,mysql_error(mysql)); + MXS_WARNING("%s: User '%s' is missing SELECT privileges on mysql.db table. " + "Database name will be ignored in authentication. MySQL error message: %s", + service->name,user,mysql_error(mysql)); } else { - skygw_log_write(LE,"%s: Error: Failed to query from mysql.db table. MySQL error message: %s", - service->name,mysql_error(mysql)); + MXS_ERROR("%s: Failed to query from mysql.db table. MySQL error message: %s", + service->name,mysql_error(mysql)); } } else { if((res = mysql_use_result(mysql)) == NULL) { - skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for permissions to the mysql.db table: %s", - service->name,mysql_error(mysql)); + MXS_ERROR("%s: Result retrieval failed when checking for permissions to the mysql.db table: %s", + service->name,mysql_error(mysql)); } else { diff --git a/server/core/dcb.c b/server/core/dcb.c index 19d95a177..b4a35012a 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -55,6 +55,10 @@ * fixes for various error situations, * remove dcb_set_state etc, simplifications. * 10/07/2015 Martin Brampton Simplify, merge dcb_read and dcb_read_n + * 04/09/2015 Martin Brampton Changes to ensure DCB always has session pointer + * 28/09/2015 Martin Brampton Add counters, maxima for DCBs and zombies + * 29/05/2015 Martin Brampton Impose locking in dcb_call_foreach callbacks + * 17/10/2015 Martin Brampton Add hangup for each and bitmask display MaxAdmin * * @endverbatim */ @@ -82,13 +86,12 @@ #define SSL_ERRBUF_LEN 140 -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static DCB *allDCBs = NULL; /* Diagnostics need a list of DCBs */ +static int nDCBs = 0; +static int maxDCBs = 0; static DCB *zombies = NULL; +static int nzombies = 0; +static int maxzombies = 0; static SPINLOCK dcbspin = SPINLOCK_INIT; static SPINLOCK zombiespin = SPINLOCK_INIT; @@ -101,7 +104,7 @@ static int dcb_null_auth(DCB *dcb, SERVER *server, SESSION *session, GWBUF *buf static inline int dcb_isvalid_nolock(DCB *dcb); static inline DCB * dcb_find_in_list(DCB *dcb); static inline void dcb_process_victim_queue(DCB *listofdcb); -static void dcb_close_finish(DCB *); +static void dcb_stop_polling_and_shutdown (DCB *dcb); static bool dcb_maybe_add_persistent(DCB *); static inline bool dcb_write_parameter_check(DCB *dcb, GWBUF *queue); #if defined(FAKE_CODE) @@ -228,29 +231,23 @@ DCB *newdcb; ptr = ptr->next; ptr->next = newdcb; } + nDCBs++; + if (nDCBs > maxDCBs) maxDCBs = nDCBs; spinlock_release(&dcbspin); return newdcb; } /** - * Free a DCB that has not been associated with a descriptor. + * Provided only for consistency, simply calls dcb_close to guarantee + * safe disposal of a DCB * * @param dcb The DCB to free */ void dcb_free(DCB *dcb) { - if (dcb->fd != DCBFD_CLOSED) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Attempt to free a DCB via dcb_free " - "that has been associated with a descriptor."))); - } - raise(SIGABRT); - /* Another statement to avoid a compiler warning */ - dcb_final_free(dcb); + dcb_close(dcb); } /* @@ -300,18 +297,15 @@ dcb_final_free(DCB *dcb) { DCB_CALLBACK *cb; - CHK_DCB(dcb); - ss_info_dassert(dcb->state == DCB_STATE_DISCONNECTED || - dcb->state == DCB_STATE_ALLOC, - "dcb not in DCB_STATE_DISCONNECTED not in DCB_STATE_ALLOC state."); + CHK_DCB(dcb); + ss_info_dassert(dcb->state == DCB_STATE_DISCONNECTED || + dcb->state == DCB_STATE_ALLOC, + "dcb not in DCB_STATE_DISCONNECTED not in DCB_STATE_ALLOC state."); if (DCB_POLL_BUSY(dcb)) { - /* Check if DCB has outstanding poll events */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "dcb_final_free: DCB %p has outstanding events", - dcb))); + /* Check if DCB has outstanding poll events */ + MXS_ERROR("dcb_final_free: DCB %p has outstanding events.", dcb); } /*< First remove this DCB from the chain */ @@ -337,55 +331,59 @@ dcb_final_free(DCB *dcb) if (ptr) ptr->next = dcb->next; } - spinlock_release(&dcbspin); + nDCBs--; + spinlock_release(&dcbspin); - if (dcb->session) { - /*< - * Terminate client session. - */ - { - SESSION *local_session = dcb->session; - dcb->session = NULL; - CHK_SESSION(local_session); - /** - * Set session's client pointer NULL so that other threads - * won't try to call dcb_close for client DCB - * after this call. - */ - if (local_session->client == dcb) - { - spinlock_acquire(&local_session->ses_lock); - local_session->client = NULL; - spinlock_release(&local_session->ses_lock); - } - session_free(local_session); - } + if (dcb->session) { + /*< + * Terminate client session. + */ + SESSION *local_session = dcb->session; + dcb->session = NULL; + CHK_SESSION(local_session); + /** + * Set session's client pointer NULL so that other threads + * won't try to call dcb_close for client DCB + * after this call. + */ + if (local_session->client == dcb) + { + spinlock_acquire(&local_session->ses_lock); + local_session->client = NULL; + spinlock_release(&local_session->ses_lock); + } + if (SESSION_STATE_DUMMY != local_session->state) + { + session_free(local_session); + } } if (dcb->protocol && (!DCB_IS_CLONE(dcb))) free(dcb->protocol); - if (dcb->protoname) - free(dcb->protoname); + if (dcb->protoname) + free(dcb->protoname); if (dcb->remote) free(dcb->remote); if (dcb->user) free(dcb->user); - /* Clear write and read buffers */ - if (dcb->delayq) { - GWBUF *queue = dcb->delayq; - while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); - } + /* Clear write and read buffers */ + if (dcb->delayq) { + GWBUF *queue = dcb->delayq; + while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); + dcb->delayq = NULL; + } if (dcb->writeq) { GWBUF *queue = dcb->writeq; while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); dcb->writeq = NULL; } - if (dcb->dcb_readqueue) - { - GWBUF* queue = dcb->dcb_readqueue; - while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); - } + if (dcb->dcb_readqueue) + { + GWBUF* queue = dcb->dcb_readqueue; + while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL); + dcb->dcb_readqueue = NULL; + } spinlock_acquire(&dcb->cb_lock); while ((cb = dcb->callbacks) != NULL) @@ -415,10 +413,9 @@ dcb_final_free(DCB *dcb) DCB * dcb_process_zombies(int threadid) { -DCB *zombiedcb, *previousdcb; +DCB *zombiedcb; +DCB *previousdcb = NULL, *nextdcb; DCB *listofdcb = NULL; -DCB *dcb = NULL; -bool succp = false; /** * Perform a dirty read to see if there is anything in the queue. @@ -428,7 +425,9 @@ bool succp = false; * dcb_final_free. */ if (!zombies) + { return NULL; + } /* * Process the zombie queue and create a list of DCB's that can be @@ -441,11 +440,10 @@ bool succp = false; */ spinlock_acquire(&zombiespin); zombiedcb = zombies; - previousdcb = NULL; while (zombiedcb) { CHK_DCB(zombiedcb); - + nextdcb = zombiedcb->memdata.next; /* * Skip processing of DCB's that are * in the event queue waiting to be processed. @@ -453,7 +451,6 @@ bool succp = false; if (zombiedcb->evq.next || zombiedcb->evq.prev) { previousdcb = zombiedcb; - zombiedcb = zombiedcb->memdata.next; } else { @@ -472,22 +469,23 @@ bool succp = false; * queue or NULL if the DCB is at the head of the * queue. Remove zombiedcb from the zombies list. */ - if (previousdcb == NULL) + if (NULL == previousdcb) + { zombies = zombiedcb->memdata.next; - else + } + else + { previousdcb->memdata.next = zombiedcb->memdata.next; + } - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [dcb_process_zombies] Remove dcb " - "%p fd %d in state %s from the " - "list of zombies.", - pthread_self(), - zombiedcb, - zombiedcb->fd, - STRDCBSTATE(zombiedcb->state)))); - ss_info_dassert(zombiedcb->state == DCB_STATE_ZOMBIE, - "dcb not in DCB_STATE_ZOMBIE state."); + MXS_DEBUG("%lu [%s] Remove dcb " + "%p fd %d in state %s from the " + "list of zombies.", + pthread_self(), + __func__, + zombiedcb, + zombiedcb->fd, + STRDCBSTATE(zombiedcb->state)); /*< * Move zombie dcb to linked list of victim dcbs. * The variable dcb is used to hold the last DCB @@ -496,33 +494,25 @@ bool succp = false; * (listofdcb) is not NULL, then it follows that * dcb will also not be null. */ - if (listofdcb == NULL) - { - listofdcb = zombiedcb; - } - else - { - dcb->memdata.next = zombiedcb; - } - /* Set dcb for next iteration of loop */ - dcb = zombiedcb; - zombiedcb = zombiedcb->memdata.next; - /* After we've moved zombiedcb forward, set - link to null as dcb is last of the new list */ - dcb->memdata.next = NULL; + nzombies--; + zombiedcb->memdata.next = listofdcb; + listofdcb = zombiedcb; } else { - /* Since we didn't remove this dcb from the zombies - list, we need to advance the previous pointer */ + /* Since we didn't remove this dcb from the zombies + list, we need to advance the previous pointer */ previousdcb = zombiedcb; - zombiedcb = zombiedcb->memdata.next; } } + zombiedcb = nextdcb; } spinlock_release(&zombiespin); - dcb_process_victim_queue(listofdcb); + if (listofdcb) + { + dcb_process_victim_queue(listofdcb); + } return zombies; } @@ -539,12 +529,62 @@ bool succp = false; static inline void dcb_process_victim_queue(DCB *listofdcb) { - DCB *dcb; + DCB *dcb = listofdcb; - dcb = listofdcb; while (dcb != NULL) { - DCB *nextdcb = NULL; + DCB *nextdcb; + /*< + * Stop dcb's listening and modify state accordingly. + */ + spinlock_acquire(&dcb->dcb_initlock); + if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_LISTENING) + { + if (dcb->state == DCB_STATE_LISTENING) + { + MXS_ERROR("%lu [%s] Error : Removing DCB %p but was in state %s " + "which is not expected for a call to dcb_close, although it" + "should be processed correctly. ", + pthread_self(), + __func__, + dcb, + STRDCBSTATE(dcb->state)); + } + else { + /* Must be DCB_STATE_POLLING */ + spinlock_release(&dcb->dcb_initlock); + if (0 == dcb->persistentstart && dcb_maybe_add_persistent(dcb)) + { + /* Have taken DCB into persistent pool, no further killing */ + dcb = dcb->memdata.next; + continue; + } + else + { + DCB *nextdcb; + dcb_stop_polling_and_shutdown(dcb); + spinlock_acquire(&zombiespin); + bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); + nextdcb = dcb->memdata.next; + dcb->memdata.next = zombies; + zombies = dcb; + nzombies++; + if (nzombies > maxzombies) maxzombies = nzombies; + spinlock_release(&zombiespin); + dcb = nextdcb; + continue; + } + } + } + /* + * Into the final close logic, so if DCB is for backend server, we + * must decrement the number of current connections. + */ + if (dcb->server && 0 == dcb->persistentstart) + { + atomic_add(&dcb->server->stats.n_current, -1); + } + if (dcb->fd > 0) { /*< @@ -555,44 +595,67 @@ dcb_process_victim_queue(DCB *listofdcb) int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [dcb_process_victim_queue] Error : Failed to close " - "socket %d on dcb %p due error %d, %s.", - pthread_self(), - dcb->fd, - dcb, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_ERROR("%lu [dcb_process_victim_queue] Error : Failed to close " + "socket %d on dcb %p due error %d, %s.", + pthread_self(), + dcb->fd, + dcb, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } else { dcb->fd = DCBFD_CLOSED; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [dcb_process_victim_queue] Closed socket " - "%d on dcb %p.", - pthread_self(), - dcb->fd, - dcb))); + MXS_DEBUG("%lu [dcb_process_victim_queue] Closed socket " + "%d on dcb %p.", + pthread_self(), + dcb->fd, + dcb); #if defined(FAKE_CODE) conn_open[dcb->fd] = false; #endif /* FAKE_CODE */ } } - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->state = DCB_STATE_DISCONNECTED; - nextdcb = dcb->memdata.next; - dcb_final_free(dcb); - dcb = nextdcb; + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + + dcb->state = DCB_STATE_DISCONNECTED; + nextdcb = dcb->memdata.next; + spinlock_release(&dcb->dcb_initlock); + dcb_final_free(dcb); + dcb = nextdcb; } /** Reset threads session data */ - LOGIF(LT, tls_log_info.li_sesid = 0); + if (LOG_IS_ENABLED(LOGFILE_TRACE)) + { + tls_log_info.li_sesid = 0; + } +} + +/** + * Remove a DCB from the poll list and trigger shutdown mechanisms. + * + * @param dcb The DCB to be processed + */ +static void +dcb_stop_polling_and_shutdown (DCB *dcb) +{ + poll_remove_dcb(dcb); + /** + * close protocol and router session + */ + if (dcb->func.close != NULL) + { + dcb->func.close(dcb); + } + /** Call possible callback for this DCB in case of close */ + dcb_call_callback(dcb, DCB_REASON_CLOSE); } /** @@ -619,9 +682,8 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol) user = session_getUser(session); if (user && strlen(user)) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Looking for persistent connection DCB user %s protocol %s\n", pthread_self(), user, protocol))); + MXS_DEBUG("%lu [dcb_connect] Looking for persistent connection DCB " + "user %s protocol %s\n", pthread_self(), user, protocol); dcb = server_get_persistent(server, user, protocol); if (dcb) { @@ -630,26 +692,21 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol) */ if (!session_link_dcb(session, dcb)) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Failed to link to session, the " - "session has been removed.\n", - pthread_self()))); + MXS_DEBUG("%lu [dcb_connect] Failed to link to session, the " + "session has been removed.\n", + pthread_self()); dcb_close(dcb); return NULL; } - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Reusing a persistent connection, dcb %p\n", pthread_self(), dcb))); + MXS_DEBUG("%lu [dcb_connect] Reusing a persistent connection, dcb %p\n", + pthread_self(), dcb); dcb->persistentstart = 0; return dcb; } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Failed to find a reusable persistent connection.\n", - pthread_self()))); + MXS_DEBUG("%lu [dcb_connect] Failed to find a reusable persistent connection.\n", + pthread_self()); } } @@ -663,12 +720,9 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol) { dcb->state = DCB_STATE_DISCONNECTED; dcb_final_free(dcb); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to load protocol module for %s, free " - "dcb %p\n", - protocol, - dcb))); + MXS_ERROR("Failed to load protocol module for %s, free dcb %p\n", + protocol, + dcb); return NULL; } memcpy(&(dcb->func), funcs, sizeof(GWPROTOCOL)); @@ -679,43 +733,36 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol) */ if (!session_link_dcb(session, dcb)) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Failed to link to session, the " - "session has been removed.", - pthread_self()))); - dcb_final_free(dcb); - return NULL; + MXS_DEBUG("%lu [dcb_connect] Failed to link to session, the " + "session has been removed.", + pthread_self()); + dcb_final_free(dcb); + return NULL; } fd = dcb->func.connect(dcb, server, session); if (fd == DCBFD_CLOSED) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_connect] Failed to connect to server %s:%d, " - "from backend dcb %p, client dcp %p fd %d.", - pthread_self(), - server->name, - server->port, - dcb, - session->client, - session->client->fd))); - dcb->state = DCB_STATE_DISCONNECTED; - dcb_final_free(dcb); - return NULL; + MXS_DEBUG("%lu [dcb_connect] Failed to connect to server %s:%d, " + "from backend dcb %p, client dcp %p fd %d.", + pthread_self(), + server->name, + server->port, + dcb, + session->client, + session->client->fd); + dcb->state = DCB_STATE_DISCONNECTED; + dcb_final_free(dcb); + return NULL; } else { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [dcb_connect] Connected to server %s:%d, " - "from backend dcb %p, client dcp %p fd %d.", - pthread_self(), - server->name, - server->port, - dcb, - session->client, - session->client->fd))); + MXS_DEBUG("%lu [dcb_connect] Connected to server %s:%d, " + "from backend dcb %p, client dcp %p fd %d.", + pthread_self(), + server->name, + server->port, + dcb, + session->client, + session->client->fd); } - ss_dassert(dcb->fd == DCBFD_CLOSED); /*< must be uninitialized at this point */ /** * Successfully connected to backend. Assign file descriptor to dcb */ @@ -784,11 +831,9 @@ int dcb_read( if (dcb->fd <= 0) { /* */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [dcb_read] Error : Read failed, dcb is %s.", - pthread_self(), - dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"))); + MXS_ERROR("%lu [dcb_read] Error : Read failed, dcb is %s.", + pthread_self(), + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"); /* */ return 0; } @@ -801,16 +846,14 @@ int dcb_read( { char errbuf[STRERROR_BUFLEN]; /* */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [dcb_read] Error : ioctl FIONREAD for dcb %p in " - "state %s fd %d failed due error %d, %s.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("%lu [dcb_read] Error : ioctl FIONREAD for dcb %p in " + "state %s fd %d failed due error %d, %s.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); /* */ return -1; } @@ -852,15 +895,13 @@ int dcb_read( */ char errbuf[STRERROR_BUFLEN]; /* */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [dcb_read] Error : Failed to allocate read buffer " - "for dcb %p fd %d, due %d, %s.", - pthread_self(), - dcb, - dcb->fd, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("%lu [dcb_read] Error : Failed to allocate read buffer " + "for dcb %p fd %d, due %d, %s.", + pthread_self(), + dcb, + dcb->fd, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); /* */ return -1; } @@ -873,16 +914,14 @@ int dcb_read( { char errbuf[STRERROR_BUFLEN]; /* */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [dcb_read] Error : Read failed, dcb %p in state " - "%s fd %d, due %d, %s.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("%lu [dcb_read] Error : Read failed, dcb %p in state " + "%s fd %d, due %d, %s.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); /* */ } gwbuf_free(buffer); @@ -890,15 +929,13 @@ int dcb_read( } nreadtotal += nsingleread; /* */ - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_read] Read %d bytes from dcb %p in state %s " - "fd %d.", - pthread_self(), - nsingleread, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_read] Read %d bytes from dcb %p in state %s " + "fd %d.", + pthread_self(), + nsingleread, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); /* */ /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); @@ -927,9 +964,8 @@ int dcb_read_SSL(DCB *dcb, if (dcb->fd <= 0) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Read failed, dcb is %s.", - dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"))); + MXS_ERROR("Read failed, dcb is %s.", + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable"); return 0; } @@ -945,13 +981,12 @@ int dcb_read_SSL(DCB *dcb, * Todo shutdown if memory allocation fails. */ char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Failed to allocate read buffer " - "for dcb %p fd %d, due %d, %s.", - dcb, - dcb->fd, - errno, - strerror_r(errno, errbuf, sizeof (errbuf))))); + MXS_ERROR("Failed to allocate read buffer " + "for dcb %p fd %d, due %d, %s.", + dcb, + dcb->fd, + errno, + strerror_r(errno, errbuf, sizeof (errbuf))); return -1; } @@ -979,13 +1014,13 @@ int dcb_read_SSL(DCB *dcb, if (buffer) { #ifdef SS_DEBUG - skygw_log_write(LD, "%lu SSL: Truncated buffer from %d to %d bytes. " - "Read %d bytes, %d bytes waiting.\n", pthread_self(), - bufsize, GWBUF_LENGTH(buffer), n, b); + MXS_DEBUG("%lu SSL: Truncated buffer from %d to %ld bytes. " + "Read %d bytes, %d bytes waiting.\n", pthread_self(), + bufsize, GWBUF_LENGTH(buffer), n, b); 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."); @@ -993,14 +1028,13 @@ int dcb_read_SSL(DCB *dcb, #endif nread += n; - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [dcb_read_SSL] Read %d bytes from dcb %p in state %s " - "fd %d.", - pthread_self(), - n, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_read_SSL] Read %d bytes from dcb %p in state %s " + "fd %d.", + pthread_self(), + n, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); /*< Append read data to the gwbuf */ *head = gwbuf_append(*head, buffer); @@ -1008,12 +1042,12 @@ int dcb_read_SSL(DCB *dcb, } ss_dassert(gwbuf_length(*head) == nread); - LOGIF(LD, skygw_log_write(LD, "%lu Read a total of %d bytes from dcb %p in state %s fd %d.", - pthread_self(), - nread, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd)); + MXS_DEBUG("%lu Read a total of %d bytes from dcb %p in state %s fd %d.", + pthread_self(), + nread, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); return nread; } @@ -1083,15 +1117,13 @@ int below_water; * queue with have. */ queue = gwbuf_consume(queue, written); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Wrote %d Bytes to dcb %p in " - "state %s fd %d", - pthread_self(), - written, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Wrote %d Bytes to dcb %p in " + "state %s fd %d", + pthread_self(), + written, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); } /*< while (queue != NULL) */ } /* if (dcb->writeq) */ @@ -1145,10 +1177,8 @@ dcb_write_parameter_check(DCB *dcb, GWBUF *queue) if (dcb->fd <= 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write failed, dcb is %s.", - dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not writable"))); + MXS_ERROR("Write failed, dcb is %s.", + dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not writable"); gwbuf_free(queue); return false; } @@ -1171,15 +1201,11 @@ dcb_write_parameter_check(DCB *dcb, GWBUF *queue) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write aborted to dcb %p because " - "it is in state %s", - pthread_self(), - dcb->stats.n_buffered, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Write aborted to dcb %p because " + "it is in state %s", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); gwbuf_free(queue); return false; } @@ -1208,15 +1234,13 @@ dcb_write_when_already_queued(DCB *dcb, GWBUF *queue) atomic_add(&dcb->writeqlen, gwbuf_length(queue)); dcb->writeq = gwbuf_append(dcb->writeq, queue); dcb->stats.n_buffered++; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Append to writequeue. %d writes " - "buffered for dcb %p in state %s fd %d", - pthread_self(), - dcb->stats.n_buffered, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Append to writequeue. %d writes " + "buffered for dcb %p in state %s fd %d", + pthread_self(), + dcb->stats.n_buffered, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); } /** @@ -1234,17 +1258,15 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) if (eno == EPIPE) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due errno %d, %s", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due errno %d, %s", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } } @@ -1255,16 +1277,14 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) eno != EWOULDBLOCK) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p in " - "state %s fd %d failed due " - "errno %d, %s", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Write to dcb %p in " + "state %s fd %d failed due " + "errno %d, %s", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } @@ -1291,13 +1311,11 @@ dcb_log_write_failure(DCB *dcb, GWBUF *queue, int eno) if (dolog) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Writing to %s socket failed due %d, %s.", - pthread_self(), - dcb_isclient(dcb) ? "client" : "backend server", - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [dcb_write] Writing to %s socket failed due %d, %s.", + pthread_self(), + dcb_isclient(dcb) ? "client" : "backend server", + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } } } @@ -1360,41 +1378,38 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue) #endif /* FAKE_CODE */ do { - w = gw_write_SSL (dcb->ssl, GWBUF_DATA (queue), GWBUF_LENGTH (queue)); + w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), GWBUF_LENGTH(queue)); dcb->stats.n_writes++; if (w <= 0) { - int ssl_errno = SSL_get_error (dcb->ssl, w); - dcb_write_SSL_error_report (dcb, w, ssl_errno); + int ssl_errno = SSL_get_error(dcb->ssl, w); + dcb_write_SSL_error_report(dcb, w, ssl_errno); if (ssl_errno != SSL_ERROR_WANT_WRITE) { - atomic_add (&dcb->writeqlen, gwbuf_length (queue)); + atomic_add(&dcb->writeqlen, gwbuf_length(queue)); dcb->stats.n_buffered++; - dcb_write_tidy_up (dcb, below_water); + dcb_write_tidy_up(dcb, below_water); return 1; } #ifdef SS_DEBUG else { - skygw_log_write (LD, "SSL error: SSL_ERROR_WANT_WRITE, retrying SSL_write..."); + MXS_DEBUG("SSL error: SSL_ERROR_WANT_WRITE, retrying SSL_write..."); } #endif } - } - while(w <= 0); + } while(w <= 0); /** Remove written bytes from the queue */ queue = gwbuf_consume(queue, w); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Wrote %d Bytes to dcb %p in " - "state %s fd %d", - pthread_self(), - w, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Wrote %d Bytes to dcb %p in " + "state %s fd %d", + pthread_self(), + w, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); } /*< while (queue != NULL) */ /*< * What wasn't successfully written is stored to write queue @@ -1426,37 +1441,31 @@ dcb_write_SSL_error_report (DCB *dcb, int ret, int ssl_errno) switch(ssl_errno) { case SSL_ERROR_WANT_READ: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_READ", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_READ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); break; case SSL_ERROR_WANT_WRITE: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error SSL_ERROR_WANT_WRITE", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error SSL_ERROR_WANT_WRITE", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); break; default: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_write] Write to dcb " - "%p in state %s fd %d failed " - "due error %d", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - dcb->fd,ssl_errno))); + MXS_DEBUG("%lu [dcb_write] Write to dcb " + "%p in state %s fd %d failed " + "due error %d", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + dcb->fd,ssl_errno); break; } } @@ -1465,26 +1474,24 @@ dcb_write_SSL_error_report (DCB *dcb, int ret, int ssl_errno) { if (ret == -1) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p in " - "state %s fd %d failed due to " - "SSL error %d", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ssl_errno))); + MXS_ERROR("Write to dcb %p in " + "state %s fd %d failed due to " + "SSL error %d", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno); if(ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL) { if(ssl_errno == SSL_ERROR_SYSCALL) { - skygw_log_write(LE,"%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); } do { char errbuf[SSL_ERRBUF_LEN]; ERR_error_string_n(ssl_errno,errbuf, sizeof(errbuf)); - skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); + MXS_ERROR("%d:%s", ssl_errno,errbuf); } while((ssl_errno = ERR_get_error()) != 0); } } @@ -1494,7 +1501,7 @@ dcb_write_SSL_error_report (DCB *dcb, int ret, int ssl_errno) { char errbuf[SSL_ERRBUF_LEN]; ERR_error_string_n(ssl_errno,errbuf,sizeof(errbuf)); - skygw_log_write(LE,"%d:%s",ssl_errno,errbuf); + MXS_ERROR("%d:%s", ssl_errno,errbuf); } while((ssl_errno = ERR_get_error()) != 0); } } @@ -1549,15 +1556,13 @@ int above_water; break; } char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Write to dcb %p " - "in state %s fd %d failed due errno %d, %s", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - saved_errno, - strerror_r(saved_errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Write to dcb %p " + "in state %s fd %d failed due errno %d, %s", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + saved_errno, + strerror_r(saved_errno, errbuf, sizeof(errbuf))); break; } /* @@ -1565,15 +1570,13 @@ int above_water; * queue with have. */ dcb->writeq = gwbuf_consume(dcb->writeq, w); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_drain_writeq] Wrote %d Bytes to dcb %p " - "in state %s fd %d", - pthread_self(), - w, - dcb, - STRDCBSTATE(dcb->state), - dcb->fd))); + MXS_DEBUG("%lu [dcb_drain_writeq] Wrote %d Bytes to dcb %p " + "in state %s fd %d", + pthread_self(), + w, + dcb, + STRDCBSTATE(dcb->state), + dcb->fd); n += w; } } @@ -1635,13 +1638,7 @@ dcb_drain_writeq_SSL(DCB *dcb) { break; } - skygw_log_write_flush(LOGFILE_ERROR, - "Error : Write to dcb failed due to " - "SSL error %d:", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ssl_errno); + MXS_ERROR("Write to dcb failed due to SSL error %d:", ssl_errno); switch(ssl_errno) { case SSL_ERROR_SSL: @@ -1650,20 +1647,20 @@ dcb_drain_writeq_SSL(DCB *dcb) { char errbuf[SSL_ERRBUF_LEN]; ERR_error_string_n(ssl_errno,errbuf,sizeof(errbuf)); - skygw_log_write(LE,"%s",errbuf); + MXS_ERROR("%s", errbuf); } if(errno != 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE,"%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("%d:%s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); } break; case SSL_ERROR_ZERO_RETURN: - skygw_log_write(LE,"Socket is closed."); + MXS_ERROR("Socket is closed."); break; default: - skygw_log_write(LE,"Unexpected error."); + MXS_ERROR("Unexpected error."); break; } break; @@ -1710,28 +1707,15 @@ dcb_close(DCB *dcb) { CHK_DCB(dcb); - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [dcb_close] DCB %p in state %s", - pthread_self(), - dcb, - dcb ? STRDCBSTATE(dcb->state) : "Invalid DCB"))); - - if (DCB_STATE_ZOMBIE == dcb->state) - { - return; - } - if (DCB_STATE_UNDEFINED == dcb->state || DCB_STATE_DISCONNECTED == dcb->state) { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "%lu [dcb_close] Error : Removing DCB %p but was in state %s " - "which is not legal for a call to dcb_close. ", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - raise(SIGABRT); + MXS_ERROR("%lu [dcb_close] Error : Removing DCB %p but was in state %s " + "which is not legal for a call to dcb_close. ", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); + raise(SIGABRT); } /** @@ -1743,43 +1727,44 @@ dcb_close(DCB *dcb) dcb_final_free(dcb); return; } - - /*< - * Stop dcb's listening and modify state accordingly. - */ - if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_LISTENING) - { - if (dcb->state == DCB_STATE_LISTENING) - { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "%lu [dcb_close] Error : Removing DCB %p but was in state %s " - "which is not expected for a call to dcb_close, although it" - "should be processed correctly. ", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - } - if ((dcb->state == DCB_STATE_POLLING && !dcb_maybe_add_persistent(dcb)) - || (dcb->state == DCB_STATE_LISTENING)) - { - dcb_close_finish(dcb); - } - } - spinlock_acquire(&zombiespin); - if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ALLOC) + /* + * If DCB is in persistent pool, mark it as an error and exit + */ + if (dcb->persistentstart > 0) { + dcb->dcb_errhandle_called = true; + return; + } + + spinlock_acquire(&zombiespin); + if (!dcb->dcb_is_zombie) + { + if (0 == dcb->persistentstart && dcb->server && DCB_STATE_POLLING == dcb->state) + { + /* May be a candidate for persistence, so save user name */ + char *user; + user = session_getUser(dcb->session); + if (user && strlen(user) && !dcb->user) + { + dcb->user = strdup(user); + } + } /*< - * Add closing dcb to the top of the list. + * Add closing dcb to the top of the list, setting zombie marker */ + dcb->dcb_is_zombie = true; dcb->memdata.next = zombies; zombies = dcb; - /*< - * Set state which indicates that it has been added to zombies - * list. - */ - dcb->state = DCB_STATE_ZOMBIE; + nzombies++; + if (nzombies > maxzombies) maxzombies = nzombies; + /*< Set bit for each maxscale thread. This should be done before + * the state is changed, so as to protect the DCB from premature + * destruction. */ + if (dcb->server) + { + bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); + } } spinlock_release(&zombiespin); } @@ -1794,25 +1779,42 @@ dcb_close(DCB *dcb) static bool dcb_maybe_add_persistent(DCB *dcb) { - char *user; int poolcount = -1; - user = session_getUser(dcb->session); - if (user - && strlen(user) + if (dcb->user != NULL + && strlen(dcb->user) && dcb->server && dcb->server->persistpoolmax + && (dcb->server->status & SERVER_RUNNING) && !dcb->dcb_errhandle_called && !(dcb->flags & DCBF_HUNG) && (poolcount = dcb_persistent_clean_count(dcb, false)) < dcb->server->persistpoolmax) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_maybe_add_persistent] Adding DCB to persistent pool, user %s.\n", - pthread_self(), - user))); - dcb->user = strdup(user); + DCB_CALLBACK *loopcallback; + MXS_DEBUG("%lu [dcb_maybe_add_persistent] Adding DCB to persistent pool, user %s.\n", + pthread_self(), + dcb->user); + dcb->dcb_is_zombie = false; dcb->persistentstart = time(NULL); - session_unlink_dcb(dcb->session, dcb); + if (dcb->session) + /*< + * Terminate client session. + */ + { + SESSION *local_session = dcb->session; + session_set_dummy(dcb); + CHK_SESSION(local_session); + if (SESSION_STATE_DUMMY != local_session->state) + { + session_free(local_session); + } + } + spinlock_acquire(&dcb->cb_lock); + while ((loopcallback = dcb->callbacks) != NULL) + { + dcb->callbacks = loopcallback->next; + free(loopcallback); + } + spinlock_release(&dcb->cb_lock); spinlock_acquire(&dcb->server->persistlock); dcb->nextpersistent = dcb->server->persistent; dcb->server->persistent = dcb; @@ -1823,60 +1825,21 @@ dcb_maybe_add_persistent(DCB *dcb) } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_maybe_add_persistent] Not adding DCB %p to persistent pool, user %s, " - "max for pool %d, error handle called %s, hung flag %s, pool count %d.\n", - pthread_self(), - dcb, - user ? user : "", - (dcb->server && dcb->server->persistpoolmax) ? dcb->server->persistpoolmax : 0, - dcb->dcb_errhandle_called ? "true" : "false", - (dcb->flags & DCBF_HUNG) ? "true" : "false", - poolcount))); + MXS_DEBUG("%lu [dcb_maybe_add_persistent] Not adding DCB %p to persistent pool, " + "user %s, max for pool %ld, error handle called %s, hung flag %s, " + "server status %d, pool count %d.\n", + pthread_self(), + dcb, + dcb->user ? dcb->user : "", + (dcb->server && dcb->server->persistpoolmax) ? dcb->server->persistpoolmax : 0, + dcb->dcb_errhandle_called ? "true" : "false", + (dcb->flags & DCBF_HUNG) ? "true" : "false", + dcb->server ? dcb->server->status : 0, + poolcount); } return false; } -/** - * Final calls for DCB close - * - * @param dcb The DCB to print - * - */ -static void -dcb_close_finish(DCB *dcb) -{ - poll_remove_dcb(dcb); - /* - * Return will always be 0 or function will have crashed, so we - * threw away return value. - */ - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [dcb_close] Removed dcb %p in state %s from poll set.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - /** - * Do a consistency check, then adjust counter if not from persistent pool - */ - if (dcb->server) - { - if (dcb->server->persistent) CHK_DCB(dcb->server->persistent); - if (0 == dcb->persistentstart) atomic_add(&dcb->server->stats.n_current, -1); - } - /** - * close protocol and router session - */ - if (dcb->func.close != NULL) - { - dcb->func.close(dcb); - } - /** Call possible callback for this DCB in case of close */ - dcb_call_callback(dcb, DCB_REASON_CLOSE); - } - /** * Diagnostic to print a DCB * @@ -1972,6 +1935,15 @@ dprintOneDCB(DCB *pdcb, DCB *dcb) if (dcb->remote) dcb_printf(pdcb, "\tConnected to: %s\n", dcb->remote); + if (dcb->server) + { + if (dcb->server->name) + dcb_printf(pdcb, "\tServer name/IP: %s\n", + dcb->server->name); + if (dcb->server->port) + dcb_printf(pdcb, "\tPort number: %d\n", + dcb->server->port); + } if (dcb->user) dcb_printf(pdcb, "\tUsername: %s\n", dcb->user); @@ -1993,6 +1965,15 @@ dprintOneDCB(DCB *pdcb, DCB *dcb) dcb_printf(pdcb, "\tRole: %s\n", rolename); free(rolename); } + if (!bitmask_isallclear(&dcb->memdata.bitmask)) + { + char *bitmasktext = bitmask_render_readable(&dcb->memdata.bitmask); + if (bitmasktext) + { + dcb_printf(pdcb, "\tBitMask: %s\n", bitmasktext); + free(bitmasktext); + } + } dcb_printf(pdcb, "\tStatistics:\n"); dcb_printf(pdcb, "\t\tNo. of Reads: %d\n", dcb->stats.n_reads); dcb_printf(pdcb, "\t\tNo. of Writes: %d\n", dcb->stats.n_writes); @@ -2202,13 +2183,11 @@ gw_dcb_state2string (int state) case DCB_STATE_POLLING: return "DCB in the polling loop"; case DCB_STATE_NOPOLLING: - return "DCB not in the polling loop"; + return "DCB not in polling loop"; case DCB_STATE_LISTENING: return "DCB for listening socket"; case DCB_STATE_DISCONNECTED: return "DCB socket closed"; - case DCB_STATE_FREED: - return "DCB memory could be freed"; case DCB_STATE_ZOMBIE: return "DCB Zombie"; case DCB_STATE_UNDEFINED: @@ -2389,12 +2368,10 @@ gw_write(DCB *dcb, const void *buf, size_t nbytes) { snprintf(s, len, "%s", (char *)str); } - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "%lu [gw_write] Wrote %d bytes : %s ", - pthread_self(), - w, - s))); + MXS_INFO("%lu [gw_write] Wrote %d bytes : %s ", + pthread_self(), + w, + s); free(s); } } @@ -2531,10 +2508,9 @@ DCB_CALLBACK *cb, *nextcb; nextcb = cb->next; spinlock_release(&dcb->cb_lock); - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [dcb_call_callback] %s", - pthread_self(), - STRDCBREASON(reason)))); + MXS_DEBUG("%lu [dcb_call_callback] %s", + pthread_self(), + STRDCBREASON(reason)); cb->cb(dcb, reason, cb->userdata); spinlock_acquire(&dcb->cb_lock); @@ -2629,9 +2605,7 @@ dcb_get_next (DCB *dcb) void dcb_call_foreach(struct server* server, DCB_REASON reason) { - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [dcb_call_foreach]", - pthread_self()))); + MXS_DEBUG("%lu [dcb_call_foreach]", pthread_self()); switch (reason) { case DCB_REASON_CLOSE: @@ -2643,17 +2617,21 @@ dcb_call_foreach(struct server* server, DCB_REASON reason) case DCB_REASON_NOT_RESPONDING: { DCB *dcb; - dcb = dcb_get_next(NULL); - + spinlock_acquire(&dcbspin); + dcb = allDCBs; + while (dcb != NULL) { + spinlock_acquire(&dcb->dcb_initlock); if (dcb->state == DCB_STATE_POLLING && dcb->server && - strcmp(dcb->server->unique_name,server->unique_name) == 0) + strcmp(dcb->server->unique_name,server->unique_name) == 0) { dcb_call_callback(dcb, DCB_REASON_NOT_RESPONDING); } - dcb = dcb_get_next(dcb); + spinlock_release(&dcb->dcb_initlock); + dcb = dcb->next; } + spinlock_release(&dcbspin); break; } @@ -2663,6 +2641,34 @@ dcb_call_foreach(struct server* server, DCB_REASON reason) return; } +/** + * Call all the callbacks on all DCB's that match the server and the reason given + * + * @param reason The DCB_REASON that triggers the callback + */ +void +dcb_hangup_foreach(struct server* server) +{ + MXS_DEBUG("%lu [dcb_hangup_foreach]", pthread_self()); + + DCB *dcb; + spinlock_acquire(&dcbspin); + dcb = allDCBs; + + while (dcb != NULL) + { + spinlock_acquire(&dcb->dcb_initlock); + if (dcb->state == DCB_STATE_POLLING && dcb->server && + strcmp(dcb->server->unique_name,server->unique_name) == 0) + { + poll_fake_hangup_event(dcb); + } + spinlock_release(&dcb->dcb_initlock); + dcb = dcb->next; + } + spinlock_release(&dcbspin); +} + /** * Null protocol write routine used for cloned dcb's. It merely consumes @@ -2736,9 +2742,11 @@ dcb_persistent_clean_count(DCB *dcb, bool cleanall) CHK_DCB(persistentdcb); nextdcb = persistentdcb->nextpersistent; if (cleanall - || persistentdcb-> dcb_errhandle_called - || count >= server->persistpoolmax - || (time(NULL) - persistentdcb->persistentstart) > server->persistmaxtime) + || persistentdcb-> dcb_errhandle_called + || count >= server->persistpoolmax + || persistentdcb->server == NULL + || !(persistentdcb->server->status & SERVER_RUNNING) + || (time(NULL) - persistentdcb->persistentstart) > server->persistmaxtime) { /* Remove from persistent pool */ if (previousdcb) { @@ -2766,7 +2774,11 @@ dcb_persistent_clean_count(DCB *dcb, bool cleanall) while (disposals) { nextdcb = disposals->nextpersistent; - dcb_close_finish(disposals); + disposals->persistentstart = -1; + if (DCB_STATE_POLLING == disposals->state) + { + dcb_stop_polling_and_shutdown(disposals); + } dcb_close(disposals); disposals = nextdcb; } @@ -2841,13 +2853,13 @@ int dcb_create_SSL(DCB* dcb) if((dcb->ssl = SSL_new(dcb->service->ctx)) == NULL) { - skygw_log_write(LE,"Error: Failed to initialize SSL for connection."); + MXS_ERROR("Failed to initialize SSL for connection."); return -1; } if(SSL_set_fd(dcb->ssl,dcb->fd) == 0) { - skygw_log_write(LE,"Error: Failed to set file descriptor for SSL connection."); + MXS_ERROR("Failed to set file descriptor for SSL connection."); return -1; } @@ -2876,14 +2888,12 @@ int dcb_accept_SSL(DCB* dcb) { ssl_rval = SSL_accept(dcb->ssl); - LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d", - ssl_rval,ssl_errnum))); + MXS_DEBUG("[dcb_accept_SSL] SSL_accept %d, error %d", ssl_rval,ssl_errnum); switch(ssl_rval) { case 0: ssl_errnum = SSL_get_error(dcb->ssl,ssl_rval); - skygw_log_write(LE,"Error: SSL authentication failed (SSL error %d):", - ssl_errnum); + MXS_ERROR("SSL authentication failed (SSL error %d):", ssl_errnum); if(ssl_errnum == SSL_ERROR_SSL || ssl_errnum == SSL_ERROR_SYSCALL) @@ -2891,15 +2901,14 @@ int dcb_accept_SSL(DCB* dcb) while((err_errnum = ERR_get_error()) != 0) { ERR_error_string_n(err_errnum,errbuf,sizeof(errbuf)); - skygw_log_write(LE,"%s",errbuf); + MXS_ERROR("%s", errbuf); } } rval = -1; break; case 1: rval = 1; - LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept done for %s", - dcb->remote))); + MXS_DEBUG("[dcb_accept_SSL] SSL_accept done for %s", dcb->remote); return rval; case -1: @@ -2911,48 +2920,42 @@ int dcb_accept_SSL(DCB* dcb) /** Not all of the data has been read. Go back to the poll queue and wait for more.*/ rval = 0; - LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept ongoing for %s", - dcb->remote))); + MXS_DEBUG("[dcb_accept_SSL] SSL_accept ongoing for %s", dcb->remote); return rval; } else { rval = -1; - skygw_log_write(LE, - "Error: Fatal error in SSL_accept for %s: (SSL version: %s SSL error code: %d)", - dcb->remote, - SSL_get_version(dcb->ssl), - ssl_errnum); + MXS_ERROR("Fatal error in SSL_accept for %s: (SSL version: %s SSL error code: %d)", + dcb->remote, + SSL_get_version(dcb->ssl), + ssl_errnum); if(ssl_errnum == SSL_ERROR_SSL || ssl_errnum == SSL_ERROR_SYSCALL) { while((err_errnum = ERR_get_error()) != 0) { ERR_error_string_n(err_errnum,errbuf,sizeof(errbuf)); - skygw_log_write(LE, - "%s", - errbuf); + MXS_ERROR("%s", errbuf); } if(errno) { - skygw_log_write(LE, "Error: SSL authentication failed due to system" - " error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("SSL authentication failed due to system" + " error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))); } } } break; default: - skygw_log_write_flush(LE, - "Error: Fatal library error in SSL_accept, returned value was %d.", - ssl_rval); + MXS_ERROR("Fatal library error in SSL_accept, returned value was %d.", ssl_rval); rval = -1; break; } ioctl(fd,FIONREAD,&b); pending = SSL_pending(dcb->ssl); #ifdef SS_DEBUG - skygw_log_write_flush(LD,"[dcb_accept_SSL] fd %d: %d bytes, %d pending",fd,b,pending); + MXS_DEBUG("[dcb_accept_SSL] fd %d: %d bytes, %d pending", fd, b, pending); #endif }while((b > 0 || pending > 0) && rval != -1); @@ -2978,16 +2981,16 @@ int dcb_connect_SSL(DCB* dcb) { case 0: errnum = SSL_get_error(dcb->ssl,rval); - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect shutdown for %s@%s", - dcb->user, - dcb->remote))); + MXS_DEBUG("SSL_connect shutdown for %s@%s", + dcb->user, + dcb->remote); return -1; break; case 1: rval = 1; - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect done for %s@%s", - dcb->user, - dcb->remote))); + MXS_DEBUG("SSL_connect done for %s@%s", + dcb->user, + dcb->remote); return rval; case -1: @@ -2999,27 +3002,24 @@ int dcb_connect_SSL(DCB* dcb) queue and wait for more.*/ rval = 0; - LOGIF(LD,(skygw_log_write_flush(LD,"SSL_connect ongoing for %s@%s", - dcb->user, - dcb->remote))); + MXS_DEBUG("SSL_connect ongoing for %s@%s", + dcb->user, + dcb->remote); } else { rval = -1; ERR_error_string_n(errnum,errbuf,sizeof(errbuf)); - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", - dcb->user, - dcb->remote, - errnum, - errbuf); + MXS_ERROR("Fatal error in SSL_accept for %s@%s: (SSL error code: %d) %s", + dcb->user, + dcb->remote, + errnum, + errbuf); } break; default: - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_connect, returned value was %d.", - rval); + MXS_ERROR("Fatal error in SSL_connect, returned value was %d.", rval); break; } @@ -3068,14 +3068,13 @@ int dcb_bytes_readable_SSL(DCB *dcb, int nread) if (rc == -1) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : ioctl FIONREAD for dcb %p in " - "state %s fd %d failed due error %d, %s.", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - errno, - strerror_r(errno, errbuf, sizeof (errbuf))))); + MXS_ERROR("ioctl FIONREAD for dcb %p in " + "state %s fd %d failed due error %d, %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + errno, + strerror_r(errno, errbuf, sizeof (errbuf))); rval = -1; } else @@ -3105,12 +3104,11 @@ int dcb_bytes_readable_SSL(DCB *dcb, int nread) #ifdef SS_DEBUG else if (nbytes != 0 || pending != 0) { - skygw_log_write_flush(LD, "Total: %d Socket: %d Pending: %d", - nread, nbytes, pending); + MXS_DEBUG("Total: %d Socket: %d Pending: %d", nread, nbytes, pending); } else { - skygw_log_write(LD, "Tried to read from socket, no data left. %d bytes read in total.", nread); + MXS_DEBUG("Tried to read from socket, no data left. %d bytes read in total.", nread); } #endif } @@ -3131,14 +3129,13 @@ void dcb_log_ssl_read_error(DCB *dcb, int ssl_errno, int rc) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Read failed, dcb %p in state " - "%s fd %d, SSL error %d: %s.", - dcb, - STRDCBSTATE(dcb->state), - dcb->fd, - ssl_errno, - strerror_r(errno, errbuf, sizeof (errbuf))))); + MXS_ERROR("Read failed, dcb %p in state " + "%s fd %d, SSL error %d: %s.", + dcb, + STRDCBSTATE(dcb->state), + dcb->fd, + ssl_errno, + strerror_r(errno, errbuf, sizeof(errbuf))); if (ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL) @@ -3146,9 +3143,7 @@ void dcb_log_ssl_read_error(DCB *dcb, int ssl_errno, int rc) while ((ssl_errno = ERR_get_error()) != 0) { ERR_error_string_n(ssl_errno, errbuf, STRERROR_BUFLEN); - skygw_log_write(LE, - "%s", - errbuf); + MXS_ERROR("%s", errbuf); } } } diff --git a/server/core/externcmd.c b/server/core/externcmd.c index d58b06351..db12d001d 100644 --- a/server/core/externcmd.c +++ b/server/core/externcmd.c @@ -1,25 +1,22 @@ #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * Tokenize a string into arguments suitable for a execvp call. * @param args Argument string * @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; @@ -85,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) + { + MXS_ERROR("Cannot find file: %s", cmd->argv[0]); + } + else + { + MXS_ERROR("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; } @@ -125,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); } /** @@ -143,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))); + MXS_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++; + MXS_DEBUG("[monitor_exec_cmd] Forked child process %d : %s.", pid, cmd->argv[0]); } 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) + { + MXS_ERROR("The executable cannot be executed: %s", command); + } + else + { + MXS_ERROR("The executable cannot be found: %s", command); + } + free(command); + } + return rval; +} diff --git a/server/core/filter.c b/server/core/filter.c index b8b1d3902..837e204c9 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -38,11 +38,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static SPINLOCK filter_spin = SPINLOCK_INIT; /**< Protects the list of all filters */ static FILTER_DEF *allFilters = NULL; /**< The list of all filters */ @@ -309,6 +304,40 @@ int i; spinlock_release(&filter->spin); } +/** + * Load a filter module for use and create an instance of it for a service. + * @param filter Filter definition + * @return True if module was successfully loaded, false if an error occurred + */ +bool filter_load(FILTER_DEF* filter) +{ + bool rval = false; + if (filter) + { + if (filter->obj == NULL) + { + /* Filter not yet loaded */ + if ((filter->obj = load_module(filter->module, MODULE_FILTER)) == NULL) + { + MXS_ERROR("Failed to load filter module '%s'.", filter->module); + return false; + } + } + + if ((filter->filter = (filter->obj->createInstance)(filter->options, + filter->parameters))) + { + rval = true; + } + else + { + MXS_ERROR("Failed to create filter '%s' instance.", filter->name); + } + + } + return rval; +} + /** * Connect the downstream filter chain for a filter. * @@ -324,38 +353,15 @@ int i; DOWNSTREAM * filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream) { -DOWNSTREAM *me; + DOWNSTREAM *me; - if (filter == NULL) - return NULL; - - if (filter->obj == NULL) - { - /* Filter not yet loaded */ - if ((filter->obj = load_module(filter->module, - MODULE_FILTER)) == NULL) - { - return NULL; - } - } - - if (filter->filter == NULL) - { - if ((filter->filter = (filter->obj->createInstance)(filter->options, - filter->parameters)) == NULL) - { - return NULL; - } - } if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation for filter session failed " - "due to %d,%s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation for filter session failed " + "due to %d,%s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); return NULL; } diff --git a/server/core/gateway.c b/server/core/gateway.c index 62f5afabc..4bc1cbe48 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -101,16 +101,6 @@ time_t MaxScaleStarted; extern char *program_invocation_name; extern char *program_invocation_short_name; -/** - * Variable holding the enabled logfiles information. - * Used from log users to check enabled logs prior calling - * actual library calls such as skygw_log_write. - */ -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /* * Server options are passed to the mysql_server_init. Each gateway must have a unique * data directory that is passed to the mysql_server_init, therefore the data directory @@ -302,9 +292,7 @@ static void maxscale_ssl_id(CRYPTO_THREADID* id) */ static void sighup_handler (int i) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Refreshing configuration following SIGHUP\n"))); + MXS_NOTICE("Refreshing configuration following SIGHUP\n"); config_reload(); } @@ -314,22 +302,15 @@ static void sighup_handler (int i) */ 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_NOTICE("Log file flush following reception of SIGUSR1\n"); + mxs_log_rotate(); } static void sigterm_handler (int i) { extern void shutdown_server(); - skygw_log_write_flush( - LOGFILE_ERROR, - "MaxScale received signal SIGTERM. Exiting."); - skygw_log_sync_all(); + MXS_ERROR("MaxScale received signal SIGTERM. Exiting."); + mxs_log_flush_sync(); shutdown_server(); } @@ -338,10 +319,8 @@ sigint_handler (int i) { extern void shutdown_server(); - skygw_log_write_flush( - LOGFILE_ERROR, - "MaxScale received signal SIGINT. Shutting down."); - skygw_log_sync_all(); + MXS_ERROR("MaxScale received signal SIGINT. Shutting down."); + mxs_log_flush_sync(); shutdown_server(); fprintf(stderr, "\n\nShutting down MaxScale\n\n"); } @@ -355,28 +334,33 @@ sigchld_handler (int i) if ((child = wait(&exit_status)) == -1) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush(LE, "Error: failed to wait child process: %d %s", - errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to wait child process: %d %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); } else { if (WIFEXITED(exit_status)) { - skygw_log_write_flush(WEXITSTATUS(exit_status) != 0 ? LE : LT, - "Child process %d exited with status %d", - child, WEXITSTATUS(exit_status)); + if (WEXITSTATUS(exit_status) != 0) + { + MXS_ERROR("Child process %d exited with status %d", + child, WEXITSTATUS(exit_status)); + } + else + { + MXS_INFO("Child process %d exited with status %d", + child, WEXITSTATUS(exit_status)); + } } else if (WIFSIGNALED(exit_status)) { - skygw_log_write_flush((LE|LT), - "Child process %d was stopped by signal %d.", - child, WTERMSIG(exit_status)); + MXS_ERROR("Child process %d was stopped by signal %d.", + child, WTERMSIG(exit_status)); } else { - skygw_log_write_flush((LE|LT), - "Child process %d did not exit normally. Exit status: %d", - child, exit_status); + MXS_ERROR("Child process %d did not exit normally. Exit status: %d", + child, exit_status); } } } @@ -396,13 +380,11 @@ sigfatal_handler(int i) GATEWAY_CONF* cnf = config_get_global_options(); fprintf(stderr, "\n\nMaxScale "MAXSCALE_VERSION" received fatal signal %d\n", i); - skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i); + MXS_ERROR("Fatal: MaxScale "MAXSCALE_VERSION" received fatal signal %d. Attempting backtrace.", i); - skygw_log_write_flush(LE, "Commit ID: %s System name: %s " - "Release string: %s Embedded library version: %s", - maxscale_commit, cnf->sysname, cnf->release_string, cnf->version_string); + MXS_ERROR("Commit ID: %s System name: %s " + "Release string: %s Embedded library version: %s", + maxscale_commit, cnf->sysname, cnf->release_string, cnf->version_string); { void *addrs[128]; @@ -411,9 +393,7 @@ sigfatal_handler(int i) if (symbols) { for (n = 0; n < count; n++) { - skygw_log_write_flush( - LOGFILE_ERROR, - " %s\n", symbols[n]); + MXS_ERROR(" %s\n", symbols[n]); } free(symbols); } else { @@ -422,7 +402,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"); @@ -459,12 +439,10 @@ static int signal_set(int sig, void (*handler)(int)) { int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed call sigaction() in %s due to %d, %s.", - program_invocation_short_name, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed call sigaction() in %s due to %d, %s.", + program_invocation_short_name, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); rc = 1; } return rc; @@ -488,13 +466,11 @@ int ntfw_cb( int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - skygw_log_write( - LOGFILE_ERROR, - "Error : Failed to remove the data directory %s of " - "MaxScale due to %d, %s.", - datadir, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to remove the data directory %s of " + "MaxScale due to %d, %s.", + datadir, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } return rc; } @@ -789,13 +765,11 @@ static void print_log_n_stderr( if (do_log) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush( - LOGFILE_ERROR, - "%s %s %s %s", - log_err, - logstr, - eno == 0 ? " " : "Error :", - eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_ERROR("%s %s %s %s", + log_err, + logstr, + eno == 0 ? " " : "Error :", + eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf))); } if (do_stderr) { char errbuf[STRERROR_BUFLEN]; @@ -828,14 +802,12 @@ static bool file_is_readable( absolute_pathname, strerror_r(eno, errbuf, sizeof(errbuf))); } - skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : Failed to read the configuration file %s due " - "to %d, %s.", - absolute_pathname, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); - skygw_log_sync_all(); + MXS_WARNING("Failed to read the configuration file %s due " + "to %d, %s.", + absolute_pathname, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); + mxs_log_flush_sync(); succp = false; } return succp; @@ -861,13 +833,11 @@ static bool file_is_writable( eno, strerror_r(eno, errbuf, sizeof(errbuf))); } - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : unable to open file %s for write due " - "to %d, %s.", - absolute_pathname, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_ERROR("Unable to open file %s for write due " + "to %d, %s.", + absolute_pathname, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); succp = false; } return succp; @@ -922,14 +892,11 @@ static char* get_expanded_pathname( relative_path, strerror_r(eno, errbuf, sizeof(errbuf))); - skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : Failed to read the " - "directory %s, due " - "to %d, %s.", - relative_path, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_WARNING("Failed to read the directory %s, due " + "to %d, %s.", + relative_path, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); free(expanded_path); *output_path = NULL; goto return_cnf_file_buf; @@ -952,10 +919,8 @@ static char* get_expanded_pathname( ss_dassert(cnf_file_buf != NULL); char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); free(expanded_path); expanded_path = NULL; @@ -1087,19 +1052,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); @@ -1179,9 +1141,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 " @@ -1688,7 +1650,7 @@ int main(int argc, char **argv) snprintf(errorbuffer, sizeof(errorbuffer), "Error: Failed to pre-parse configuration file. Memory allocation failed."); - skygw_log_write(LE, errorbuffer); + MXS_ERROR("%s", errorbuffer); if (!daemon_mode) { strncat(errorbuffer, "\n", STRING_BUFFER_SIZE); @@ -1708,13 +1670,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) @@ -1725,10 +1682,6 @@ int main(int argc, char **argv) goto return_main; } - argv[0] = "MaxScale"; - argv[1] = "-j"; - argv[2] = get_logdir(); - if (!(*syslog_enabled)) { printf("Syslog logging is disabled.\n"); @@ -1738,27 +1691,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[3] = "-l"; /*< write to syslog */ - /** Logs that should be syslogged */ - argv[4] = "LOGFILE_MESSAGE,LOGFILE_ERROR" - "LOGFILE_DEBUG,LOGFILE_TRACE"; - argv[5] = NULL; - succp = skygw_logmanager_init(5, argv); - } - else - { - argv[3] = "-s"; /*< store to shared memory */ - argv[4] = "LOGFILE_DEBUG,LOGFILE_TRACE"; /*< to shm */ - argv[5] = "-l"; /*< write to syslog */ - argv[6] = "LOGFILE_MESSAGE,LOGFILE_ERROR"; /*< to syslog */ - argv[7] = NULL; - succp = skygw_logmanager_init(7, 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) { @@ -1816,29 +1753,11 @@ int main(int argc, char **argv) get_cachedir()); } - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Configuration file: %s", - cnf_file_path))); - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Log directory: %s/", - get_logdir()))); - LOGIF(LM, - (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Data directory: %s", - get_datadir()))); - LOGIF(LM, - (skygw_log_write_flush(LOGFILE_MESSAGE, - "Module directory: %s", - get_libdir()))); - LOGIF(LM, - (skygw_log_write_flush(LOGFILE_MESSAGE, - "Service cache: %s", - get_cachedir()))); + MXS_NOTICE("Configuration file: %s", cnf_file_path); + MXS_NOTICE("Log directory: %s", get_logdir()); + MXS_NOTICE("Data directory: %s", get_datadir()); + MXS_NOTICE("Module directory: %s", get_libdir()); + MXS_NOTICE("Service cache: %s", get_cachedir()); /*< Update the server options */ for (i = 0; server_options[i]; i++) @@ -1895,15 +1814,13 @@ int main(int argc, char **argv) } } } - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : mysql_library_init failed. It is a " - "mandatory component, required by router services and " - "the MaxScale core. Error %d, %s, %s : %d. Exiting.", - mysql_errno(NULL), - mysql_error(NULL), - __FILE__, - __LINE__); + MXS_ERROR("mysql_library_init failed. It is a " + "mandatory component, required by router services and " + "the MaxScale core. Error %d, %s, %s : %d. Exiting.", + mysql_errno(NULL), + mysql_error(NULL), + __FILE__, + __LINE__); rc = MAXSCALE_NOLIBRARY; goto return_main; } @@ -1914,22 +1831,14 @@ int main(int argc, char **argv) char* fprerr = "Failed to load MaxScale configuration " "file. Exiting. See the error log for details."; print_log_n_stderr(false, !daemon_mode, fprerr, fprerr, 0); - skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to load MaxScale configuration file %s. " - "Exiting.", - cnf_file_path); + MXS_ERROR("Failed to load MaxScale configuration file %s. " + "Exiting.", + cnf_file_path); rc = MAXSCALE_BADCONFIG; goto return_main; } - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MariaDB Corporation MaxScale %s (C) MariaDB Corporation Ab 2013-2015", - MAXSCALE_VERSION))); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale is running in process %i", - getpid()))); + MXS_NOTICE("MariaDB Corporation MaxScale %s (C) MariaDB Corporation Ab 2013-2015", MAXSCALE_VERSION); + MXS_NOTICE("MaxScale is running in process %i", getpid()); /** Check if a MaxScale process is already running */ if (pid_file_exists()) @@ -1993,9 +1902,7 @@ int main(int argc, char **argv) { threads[thread_id] = thread_start(poll_waitevents, (void *)(thread_id + 1)); } - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "MaxScale started with %d server threads.", - config_threadcount()))); + MXS_NOTICE("MaxScale started with %d server threads.", config_threadcount()); /** * Successful start, notify the parent process that it can exit. */ @@ -2024,16 +1931,12 @@ int main(int argc, char **argv) /*< Stop all the monitors */ monitorStopAll(); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale is shutting down."))); + MXS_NOTICE("MaxScale is shutting down."); /** Release mysql thread context*/ mysql_thread_end(); datadir_cleanup(); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "MaxScale shutdown completed."))); + MXS_NOTICE("MaxScale shutdown completed."); unload_all_modules(); /* Remove Pidfile */ @@ -2093,17 +1996,12 @@ static void log_flush_cb( ts1.tv_sec = timeout_ms/1000; ts1.tv_nsec = (timeout_ms%1000) * 1000000; - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "Started MaxScale log flusher."))); + MXS_NOTICE("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, - "Finished MaxScale log flusher."))); + MXS_NOTICE("Finished MaxScale log flusher."); } static void unlock_pidfile() @@ -2387,7 +2285,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/gw_utils.c b/server/core/gw_utils.c index 043a4d645..6054b6372 100644 --- a/server/core/gw_utils.c +++ b/server/core/gw_utils.c @@ -48,11 +48,6 @@ SPINLOCK tmplock = SPINLOCK_INIT; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /* * Set IP address in socket structure in_addr * @@ -79,26 +74,22 @@ setipaddress(struct in_addr *a, char *p) { hint.ai_flags = AI_PASSIVE; hint.ai_family = AF_UNSPEC; if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to obtain address for host %s, %s", - p, - gai_strerror(rc)))); + MXS_ERROR("Failed to obtain address for host %s, %s", + p, + gai_strerror(rc)); - return 0; + return 0; } } else { hint.ai_flags = AI_CANONNAME; hint.ai_family = AF_INET; if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to obtain address for host %s, %s", - p, - gai_strerror(rc)))); + MXS_ERROR("Failed to obtain address for host %s, %s", + p, + gai_strerror(rc)); - return 0; + return 0; } } @@ -120,12 +111,9 @@ setipaddress(struct in_addr *a, char *p) { if (h == NULL) { if ((a->s_addr = inet_addr(p)) == -1) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : gethostbyname failed for [%s]", - p))); + MXS_ERROR("gethostbyname failed for [%s]", p); - return 0; + return 0; } } else { /* take the first one */ @@ -213,11 +201,8 @@ struct hostent *hp; } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to lookup host '%s'. ", - buf))); - return 0; + MXS_ERROR("Failed to lookup host '%s'.", buf); + return 0; } } } @@ -238,11 +223,11 @@ long get_processor_count() #ifdef _SC_NPROCESSORS_ONLN if ((processors = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { - skygw_log_write(LE, "Unable to establish the number of available cores. Defaulting to 4."); + MXS_WARNING("Unable to establish the number of available cores. Defaulting to 4."); processors = 4; } #else #error _SC_NPROCESSORS_ONLN not available. #endif return processors; -} \ No newline at end of file +} diff --git a/server/core/gwbitmask.c b/server/core/gwbitmask.c index 765864d72..0068c846f 100644 --- a/server/core/gwbitmask.c +++ b/server/core/gwbitmask.c @@ -17,6 +17,7 @@ */ #include #include +#include #include /** @@ -47,10 +48,14 @@ * Date Who Description * 28/06/13 Mark Riddoch Initial implementation * 20/08/15 Martin Brampton Added caveats about limitations (above) + * 17/10/15 Martin Brampton Added display of bitmask * * @endverbatim */ +static int bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit); +static int bitmask_count_bits_set(GWBITMASK *bitmask); + /** * Initialise a bitmask * @@ -151,19 +156,42 @@ unsigned char mask; * Return a non-zero value if the bit at the specified bit * position in the bitmask is set. * The bitmask will automatically be extended if the bit is - * beyond the current bitmask length. This could be optimised - * by assuming that a bit beyond the length is unset. + * beyond the current bitmask length. The work is done in the function + * bitmask_isset_without_spinlock, which can be called when a spinlock + * has already been acquired. * * @param bitmask Pointer the bitmask - * @param bit Bit to clear + * @param bit Bit to test */ int bitmask_isset(GWBITMASK *bitmask, int bit) { +int result; + + spinlock_acquire(&bitmask->lock); + result = bitmask_isset_without_spinlock(bitmask, bit); + spinlock_release(&bitmask->lock); + return result; +} + +/** + * Return a non-zero value if the bit at the specified bit + * position in the bitmask is set. Should be called while holding a + * lock on the bitmask. + * + * The bitmask will automatically be extended if the bit is + * beyond the current bitmask length. This could be optimised + * by assuming that a bit beyond the length is unset. + * + * @param bitmask Pointer the bitmask + * @param bit Bit to test + */ +static int +bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit) +{ unsigned char *ptr; unsigned char mask; - spinlock_acquire(&bitmask->lock); if (bit >= bitmask->length) { bitmask->bits = realloc(bitmask->bits, @@ -174,7 +202,6 @@ unsigned char mask; } ptr = bitmask->bits + (bit / 8); mask = 1 << (bit % 8); - spinlock_release(&bitmask->lock); return *ptr & mask; } @@ -239,3 +266,90 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src) spinlock_release(&src->lock); } +/** + * Return a comma separated list of the numbers of the bits that are set in + * a bitmask, numbering starting at zero. Constrained to reject requests that + * could require more than three digit numbers. The returned string must be + * freed by the caller (unless it is null on account of memory allocation + * failure). + * + * @param bitmask Bitmap to make readable + * @return pointer to the newly allocated string, or null if no memory + */ +char * +bitmask_render_readable(GWBITMASK *bitmask) +{ + char *toobig = "Bitmask is too large to render readable"; + char *empty = "No bits are set"; + char onebit[5]; + char *result; + int count_set = 0; + + spinlock_acquire(&bitmask->lock); + if (999 < bitmask->length) + { + result = malloc(strlen(toobig)); + if (result) + { + strcpy(result, toobig); + } + } + else + { + count_set = bitmask_count_bits_set(bitmask); + if (count_set) + { + result = malloc(1 + (4 * count_set)); + if (result) + { + result[0] = 0; + for (int i = 0; ilength; i++) + { + if (bitmask_isset_without_spinlock(bitmask, i)) + { + sprintf(onebit, "%d,", i); + strcat(result, onebit); + } + } + result[strlen(result)-1] = 0; + } + } + else + { + result = malloc(strlen(empty)); + if (result) + { + strcpy(result, empty); + } + } + } + spinlock_release(&bitmask->lock); + return result; +} + +/** + * Return a count of the number of bits set in a bitmask. Helpful for setting + * the size of string needed to show the set bits in readable form. + * + * @param bitmask Bitmap whose bits are to be counted + * @return int Number of set bits + */ +static int +bitmask_count_bits_set(GWBITMASK *bitmask) +{ + const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; + unsigned char partresults; + int result = 0; + unsigned char *ptr, *eptr; + + ptr = bitmask->bits; + eptr = ptr + (bitmask->length / 8); + while (ptr < eptr) + { + partresults = oneBits[*ptr&0x0f]; + partresults += oneBits[*ptr>>4]; + result += partresults; + ptr++; + } + return result; +} \ No newline at end of file diff --git a/server/core/hashtable.c b/server/core/hashtable.c index c43b76916..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; } @@ -379,7 +383,7 @@ hashtable_fetch(HASHTABLE *table, void *key) unsigned int hashkey; HASHENTRIES *entry; - if(table == NULL || key == NULL) + if(table == NULL || key == NULL || 0 == table->hashsize) return NULL; hashkey = table->hashfn(key) % table->hashsize; @@ -762,7 +766,9 @@ char buf[40]; key = keyread(fd); value = valueread(fd); if (key == NULL || value == NULL) - break; + { + break; + } hashtable_add(table, key, value); rval++; } @@ -770,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/load_utils.c b/server/core/load_utils.c index 7ff97bfa7..3f0259776 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -51,11 +51,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static MODULES *registered = NULL; static MODULES *find_module(const char *module); @@ -93,10 +88,8 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) mem->data = realloc(mem->data, mem->size + realsize + 1); if(mem->data == NULL) { /* out of memory! */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error in module_feedback_send(), not enough memory for realloc"))); - return 0; + MXS_ERROR("Error in module_feedback_send(), not enough memory for realloc"); + return 0; } memcpy(&(mem->data[mem->size]), contents, realsize); @@ -138,36 +131,30 @@ MODULE_INFO *mod_info = NULL; if (access(fname, F_OK) == -1) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to find library for " - "module: %s. Module dir: %s", - module, get_libdir()))); + MXS_ERROR("Unable to find library for " + "module: %s. Module dir: %s", + module, get_libdir()); return NULL; } if ((dlhandle = dlopen(fname, RTLD_NOW|RTLD_LOCAL)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to load library for module: " - "%s\n\n\t\t %s." - "\n\n", - module, - dlerror()))); - return NULL; + MXS_ERROR("Unable to load library for module: " + "%s\n\n\t\t %s." + "\n\n", + module, + dlerror()); + return NULL; } if ((sym = dlsym(dlhandle, "version")) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Version interface not supported by " - "module: %s\n\t\t\t %s.", - module, - dlerror()))); - dlclose(dlhandle); - return NULL; + MXS_ERROR("Version interface not supported by " + "module: %s\n\t\t\t %s.", + module, + dlerror()); + dlclose(dlhandle); + return NULL; } ver = sym; version = ver(); @@ -188,42 +175,26 @@ MODULE_INFO *mod_info = NULL; if (strcmp(type, MODULE_PROTOCOL) == 0 && mod_info->modapi != MODULE_API_PROTOCOL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Module '%s' does not implement " - "the protocol API.\n", - module))); - fatal = 1; + MXS_ERROR("Module '%s' does not implement the protocol API.", module); + fatal = 1; } if (strcmp(type, MODULE_ROUTER) == 0 && mod_info->modapi != MODULE_API_ROUTER) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Module '%s' does not implement " - "the router API.\n", - module))); - fatal = 1; + MXS_ERROR("Module '%s' does not implement the router API.", module); + fatal = 1; } if (strcmp(type, MODULE_MONITOR) == 0 && mod_info->modapi != MODULE_API_MONITOR) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Module '%s' does not implement " - "the monitor API.\n", - module))); - fatal = 1; + MXS_ERROR("Module '%s' does not implement the monitor API.", module); + fatal = 1; } if (strcmp(type, MODULE_FILTER) == 0 && mod_info->modapi != MODULE_API_FILTER) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Module '%s' does not implement " - "the filter API.\n", - module))); - fatal = 1; + MXS_ERROR("Module '%s' does not implement the filter API.", module); + fatal = 1; } if (fatal) { @@ -234,24 +205,20 @@ MODULE_INFO *mod_info = NULL; if ((sym = dlsym(dlhandle, "GetModuleObject")) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Expected entry point interface missing " - "from module: %s\n\t\t\t %s.", - module, - dlerror()))); - dlclose(dlhandle); - return NULL; + MXS_ERROR("Expected entry point interface missing " + "from module: %s\n\t\t\t %s.", + module, + dlerror()); + dlclose(dlhandle); + return NULL; } ep = sym; modobj = ep(); - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "Loaded module %s: %s from %s", - module, - version, - fname))); + MXS_NOTICE("Loaded module %s: %s from %s", + module, + version, + fname); register_module(module, type, dlhandle, version, modobj, mod_info); } else @@ -453,11 +420,9 @@ MODULES *modules_list = registered; FEEDBACK_CONF *feedback_config = config_get_feedback_data(); if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"))); + MXS_ERROR("Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"); - return; + return; } dcb_printf(dcb, (char *)GWBUF_DATA(buffer)); gwbuf_free(buffer); @@ -573,13 +538,12 @@ module_feedback_send(void* data) { /* Configuration check */ if (feedback_config->feedback_enable == 0 || feedback_config->feedback_url == NULL || feedback_config->feedback_user_info == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error in module_feedback_send(): some mandatory parameters are not set" - " feedback_enable=%u, feedback_url=%s, feedback_user_info=%s", - feedback_config->feedback_enable, - feedback_config->feedback_url == NULL ? "NULL" : feedback_config->feedback_url, - feedback_config->feedback_user_info == NULL ? "NULL" : feedback_config->feedback_user_info))); + MXS_ERROR("Error in module_feedback_send(): some mandatory parameters are not set" + " feedback_enable=%u, feedback_url=%s, feedback_user_info=%s", + feedback_config->feedback_enable, + feedback_config->feedback_url == NULL ? "NULL" : feedback_config->feedback_url, + feedback_config->feedback_user_info == NULL ? + "NULL" : feedback_config->feedback_user_info); feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; @@ -596,36 +560,29 @@ module_feedback_send(void* data) { /* It's not the rigt time, mark it as to be done and return */ feedback_config->feedback_last_action = _NOTIFICATION_SEND_PENDING; - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "module_feedback_send(): execution skipped, current hour [%d]" - " is not within the proper interval (from 2 AM to 4 AM)", - hour))); + MXS_INFO("module_feedback_send(): execution skipped, current hour [%d]" + " is not within the proper interval (from 2 AM to 4 AM)", + hour); return; } /* Time to run the task: if a previous run was succesfull skip next runs */ if (feedback_config->feedback_last_action == _NOTIFICATION_SEND_OK) { - /* task was done before, return */ + /* task was done before, return */ - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "module_feedback_send(): execution skipped because of previous succesful run: hour is [%d], last_action [%d]", - hour, feedback_config->feedback_last_action))); + MXS_INFO("module_feedback_send(): execution skipped because of previous " + "succesful run: hour is [%d], last_action [%d]", + hour, feedback_config->feedback_last_action); - return; + return; } - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "module_feedback_send(): task now runs: hour is [%d], last_action [%d]", - hour, feedback_config->feedback_last_action))); + MXS_INFO("module_feedback_send(): task now runs: hour is [%d], last_action [%d]", + hour, feedback_config->feedback_last_action); if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"))); + MXS_ERROR("Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory"); feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; @@ -640,16 +597,12 @@ module_feedback_send(void* data) { } else { feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR; - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "Error in module_create_feedback_report(): do_http_post ret_code is %d", http_send))); + MXS_INFO("Error in module_create_feedback_report(): do_http_post ret_code is %d", http_send); } - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "module_feedback_send(): task completed: hour is [%d], last_action [%d]", - hour, - feedback_config->feedback_last_action))); + MXS_INFO("module_feedback_send(): task completed: hour is [%d], last_action [%d]", + hour, + feedback_config->feedback_last_action); gwbuf_free(buffer); @@ -827,12 +780,10 @@ do_http_post(GWBUF *buffer, void *cfg) { /* Check for errors */ if(res != CURLE_OK) { ret_code = 2; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: do_http_post(), curl call for [%s] failed due: %s, %s", - feedback_config->feedback_url, - curl_easy_strerror(res), - error_message))); + MXS_ERROR("do_http_post(), curl call for [%s] failed due: %s, %s", + feedback_config->feedback_url, + curl_easy_strerror(res), + error_message); goto cleanup; } else { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); @@ -847,25 +798,18 @@ do_http_post(GWBUF *buffer, void *cfg) { goto cleanup; } } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: do_http_post(), Bad HTTP Code from remote server: %lu", - http_code))); - ret_code = 4; - goto cleanup; + MXS_ERROR("do_http_post(), Bad HTTP Code from remote server: %lu", http_code); + ret_code = 4; + goto cleanup; } } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: do_http_post(), curl object not initialized"))); - ret_code = 1; - goto cleanup; + MXS_ERROR("do_http_post(), curl object not initialized"); + ret_code = 1; + goto cleanup; } - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "do_http_post() ret_code [%d], HTTP code [%d]", - ret_code, http_code))); + MXS_INFO("do_http_post() ret_code [%d], HTTP code [%ld]", + ret_code, http_code); cleanup: if (chunk.data) diff --git a/server/core/maxkeys.c b/server/core/maxkeys.c index 27b5c8382..21546fb49 100644 --- a/server/core/maxkeys.c +++ b/server/core/maxkeys.c @@ -22,59 +22,42 @@ * @verbatim * Revision History * - * Date Who Description - * 24/07/13 Mark Riddoch Initial implementation + * Date Who Description + * 24/07/13 Mark Riddoch Initial implementation * * @endverbatim */ -#include -#include +#include +#include #include #include #include int main(int argc, char **argv) { - int arg_count = 4; - char *home; char *keyfile; - char** arg_vector; int rval = 0; - if (argc < 2) - { - keyfile = "/var/lib/maxscale/"; - fprintf(stderr, "Generating .secrets file in /var/lib/maxscale/ ...\n"); - } - else - { - keyfile = argv[1]; - } - arg_vector = malloc(sizeof(char*)*(arg_count + 1)); + if (argc < 2) + { + keyfile = "/var/lib/maxscale/"; + fprintf(stderr, "Generating .secrets file in /var/lib/maxscale/ ...\n"); + } + else + { + keyfile = argv[1]; + } - if(arg_vector == NULL) - { - fprintf(stderr,"Error: Memory allocation failed.\n"); - return 1; - } + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); - arg_vector[0] = "logmanager"; - arg_vector[1] = "-j"; - arg_vector[2] = "/var/log/maxscale/maxkeys"; - arg_vector[3] = "-o"; - arg_vector[4] = NULL; - skygw_logmanager_init(arg_count,arg_vector); - free(arg_vector); - + if (secrets_writeKeys(keyfile)) + { + fprintf(stderr, "Failed to encode the password\n"); + rval = 1; + } - if (secrets_writeKeys(keyfile)) - { - fprintf(stderr, "Failed to encode the password\n"); - 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 0a3d5ddea..89d37caaf 100644 --- a/server/core/maxpasswd.c +++ b/server/core/maxpasswd.c @@ -22,74 +22,59 @@ * @verbatim * Revision History * - * Date Who Description - * 24/07/13 Mark Riddoch Initial implementation + * Date Who Description + * 24/07/13 Mark Riddoch Initial implementation * * @endverbatim */ -#include -#include +#include +#include #include #include /** * Encrypt a password for storing in the MaxScale.cnf file * - * @param argc Argument count - * @param argv Argument vector + * @param argc Argument count + * @param argv Argument vector */ int main(int argc, char **argv) { - char *enc, *pw; - int arg_count = 6; - char *home; - char** arg_vector; - int rval = 0; + char *enc; + char *pw; + char *home; + int rval = 0; - if (argc != 3) - { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } + if (argc != 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } - 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.\n"); - return 1; - } + pw = calloc(81, sizeof(char)); - arg_vector[0] = strdup("logmanager"); - arg_vector[1] = strdup("-j"); - arg_vector[2] = strdup("/var/log/maxscale"); + if (pw == NULL) + { + fprintf(stderr, "Error: cannot allocate enough memory."); + return 1; + } - arg_vector[3] = "-o"; - arg_vector[4] = "-l"; - arg_vector[5] = "LOGFILE_ERROR"; - arg_vector[6] = NULL; - skygw_logmanager_init(arg_count,arg_vector); - free(arg_vector[2]); - free(arg_vector); - - pw = calloc(81,sizeof(char)); + strncpy(pw, argv[2], 80); - if(pw == NULL){ - fprintf(stderr, "Error: cannot allocate enough memory."); - return 1; - } + if ((enc = encryptPassword(argv[1], pw)) != NULL) + { + printf("%s\n", enc); + } + else + { + fprintf(stderr, "Failed to encode the password\n"); + rval = 1; + } - strncpy(pw,argv[2],80); - - if ((enc = encryptPassword(argv[1],pw)) != NULL){ - printf("%s\n", enc); - }else{ - fprintf(stderr, "Failed to encode the password\n"); - rval = 1; - } - - free(pw); - skygw_log_sync_all(); - skygw_logmanager_done(); - return rval; + free(pw); + mxs_log_flush_sync(); + mxs_log_finish(); + return rval; } diff --git a/server/core/modutil.c b/server/core/modutil.c index fdf4d4f73..0f10005e0 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -33,11 +33,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** These are used when converting MySQL wildcards to regular expressions */ static SPINLOCK re_lock = SPINLOCK_INIT; static bool pattern_init = false; @@ -593,8 +588,7 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf) /** The next packet is a partial, split into complete and partial packets */ if((buff = gwbuf_clone_portion(packet,0,total)) == NULL) { - skygw_log_write(LOGFILE_ERROR, - "Error: Failed to partially clone buffer."); + MXS_ERROR("Failed to partially clone buffer."); return NULL; } gwbuf_consume(packet,total); @@ -740,10 +734,8 @@ static void modutil_reply_routing_error( if (buf == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Creating routing error message failed."))); - return; + MXS_ERROR("Creating routing error message failed."); + return; } /** Set flags that help router to process reply correctly */ gwbuf_set_type(buf, flags); @@ -881,8 +873,7 @@ void prepare_pcre2_patterns() else { pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - skygw_log_write(LE, "Error: Failed to compile PCRE2 pattern: %s", - errbuf); + MXS_ERROR("Failed to compile PCRE2 pattern: %s", errbuf); } if (!pattern_init) @@ -945,8 +936,7 @@ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* { PCRE2_UCHAR errbuf[STRERROR_BUFLEN]; pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); - skygw_log_write(LE, "Error: Failed to match pattern: %s", - errbuf); + MXS_ERROR("Failed to match pattern: %s", errbuf); } err = true; } @@ -959,7 +949,7 @@ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* if (err) { - skygw_log_write(LE, "Error: Fatal error when matching wildcard patterns."); + MXS_ERROR("Fatal error when matching wildcard patterns."); } pcre2_match_data_free(mdata_percent); diff --git a/server/core/monitor.c b/server/core/monitor.c index ef782ed47..afad7d7da 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -43,11 +43,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static MONITOR *allMonitors = NULL; static SPINLOCK monLock = SPINLOCK_INIT; @@ -71,12 +66,9 @@ MONITOR *mon; if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to load monitor module '%s'.", - name))); - free(mon); - return NULL; + MXS_ERROR("Unable to load monitor module '%s'.", name); + free(mon); + return NULL; } mon->state = MONITOR_STATE_ALLOC; mon->name = strdup(name); @@ -89,6 +81,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; @@ -123,6 +116,7 @@ MONITOR *ptr; ptr->next = mon->next; } spinlock_release(&monLock); + free_config_parameter(mon->parameters); free(mon->name); free(mon); } @@ -142,6 +136,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 * @@ -350,10 +361,8 @@ monitorSetNetworkTimeout(MONITOR *mon, int type, int value) { memcpy(&mon->connect_timeout, &value, sizeof(int)); } else { memcpy(&mon->connect_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); + MXS_WARNING("Monitor Connect Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout); } break; @@ -362,10 +371,8 @@ monitorSetNetworkTimeout(MONITOR *mon, int type, int value) { memcpy(&mon->read_timeout, &value, sizeof(int)); } else { memcpy(&mon->read_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); + MXS_WARNING("Monitor Read Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout); } break; @@ -374,17 +381,13 @@ monitorSetNetworkTimeout(MONITOR *mon, int type, int value) { memcpy(&mon->write_timeout, &value, sizeof(int)); } else { memcpy(&mon->write_timeout, &new_timeout, sizeof(int)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds" - ", lowering to %i seconds", value, max_timeout, new_timeout))); + MXS_WARNING("Monitor Write Timeout %i is greater than monitor interval ~%i seconds" + ", lowering to %i seconds", value, max_timeout, new_timeout); } break; default: - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Monitor setNetworkTimeout received an unsupported action type %i", type))); - break; + MXS_ERROR("Monitor setNetworkTimeout received an unsupported action type %i", type); + break; } } @@ -472,7 +475,7 @@ bool check_monitor_permissions(MONITOR* monitor) if((mysql = mysql_init(NULL)) == NULL) { - skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + MXS_ERROR("[%s] Error: MySQL connection initialization failed.", __FUNCTION__); free(dpasswd); return false; } @@ -485,12 +488,12 @@ bool check_monitor_permissions(MONITOR* monitor) * user permissions. */ if(mysql_real_connect(mysql,server->name,user,dpasswd,NULL,server->port,NULL,0) == NULL) { - skygw_log_write(LE,"%s: Error: Failed to connect to server %s(%s:%d) when" - " checking monitor user credentials and permissions.", - monitor->name, - server->unique_name, - server->name, - server->port); + MXS_ERROR("%s: Failed to connect to server %s(%s:%d) when" + " checking monitor user credentials and permissions.", + monitor->name, + server->unique_name, + server->name, + server->port); mysql_close(mysql); free(dpasswd); return false; @@ -500,13 +503,13 @@ bool check_monitor_permissions(MONITOR* monitor) { if(mysql_errno(mysql) == ER_SPECIFIC_ACCESS_DENIED_ERROR) { - skygw_log_write(LE,"%s: Error: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", - monitor->name,user,mysql_error(mysql)); + MXS_ERROR("%s: User '%s' is missing REPLICATION CLIENT privileges. MySQL error message: %s", + monitor->name,user,mysql_error(mysql)); } else { - skygw_log_write(LE,"%s: Error: Monitor failed to query for slave status. MySQL error message: %s", - monitor->name,mysql_error(mysql)); + MXS_ERROR("%s: Monitor failed to query for slave status. MySQL error message: %s", + monitor->name,mysql_error(mysql)); } rval = false; } @@ -514,8 +517,8 @@ bool check_monitor_permissions(MONITOR* monitor) { if((res = mysql_use_result(mysql)) == NULL) { - skygw_log_write(LE,"%s: Error: Result retrieval failed when checking for REPLICATION CLIENT permissions: %s", - monitor->name,mysql_error(mysql)); + MXS_ERROR("%s: Result retrieval failed when checking for REPLICATION CLIENT permissions: %s", + monitor->name,mysql_error(mysql)); rval = false; } else @@ -526,4 +529,23 @@ bool check_monitor_permissions(MONITOR* monitor) mysql_close(mysql); free(dpasswd); return rval; -} \ No newline at end of file +} + +/** + * 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/poll.c b/server/core/poll.c index 906682d22..1a74aeb93 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -46,11 +46,6 @@ extern unsigned long hkheartbeat; MEMLOG *plog; #endif -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - int number_poll_spins; int max_poll_sleep; @@ -74,8 +69,7 @@ int max_poll_sleep; * thread utilisation and fairer scheduling of the event * processing. * 07/07/15 Martin Brampton Simplified add and remove DCB, improve error handling. - * 23/08/15 Martin Brampton Provisionally added test so only DCB with a - * session link can be added to the poll list + * 23/08/15 Martin Brampton Added test so only DCB with a session link can be added to the poll list * * @endverbatim */ @@ -96,7 +90,7 @@ static simple_mutex_t epoll_wait_mutex; /*< serializes calls to epoll_wait */ static int n_waiting = 0; /*< No. of threads in epoll_wait */ static int process_pollq(int thread_id); static void poll_add_event_to_dcb(DCB* dcb, GWBUF* buf, __uint32_t ev); - +static bool poll_dcb_session_check(DCB *dcb, const char *); DCB *eventq = NULL; SPINLOCK pollqlock = SPINLOCK_INIT; @@ -290,42 +284,21 @@ poll_add_dcb(DCB *dcb) || DCB_STATE_ZOMBIE == dcb->state || DCB_STATE_UNDEFINED == dcb->state) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_add_dcb] Error : existing state of dcb %p " - "is %s, but this should be impossible, crashing.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - raise(SIGABRT); - } - /* - * This test could be wrong. On the face of it, we don't want to add a - * DCB to the poll list if it is not linked to a session because the code - * that handles events will expect to find a session. Test added by - * Martin as an experiment on 23 August 2015 - */ - if (false && NULL == dcb->session) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [%s] Error : Attempt to add dcb %p " - "to poll list but it is not linked to a session, crashing.", - __func__, - pthread_self(), - dcb))); + MXS_ERROR("%lu [poll_add_dcb] Error : existing state of dcb %p " + "is %s, but this should be impossible, crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); raise(SIGABRT); } if (DCB_STATE_POLLING == dcb->state || DCB_STATE_LISTENING == dcb->state) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_add_dcb] Error : existing state of dcb %p " - "is %s, but this is probably an error, not crashing.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); + MXS_ERROR("%lu [poll_add_dcb] Error : existing state of dcb %p " + "is %s, but this is probably an error, not crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); } dcb->state = new_state; spinlock_release(&dcb->dcb_initlock); @@ -341,12 +314,10 @@ poll_add_dcb(DCB *dcb) } if (0 == rc) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_add_dcb] Added dcb %p in state %s to poll set.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); + MXS_DEBUG("%lu [poll_add_dcb] Added dcb %p in state %s to poll set.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); } else dcb->state = old_state; return rc; @@ -377,18 +348,12 @@ poll_remove_dcb(DCB *dcb) if (DCB_STATE_POLLING != dcb->state && DCB_STATE_LISTENING != dcb->state) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_remove_dcb] Error : existing state of dcb %p " - "is %s, but this is probably an error, not crashing.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); + MXS_ERROR("%lu [poll_remove_dcb] Error : existing state of dcb %p " + "is %s, but this is probably an error, not crashing.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); } - /*< Set bit for each maxscale thread. This should be done before - * the state is changed, so as to protect the DCB from premature - * destruction. */ - bitmask_copy(&dcb->memdata.bitmask, poll_bitmask()); /*< * Set state to NOPOLLING and remove dcb from poll set. */ @@ -436,25 +401,21 @@ poll_resolve_error(DCB *dcb, int errornum, bool adding) { if (EEXIST == errornum) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_resolve_error] Error : epoll_ctl could not add, " - "already exists for DCB %p.", - pthread_self(), - dcb))); + MXS_ERROR("%lu [poll_resolve_error] Error : epoll_ctl could not add, " + "already exists for DCB %p.", + pthread_self(), + dcb); // Assume another thread added and no serious harm done return 0; } if (ENOSPC == errornum) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_resolve_error] The limit imposed by " - "/proc/sys/fs/epoll/max_user_watches was " - "encountered while trying to register (EPOLL_CTL_ADD) a new " - "file descriptor on an epoll instance for dcb %p.", - pthread_self(), - dcb))); + MXS_ERROR("%lu [poll_resolve_error] The limit imposed by " + "/proc/sys/fs/epoll/max_user_watches was " + "encountered while trying to register (EPOLL_CTL_ADD) a new " + "file descriptor on an epoll instance for dcb %p.", + pthread_self(), + dcb); /* Failure - assume handled by callers */ return -1; } @@ -464,12 +425,10 @@ poll_resolve_error(DCB *dcb, int errornum, bool adding) /* Must be removing */ if (ENOENT == errornum) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [poll_resolve_error] Error : epoll_ctl could not remove, " - "not found, for dcb %p.", - pthread_self(), - dcb))); + MXS_ERROR("%lu [poll_resolve_error] Error : epoll_ctl could not remove, " + "not found, for dcb %p.", + pthread_self(), + dcb); // Assume another thread removed and no serious harm done return 0; } @@ -584,13 +543,11 @@ int poll_spins = 0; atomic_add(&n_waiting, -1); int eno = errno; errno = 0; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] epoll_wait returned " - "%d, errno %d", - pthread_self(), - nfds, - eno))); + MXS_DEBUG("%lu [poll_waitevents] epoll_wait returned " + "%d, errno %d", + pthread_self(), + nfds, + eno); atomic_add(&n_waiting, -1); } /* @@ -631,11 +588,9 @@ int poll_spins = 0; if (poll_spins <= number_poll_spins + 1) atomic_add(&pollStats.n_nbpollev, 1); poll_spins = 0; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] epoll_wait found %d fds", - pthread_self(), - nfds))); + MXS_DEBUG("%lu [poll_waitevents] epoll_wait found %d fds", + pthread_self(), + nfds); atomic_add(&pollStats.n_pollev, 1); if (thread_data) { @@ -784,8 +739,8 @@ poll_set_maxwait(unsigned int maxwait) * Including session id to log entries depends on this function. Assumption is * that when maxscale thread starts processing of an event it processes one * and only one session until it returns from this function. Session id is - * read to thread's local storage in macro LOGIF_MAYBE(...) and reset back - * to zero just before returning in LOGIF(...) macro. + * read to thread's local storage if LOG_MAY_BE_ENABLED(LOGFILE_TRACE) returns true + * reset back to zero just before returning in LOG_IS_ENABLED(LOGFILE_TRACE) returns true. * Thread local storage (tls_log_info_t) follows thread and is accessed every * time log is written to particular log. * @@ -869,31 +824,31 @@ unsigned long qtime; #if defined(FAKE_CODE) if (dcb_fake_write_ev[dcb->fd] != 0) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "Added fake events %d to ev %d.", - pthread_self(), - dcb_fake_write_ev[dcb->fd], - ev))); - ev |= dcb_fake_write_ev[dcb->fd]; - dcb_fake_write_ev[dcb->fd] = 0; + MXS_DEBUG("%lu [poll_waitevents] " + "Added fake events %d to ev %d.", + pthread_self(), + dcb_fake_write_ev[dcb->fd], + ev); + ev |= dcb_fake_write_ev[dcb->fd]; + dcb_fake_write_ev[dcb->fd] = 0; } #endif /* FAKE_CODE */ ss_debug(spinlock_acquire(&dcb->dcb_initlock);) ss_dassert(dcb->state != DCB_STATE_ALLOC); - ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); - ss_dassert(dcb->state != DCB_STATE_FREED); - ss_debug(spinlock_release(&dcb->dcb_initlock);) + /* It isn't obvious that this is impossible */ + /* ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); */ + if (DCB_STATE_DISCONNECTED == dcb->state) + { + return 0; + } + ss_debug(spinlock_release(&dcb->dcb_initlock)); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] event %d dcb %p " - "role %s", - pthread_self(), - ev, - dcb, - STRDCBROLE(dcb->dcb_role)))); + MXS_DEBUG("%lu [poll_waitevents] event %d dcb %p " + "role %s", + pthread_self(), + ev, + dcb, + STRDCBROLE(dcb->dcb_role)); if (ev & EPOLLOUT) { @@ -903,59 +858,71 @@ unsigned long qtime; if (eno == 0) { atomic_add(&pollStats.n_write, 1); /** Read session id to thread's local storage */ - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.write_ready(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + + if (poll_dcb_session_check(dcb, "write_ready")) + { + dcb->func.write_ready(dcb); + } } else { char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "EPOLLOUT due %d, %s. " - "dcb %p, fd %i", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf)), - dcb, - dcb->fd))); + MXS_DEBUG("%lu [poll_waitevents] " + "EPOLLOUT due %d, %s. " + "dcb %p, fd %i", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf)), + dcb, + dcb->fd); } } if (ev & EPOLLIN) { if (dcb->state == DCB_STATE_LISTENING) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "Accept in fd %d", - pthread_self(), - dcb->fd))); + MXS_DEBUG("%lu [poll_waitevents] " + "Accept in fd %d", + pthread_self(), + dcb->fd); atomic_add( &pollStats.n_accept, 1); - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.accept(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + + if (poll_dcb_session_check(dcb, "accept")) + { + dcb->func.accept(dcb); + } } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "Read in dcb %p fd %d", - pthread_self(), - dcb, - dcb->fd))); + MXS_DEBUG("%lu [poll_waitevents] " + "Read in dcb %p fd %d", + pthread_self(), + dcb, + dcb->fd); atomic_add(&pollStats.n_read, 1); /** Read session id to thread's local storage */ - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.read(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + + if (poll_dcb_session_check(dcb, "read")) + { + dcb->func.read(dcb); + } } } if (ev & EPOLLERR) @@ -965,34 +932,35 @@ unsigned long qtime; if (eno == 0) { eno = dcb_fake_write_errno[dcb->fd]; char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "Added fake errno %d. " - "%s", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [poll_waitevents] " + "Added fake errno %d. " + "%s", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } dcb_fake_write_errno[dcb->fd] = 0; #endif /* FAKE_CODE */ if (eno != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "EPOLLERR due %d, %s.", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [poll_waitevents] " + "EPOLLERR due %d, %s.", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } atomic_add(&pollStats.n_error, 1); /** Read session id to thread's local storage */ - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.error(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + if (poll_dcb_session_check(dcb, "error")) + { + dcb->func.error(dcb); + } } if (ev & EPOLLHUP) @@ -1000,16 +968,14 @@ unsigned long qtime; int eno = 0; eno = gw_getsockerrno(dcb->fd); char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "EPOLLHUP on dcb %p, fd %d. " - "Errno %d, %s.", - pthread_self(), - dcb, - dcb->fd, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [poll_waitevents] " + "EPOLLHUP on dcb %p, fd %d. " + "Errno %d, %s.", + pthread_self(), + dcb, + dcb->fd, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); atomic_add(&pollStats.n_hup, 1); spinlock_acquire(&dcb->dcb_initlock); if ((dcb->flags & DCBF_HUNG) == 0) @@ -1017,11 +983,16 @@ unsigned long qtime; dcb->flags |= DCBF_HUNG; spinlock_release(&dcb->dcb_initlock); /** Read session id to thread's local storage */ - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.hangup(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + if (poll_dcb_session_check(dcb, "hangup EPOLLHUP")) + { + dcb->func.hangup(dcb); + } } else spinlock_release(&dcb->dcb_initlock); @@ -1033,16 +1004,14 @@ unsigned long qtime; int eno = 0; eno = gw_getsockerrno(dcb->fd); char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [poll_waitevents] " - "EPOLLRDHUP on dcb %p, fd %d. " - "Errno %d, %s.", - pthread_self(), - dcb, - dcb->fd, - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [poll_waitevents] " + "EPOLLRDHUP on dcb %p, fd %d. " + "Errno %d, %s.", + pthread_self(), + dcb, + dcb->fd, + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); atomic_add(&pollStats.n_hup, 1); spinlock_acquire(&dcb->dcb_initlock); if ((dcb->flags & DCBF_HUNG) == 0) @@ -1050,11 +1019,16 @@ unsigned long qtime; dcb->flags |= DCBF_HUNG; spinlock_release(&dcb->dcb_initlock); /** Read session id to thread's local storage */ - LOGIF_MAYBE(LT, (dcb_get_ses_log_info( - dcb, - &tls_log_info.li_sesid, - &tls_log_info.li_enabled_logs))); - dcb->func.hangup(dcb); + if (LOG_MAY_BE_ENABLED(LOGFILE_TRACE)) + { + dcb_get_ses_log_info(dcb, + &tls_log_info.li_sesid, + &tls_log_info.li_enabled_logs); + } + if (poll_dcb_session_check(dcb, "hangup EPOLLRDHUP")) + { + dcb->func.hangup(dcb); + } } else spinlock_release(&dcb->dcb_initlock); @@ -1116,13 +1090,45 @@ unsigned long qtime; } dcb->evq.processing = 0; /** Reset session id from thread's local storage */ - LOGIF(LT, tls_log_info.li_sesid = 0); + if (LOG_IS_ENABLED(LOGFILE_TRACE)) + { + tls_log_info.li_sesid = 0; + } spinlock_release(&pollqlock); return 1; } /** + * + * Check that the DCB has a session link before processing. + * If not, log an error. Processing will be bypassed + * + * @param dcb The DCB to check + * @param function The name of the function about to be called + * @return bool Does the DCB have a non-null session link + */ +static bool +poll_dcb_session_check(DCB *dcb, const char *function) +{ + if (dcb->session) + { + return true; + } + else + { + MXS_ERROR("%lu [%s] The dcb %p that was about to be processed by %s does not " + "have a non-null session pointer ", + pthread_self(), + __func__, + dcb, + function); + return false; + } +} + +/** + * * Shutdown the polling loop */ void @@ -1548,6 +1554,53 @@ uint32_t ev = EPOLLOUT; spinlock_release(&pollqlock); } +/* + * Insert a fake hangup event for a DCB into the polling queue. + * + * This is used when a monitor detects that a server is not responding. + * + * @param dcb DCB to emulate an EPOLLOUT event for + */ +void +poll_fake_hangup_event(DCB *dcb) +{ +uint32_t ev = EPOLLRDHUP; + + spinlock_acquire(&pollqlock); + if (DCB_POLL_BUSY(dcb)) + { + if (dcb->evq.pending_events == 0) + pollStats.evq_pending++; + dcb->evq.pending_events |= ev; + } + else + { + dcb->evq.pending_events = ev; + dcb->evq.inserted = hkheartbeat; + if (eventq) + { + dcb->evq.prev = eventq->evq.prev; + eventq->evq.prev->evq.next = dcb; + eventq->evq.prev = dcb; + dcb->evq.next = eventq; + } + else + { + eventq = dcb; + dcb->evq.prev = dcb; + dcb->evq.next = dcb; + } + pollStats.evq_length++; + pollStats.evq_pending++; + dcb->evq.inserted = hkheartbeat; + if (pollStats.evq_length > pollStats.evq_max) + { + pollStats.evq_max = pollStats.evq_length; + } + } + spinlock_release(&pollqlock); +} + /** * Print the event queue contents * diff --git a/server/core/random_jkiss.c b/server/core/random_jkiss.c new file mode 100644 index 000000000..97a09fa34 --- /dev/null +++ b/server/core/random_jkiss.c @@ -0,0 +1,131 @@ +/* + * 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 + */ + +/** + * @file random_jkiss.c - Random number generator for the MariaDB Corporation MaxScale + * + * See http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf for discussion of random + * number generators (RNGs). + * + * @verbatim + * Revision History + * + * Date Who Description + * 26/08/15 Martin Brampton Initial implementation + * + * @endverbatim + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Public domain code for JKISS RNG - Comment header added */ + +/* If possible, the seed variables will be set from /dev/urandom but + * should that fail, these arbitrary numbers will be used as a last resort. + */ +static unsigned int x = 123456789,y = 987654321,z = 43219876,c = 6543217; /* Seed variables */ +static bool init = false; + +static SPINLOCK random_jkiss_spinlock = SPINLOCK_INIT; + +static unsigned int random_jkiss_devrand(void); +static void random_init_jkiss(void); + +/*** + * + * Return a pseudo-random number that satisfies major tests for random sequences + * + * @return uint Random number + * + */ +unsigned int +random_jkiss(void) +{ + unsigned long long t; + unsigned int result; + + spinlock_acquire(&random_jkiss_spinlock); + if (!init) + { + /* Must set init first because initialisation calls this function */ + init = true; + spinlock_release(&random_jkiss_spinlock); + random_init_jkiss(); + } + x = 314527869 * x + 1234567; + y ^= y << 5; + y ^= y >> 7; + y ^= y << 22; + t = 4294584393ULL * z + c; + c = t >> 32; + z = t; + result = x + y + z; + spinlock_release(&random_jkiss_spinlock); + return result; +} + +/* Own code adapted from http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf */ + +/*** + * + * Obtain a seed random number from /dev/urandom if available. + * + * @return uint Random number + * + */ +static unsigned int +random_jkiss_devrand(void) +{ + int fn; + unsigned int r; + if ((fn = open("/dev/urandom", O_RDONLY)) == -1) return 0; + if (read(fn, &r, sizeof(r)) != sizeof(r)) + { + r = 0; + } + close(fn); + return r; +} + +/*** + * + * Initialise the generator using /dev/urandom if available, and warm up + * with 1000 iterations + * + */ +static void +random_init_jkiss(void) +{ + int newrand, i; + spinlock_acquire(&random_jkiss_spinlock); + if ((newrand = random_jkiss_devrand()) != 0) x = newrand; + if ((newrand = random_jkiss_devrand()) != 0) y = newrand; + if ((newrand = random_jkiss_devrand()) != 0) z = newrand; + if ((newrand = random_jkiss_devrand()) != 0) + c = newrand % 698769068 + 1; /* Should be less than 698769069 */ + spinlock_release(&random_jkiss_spinlock); + + /* "Warm up" our random number generator */ + for (i = 0; i < 100; i++) random_jkiss(); +} diff --git a/server/core/secrets.c b/server/core/secrets.c index fd4f42ac5..3f4ff586c 100644 --- a/server/core/secrets.c +++ b/server/core/secrets.c @@ -1,419 +1,392 @@ -/* - * 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 - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; -/** - * Generate a random printable character - * - * @return A random printable character - */ -static unsigned char -secrets_randomchar() -{ - return (char)((rand() % ('~' - ' ')) + ' '); -} - -static int -secrets_random_str(unsigned char *output, int len) -{ -int i; - srand((unsigned long )time(0L) ^ (unsigned long )output); - - 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); - srand(randval); - 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]; + MXS_NOTICE("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]; + MXS_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]; + MXS_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]; + MXS_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]; + MXS_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); + MXS_ERROR("Ignoring secrets file " + "%s, invalid permissions.", + secret_file); + return NULL; + } + + if ((keys = (MAXKEYS *) malloc(sizeof(MAXKEYS))) == NULL) + { + close(fd); + MXS_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]; + MXS_ERROR("Read from secrets file " + "%s failed. Read %d, expected %d bytes. Error %d, %s.", + secret_file, + len, + (int)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]; + MXS_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) + { + MXS_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]; + MXS_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]; + MXS_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) + { + MXS_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]; + MXS_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]; + MXS_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]; + MXS_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; + const 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 34875fe91..8623eaa2e 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -48,11 +48,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static SPINLOCK server_spin = SPINLOCK_INIT; static SERVER *allServers = NULL; @@ -79,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; @@ -87,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); @@ -154,7 +150,10 @@ server_get_persistent(SERVER *server, char *user, const char *protocol) { DCB *dcb, *previous = NULL; - if (server->persistent && dcb_persistent_clean_count(server->persistent, false) && server->persistent) + if (server->persistent + && dcb_persistent_clean_count(server->persistent, false) + && server->persistent + && (server->status & SERVER_RUNNING)) { spinlock_acquire(&server->persistlock); dcb = server->persistent; @@ -183,19 +182,17 @@ server_get_persistent(SERVER *server, char *user, const char *protocol) } else { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [server_get_persistent] Rejected dcb " - "%p from pool, user %s looking for %s, protocol %s " - "looking for %s, hung flag %s, error handle called %s.", - pthread_self(), - dcb, - dcb->user ? dcb->user : "NULL", - user, - dcb->protoname ? dcb->protoname : "NULL", - protocol, - (dcb->flags & DCBF_HUNG) ? "true" : "false", - dcb-> dcb_errhandle_called ? "true" : "false"))); + MXS_DEBUG("%lu [server_get_persistent] Rejected dcb " + "%p from pool, user %s looking for %s, protocol %s " + "looking for %s, hung flag %s, error handle called %s.", + pthread_self(), + dcb, + dcb->user ? dcb->user : "NULL", + user, + dcb->protoname ? dcb->protoname : "NULL", + protocol, + (dcb->flags & DCBF_HUNG) ? "true" : "false", + dcb-> dcb_errhandle_called ? "true" : "false"); } previous = dcb; dcb = dcb->nextpersistent; @@ -722,26 +719,22 @@ server_update(SERVER *server, char *protocol, char *user, char *passwd) { if (!strcmp(server->protocol, protocol)) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Update server protocol for server %s to protocol %s.", - server->name, - protocol))); - free(server->protocol); - server->protocol = strdup(protocol); + MXS_NOTICE("Update server protocol for server %s to protocol %s.", + server->name, + protocol); + free(server->protocol); + server->protocol = strdup(protocol); } if (user != NULL && passwd != NULL) { if (strcmp(server->monuser, user) == 0 || strcmp(server->monpw, passwd) == 0) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Update server monitor credentials for server %s", - server->name))); - free(server->monuser); - free(server->monpw); - serverAddMonUser(server, user, passwd); + MXS_NOTICE("Update server monitor credentials for server %s", + server->name); + free(server->monuser); + free(server->monpw); + serverAddMonUser(server, user, passwd); } } } @@ -912,3 +905,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 617ee7926..f97393cd0 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -66,11 +66,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static RSA *rsa_512 = NULL; static RSA *rsa_1024 = NULL; @@ -123,19 +118,17 @@ SERVICE *service; char* home = get_libdir(); char* ldpath = getenv("LD_LIBRARY_PATH"); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to load %s module \"%s\".\n\t\t\t" - " Ensure that lib%s.so exists in one of the " - "following directories :\n\t\t\t " - "- %s\n%s%s", - MODULE_ROUTER, - router, - router, - home, - ldpath?"\t\t\t - ":"", - ldpath?ldpath:""))); - free(service); + MXS_ERROR("Unable to load %s module \"%s\".\n\t\t\t" + " Ensure that lib%s.so exists in one of the " + "following directories :\n\t\t\t " + "- %s\n%s%s", + MODULE_ROUTER, + router, + router, + home, + ldpath?"\t\t\t - ":"", + ldpath?ldpath:""); + free(service); return NULL; } service->name = strdup(servname); @@ -149,6 +142,7 @@ SERVICE *service; service->ssl_ca_cert = NULL; service->ssl_cert = NULL; service->ssl_key = NULL; + service->log_auth_warnings = true; service->ssl_cert_verify_depth = DEFAULT_SSL_CERT_VERIFY_DEPTH; /** Support the highest possible SSL/TLS methods available as the default */ service->ssl_method_type = SERVICE_SSL_TLS_MAX; @@ -218,11 +212,8 @@ GWPROTOCOL *funcs; if (port->listener == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create listener for service %s.", - service->name))); - goto retblock; + MXS_ERROR("Failed to create listener for service %s.", service->name); + goto retblock; } if (strcmp(port->protocol, "MySQLClient") == 0) { @@ -237,13 +228,11 @@ GWPROTOCOL *funcs; if ((loaded = load_mysql_users(service)) < 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to load users for " - "service %s listening at %s:%d.", - service->name, - (port->address == NULL ? "0.0.0.0" : port->address), - port->port))); + MXS_ERROR("Unable to load users for " + "service %s listening at %s:%d.", + service->name, + (port->address == NULL ? "0.0.0.0" : port->address), + port->port); { /* Try loading authentication data from file cache */ @@ -255,15 +244,12 @@ GWPROTOCOL *funcs; loaded = dbusers_load(service->users, path); if (loaded != -1) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Using cached credential information."))); + MXS_ERROR("Using cached credential information."); } } if (loaded == -1) { - hashtable_free(service->users->data); - free(service->users); + users_free(service->users); service->users = NULL; dcb_close(port->listener); port->listener = NULL; @@ -288,10 +274,10 @@ GWPROTOCOL *funcs; if(errno != EEXIST) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", - path, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to create directory '%s': [%d] %s", + path, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } mkdir_rval = 0; } @@ -307,10 +293,10 @@ GWPROTOCOL *funcs; if(errno != EEXIST) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", - path, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to create directory '%s': [%d] %s", + path, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } mkdir_rval = 0; } @@ -319,12 +305,10 @@ GWPROTOCOL *funcs; } if (loaded == 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Service %s: failed to load any user " - "information. Authentication will " - "probably fail as a result.", - service->name))); + MXS_ERROR("Service %s: failed to load any user " + "information. Authentication will " + "probably fail as a result.", + service->name); } /* At service start last update is set to USERS_REFRESH_TIME seconds earlier. @@ -333,10 +317,8 @@ GWPROTOCOL *funcs; service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME; service->rate_limit.nloads=1; - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Loaded %d MySQL Users for service [%s].", - loaded, service->name))); + MXS_NOTICE("Loaded %d MySQL Users for service [%s].", + loaded, service->name); } } else @@ -351,18 +333,17 @@ GWPROTOCOL *funcs; == NULL) { 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, - "Error : Unable to load protocol module %s. Listener " - "for service %s not started.", - port->protocol, - service->name))); + MXS_ERROR("Unable to load protocol module %s. Listener " + "for service %s not started.", + port->protocol, + service->name); goto retblock; } memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL)); - port->listener->session = NULL; if (port->address) sprintf(config_bind, "%s:%d", port->address, port->port); @@ -380,26 +361,25 @@ GWPROTOCOL *funcs; } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create session to service %s.", - service->name))); + MXS_ERROR("Failed to create session to service %s.", + service->name); users_free(service->users); - dcb_close(port->listener); + service->users = NULL; + dcb_close(port->listener); port->listener = NULL; + service->users = NULL; goto retblock; } } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to start to listen port %d for %s %s.", - port->port, - port->protocol, - service->name))); + MXS_ERROR("Unable to start to listen port %d for %s %s.", + port->port, + port->protocol, + service->name); users_free(service->users); + service->users = NULL; dcb_close(port->listener); port->listener = NULL; } @@ -446,8 +426,8 @@ int serviceStartAllPorts(SERVICE* service) service->name, service->stats.n_failed_starts); hktask_oneshot(taskname, service_interal_restart, (void*) service, retry_after); - skygw_log_write(LM, "Failed to start service %s, retrying in %d seconds.", - service->name, retry_after); + MXS_NOTICE("Failed to start service %s, retrying in %d seconds.", + service->name, retry_after); } return listeners; } @@ -481,25 +461,21 @@ serviceStart(SERVICE *service) } else { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "%s: Failed to create router instance for service. Service not started.", - service->name))); + MXS_ERROR("%s: Failed to create router instance for service. Service not started.", + service->name); service->state = SERVICE_STATE_FAILED; } } else { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "%s: SSL initialization failed. Service not started.", - service->name))); + MXS_ERROR("%s: SSL initialization failed. Service not started.", service->name); service->state = SERVICE_STATE_FAILED; } } else { - skygw_log_write_flush(LE, - "%s: Error: Inadequate user permissions for service. Service not started.", - service->name); + MXS_ERROR("%s: Inadequate user permissions for service. Service not started.", + service->name); service->state = SERVICE_STATE_FAILED; } return listeners; @@ -547,10 +523,7 @@ int n = 0,i; if(i == 0) { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Failed to start service '%s'.", - ptr->name))); + MXS_ERROR("Failed to start service '%s'.", ptr->name); } ptr = ptr->next; @@ -691,6 +664,7 @@ SERV_PROTOCOL *proto; { return 0; } + proto->listener = NULL; proto->protocol = strdup(protocol); if (address) proto->address = strdup(address); @@ -922,7 +896,7 @@ serviceOptimizeWildcard(SERVICE *service, int action) service->optimize_wildcard = action; if(action) { - LOGIF(LM,(skygw_log_write(LOGFILE_MESSAGE,"[%s] Optimizing wildcard database grants.",service->name))); + MXS_NOTICE("[%s] Optimizing wildcard database grants.",service->name); } return 1; } @@ -1105,9 +1079,8 @@ int n = 0; if ((flist = (FILTER_DEF **)malloc(sizeof(FILTER_DEF *))) == NULL) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Out of memory adding filters to service.\n"))); - return; + MXS_ERROR("Out of memory adding filters to service.\n"); + return; } ptr = strtok_r(filters, "|", &brkt); while (ptr) @@ -1116,19 +1089,23 @@ int n = 0; if ((flist = (FILTER_DEF **)realloc(flist, (n + 1) * sizeof(FILTER_DEF *))) == NULL) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Out of memory adding filters to service.\n"))); - return; + MXS_ERROR("Out of memory adding filters to service."); + return; } - if ((flist[n-1] = filter_find(trim(ptr))) == NULL) + char *filter_name = trim(ptr); + if ((flist[n-1] = filter_find(filter_name)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : Unable to find filter '%s' for service '%s'\n", - trim(ptr), service->name - ))); + MXS_WARNING("Unable to find filter '%s' for service '%s'\n", + filter_name, service->name); + n--; + } + else if (!filter_load(flist[n - 1])) + { + MXS_ERROR("Failed to load filter '%s' for service '%s'.", + filter_name, service->name); n--; } + flist[n] = NULL; ptr = strtok_r(NULL, "|", &brkt); } @@ -1405,34 +1382,27 @@ void *router_obj; { if ((router_obj = load_module(router, MODULE_ROUTER)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to update router " - "for service %s to %s.", - service->name, - router))); + MXS_ERROR("Failed to update router " + "for service %s to %s.", + service->name, + router); } else { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Update router for service %s to %s.", - service->name, - router))); - free(service->routerModule); - service->routerModule = strdup(router); - service->router = router_obj; + MXS_NOTICE("Update router for service %s to %s.", + service->name, + router); + free(service->routerModule); + service->routerModule = strdup(router); + service->router = router_obj; } } if (user && (strcmp(service->credentials.name, user) != 0 || strcmp(service->credentials.authdata, auth) != 0)) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Update credentials for service %s.", - service->name))); - serviceSetUser(service, user, auth); + MXS_NOTICE("Update credentials for service %s.", service->name); + serviceSetUser(service, user, auth); } } @@ -1448,22 +1418,19 @@ int service_refresh_users(SERVICE *service) { int ret = 1; /* check for another running getUsers request */ if (! spinlock_acquire_nowait(&service->users_table_spin)) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%s: [service_refresh_users] failed to get get lock for loading new users' table: another thread is loading users", - service->name))); + MXS_DEBUG("%s: [service_refresh_users] failed to get get lock for " + "loading new users' table: another thread is loading users", + service->name); - return 1; + return 1; } /* check if refresh rate limit has exceeded */ if ( (time(NULL) < (service->rate_limit.last + USERS_REFRESH_TIME)) || (service->rate_limit.nloads > USERS_REFRESH_MAX_PER_TIME)) { spinlock_release(&service->users_table_spin); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%s: Refresh rate limit exceeded for load of users' table.", - service->name))); + MXS_ERROR("%s: Refresh rate limit exceeded for load of users' table.", + service->name); return 1; } @@ -2046,7 +2013,7 @@ int serviceInitSSL(SERVICE* service) if((service->ctx = SSL_CTX_new(service->method)) == NULL) { - skygw_log_write(LE, "Error: SSL context initialization failed."); + MXS_ERROR("SSL context initialization failed."); return -1; } @@ -2059,7 +2026,7 @@ int serviceInitSSL(SERVICE* service) rsa_512 = RSA_generate_key(512,RSA_F4,NULL,NULL); if (rsa_512 == NULL) { - skygw_log_write(LE,"Error: 512-bit RSA key generation failed."); + MXS_ERROR("512-bit RSA key generation failed."); return -1; } } @@ -2068,7 +2035,7 @@ int serviceInitSSL(SERVICE* service) rsa_1024 = RSA_generate_key(1024,RSA_F4,NULL,NULL); if (rsa_1024 == NULL) { - skygw_log_write(LE,"Error: 1024-bit RSA key generation failed."); + MXS_ERROR("1024-bit RSA key generation failed."); return -1; } } @@ -2078,26 +2045,26 @@ int serviceInitSSL(SERVICE* service) /** Load the server sertificate */ if (SSL_CTX_use_certificate_file(service->ctx, service->ssl_cert, SSL_FILETYPE_PEM) <= 0) { - skygw_log_write(LE,"Error: Failed to set server SSL certificate."); + MXS_ERROR("Failed to set server SSL certificate."); return -1; } /* Load the private-key corresponding to the server certificate */ if (SSL_CTX_use_PrivateKey_file(service->ctx, service->ssl_key, SSL_FILETYPE_PEM) <= 0) { - skygw_log_write(LE,"Error: Failed to set server SSL key."); + MXS_ERROR("Failed to set server SSL key."); return -1; } /* Check if the server certificate and private-key matches */ if (!SSL_CTX_check_private_key(service->ctx)) { - skygw_log_write(LE,"Error: Server SSL certificate and key do not match."); + MXS_ERROR("Server SSL certificate and key do not match."); return -1; } /* Load the RSA CA certificate into the SSL_CTX structure */ if (!SSL_CTX_load_verify_locations(service->ctx, service->ssl_ca_cert, NULL)) { - skygw_log_write(LE,"Error: Failed to set Certificate Authority file."); + MXS_ERROR("Failed to set Certificate Authority file."); return -1; } @@ -2118,4 +2085,4 @@ void service_interal_restart(void *data) { SERVICE* service = (SERVICE*)data; serviceStartAllPorts(service); -} \ No newline at end of file +} diff --git a/server/core/session.c b/server/core/session.c index 32c7148e5..bb76808ed 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -27,6 +27,7 @@ * 02/09/13 Massimiliano Pinto Added session refcounter * 29/05/14 Mark Riddoch Addition of filter mechanism * 23/08/15 Martin Brampton Tidying; slight improvement in safety + * 17/09/15 Martin Brampton Keep failed session in existence - leave DCBs to close * * @endverbatim */ @@ -45,19 +46,16 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** Global session id; updated safely by holding session_spin */ static size_t session_id; static SPINLOCK session_spin = SPINLOCK_INIT; static SESSION *allSessions = NULL; +static struct session session_dummy_struct; static int session_setup_filters(SESSION *session); +static void session_simple_free(SESSION *session, DCB *dcb); /** * Allocate a new session for a new client of the specified service. @@ -76,55 +74,54 @@ session_alloc(SERVICE *service, DCB *client_dcb) SESSION *session; session = (SESSION *)calloc(1, sizeof(SESSION)); - ss_info_dassert(session != NULL, - "Allocating memory for session failed."); + ss_info_dassert(session != NULL, "Allocating memory for session failed."); if (session == NULL) { - char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to allocate memory for " - "session object due error %d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + char errbuf[STRERROR_BUFLEN]; + MXS_ERROR("Failed to allocate memory for " + "session object due error %d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); + /* Does this possibly need a lock? */ + /* + * This is really not the right way to do this. The data in a DCB is + * router specific and should be freed by a function in the relevant + * router. This would be better achieved by placing a function reference + * in the DCB and having dcb_final_free call it to dispose of the data + * at the final destruction of the DCB. However, this piece of code is + * only run following a calloc failure, so the system is probably on + * the point of crashing anyway. + * + */ if (client_dcb->data && !DCB_IS_CLONE(client_dcb)) { - void *clientdata = client_dcb->data; + void * clientdata = client_dcb->data; client_dcb->data = NULL; free(clientdata); } - goto return_session; + return NULL; } #if defined(SS_DEBUG) session->ses_chk_top = CHK_NUM_SESSION; session->ses_chk_tail = CHK_NUM_SESSION; #endif - if (DCB_IS_CLONE(client_dcb)) - { - session->ses_is_child = true; - } + session->ses_is_child = (bool) DCB_IS_CLONE(client_dcb); spinlock_init(&session->ses_lock); - /*< - * Prevent backend threads from accessing before session is completely - * initialized. - */ - spinlock_acquire(&session->ses_lock); session->service = service; - session->client = client_dcb; - session->n_filters = 0; - memset(&session->stats, 0, sizeof(SESSION_STATS)); - session->stats.connect = time(0); - session->state = SESSION_STATE_ALLOC; + session->client = client_dcb; + session->n_filters = 0; + memset(&session->stats, 0, sizeof(SESSION_STATS)); + session->stats.connect = time(0); + session->state = SESSION_STATE_ALLOC; /*< - * Associate the session to the client DCB and set the reference count on - * the session to indicate that there is a single reference to the + * Associate the session to the client DCB and set the reference count on + * the session to indicate that there is a single reference to the * session. There is no need to protect this or use atomic add as the * session has not been made available to the other threads at this * point. */ session->data = client_dcb->data; - client_dcb->session = session; session->refcount = 1; /*< * This indicates that session is ready to be shared with backend @@ -132,139 +129,135 @@ session_alloc(SERVICE *service, DCB *client_dcb) */ session->state = SESSION_STATE_READY; - /*< Release session lock */ - spinlock_release(&session->ses_lock); - - /* - * Only create a router session if we are not the listening - * DCB or an internal DCB. Creating a router session may create a connection to a - * backend server, depending upon the router module implementation - * and should be avoided for the listener session - * - * Router session creation may create other DCBs that link to the - * session, therefore it is important that the session lock is + /* + * Only create a router session if we are not the listening + * DCB or an internal DCB. Creating a router session may create a connection to a + * backend server, depending upon the router module implementation + * and should be avoided for the listener session + * + * Router session creation may create other DCBs that link to the + * session, therefore it is important that the session lock is * relinquished before the router call. - */ - if (client_dcb->state != DCB_STATE_LISTENING && + */ + if (client_dcb->state != DCB_STATE_LISTENING && client_dcb->dcb_role != DCB_ROLE_INTERNAL) - { - session->router_session = - service->router->newSession(service->router_instance, session); + { + session->router_session = service->router->newSession(service->router_instance, session); if (session->router_session == NULL) - { - /** - * Inform other threads that session is closing. - */ - session->state = SESSION_STATE_STOPPING; - /*< - * Decrease refcount, set dcb's session pointer NULL - * and set session pointer to NULL. - */ - session->client = NULL; - session_free(session); - client_dcb->session = NULL; - session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create %s session.", - service->name))); - - goto return_session; - } - /* - * Pending filter chain being setup set the head of the chain to - * be the router. As filters are inserted the current head will - * be pushed to the filter and the head updated. - * - * NB This dictates that filters are created starting at the end - * of the chain nearest the router working back to the client - * protocol end of the chain. - */ - session->head.instance = service->router_instance; - session->head.session = session->router_session; - - session->head.routeQuery = (void *)(service->router->routeQuery); - - session->tail.instance = session; - session->tail.session = session; - session->tail.clientReply = session_reply; - - if (service->n_filters > 0) - { - if (!session_setup_filters(session)) - { - /** - * Inform other threads that session is closing. - */ - session->state = SESSION_STATE_STOPPING; - /*< - * Decrease refcount, set dcb's session pointer NULL - * and set session pointer to NULL. - */ - session->client = NULL; - session_free(session); - client_dcb->session = NULL; - session = NULL; - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Setting up filters failed. " - "Terminating session %s.", - service->name))); - goto return_session; - } - } - } - - spinlock_acquire(&session->ses_lock); - - if (session->state != SESSION_STATE_READY) { - spinlock_release(&session->ses_lock); - session->client = NULL; - session_free(session); - client_dcb->session = NULL; - session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create %s session.", - service->name))); - spinlock_release(&session_spin); + session->state = SESSION_STATE_TO_BE_FREED; + + MXS_ERROR("%lu [%s] Error : Failed to create %s session because router" + "could not establish a new router session, see earlier error.", + pthread_self(), + __func__, + service->name); + + } + /* + * Pending filter chain being setup set the head of the chain to + * be the router. As filters are inserted the current head will + * be pushed to the filter and the head updated. + * + * NB This dictates that filters are created starting at the end + * of the chain nearest the router working back to the client + * protocol end of the chain. + */ + session->head.instance = service->router_instance; + session->head.session = session->router_session; + + session->head.routeQuery = (void *)(service->router->routeQuery); + + session->tail.instance = session; + session->tail.session = session; + session->tail.clientReply = session_reply; + + if (SESSION_STATE_TO_BE_FREED != session->state + && service->n_filters > 0 + && !session_setup_filters(session)) + { + session->state = SESSION_STATE_TO_BE_FREED; + MXS_ERROR("Setting up filters failed. " + "Terminating session %s.", + service->name); + } + } + + if (SESSION_STATE_TO_BE_FREED != session->state) + { + session->state = SESSION_STATE_ROUTER_READY; + + if (session->client->user == NULL) + { + MXS_INFO("Started session [%lu] for %s service ", + session->ses_id, + service->name); } else { - session->state = SESSION_STATE_ROUTER_READY; - spinlock_release(&session->ses_lock); - spinlock_acquire(&session_spin); - /** Assign a session id and increase */ - session->ses_id = ++session_id; - session->next = allSessions; - allSessions = session; - spinlock_release(&session_spin); - - if (session->client->user == NULL) - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Started session [%lu] for %s service ", - session->ses_id, - service->name))); - } - else - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Started %s client session [%lu] for '%s' from %s", - service->name, - session->ses_id, - session->client->user, - session->client->remote))); - } - atomic_add(&service->stats.n_sessions, 1); - atomic_add(&service->stats.n_current, 1); - CHK_SESSION(session); - } -return_session: - return session; + MXS_INFO("Started %s client session [%lu] for '%s' from %s", + service->name, + session->ses_id, + session->client->user, + session->client->remote); + } + } + else + { + MXS_INFO("Start %s client session [%lu] for '%s' from %s failed, will be " + "closed as soon as all related DCBs have been closed.", + service->name, + session->ses_id, + session->client->user, + session->client->remote); + } + spinlock_acquire(&session_spin); + /** Assign a session id and increase, insert session into list */ + session->ses_id = ++session_id; + session->next = allSessions; + allSessions = session; + spinlock_release(&session_spin); + atomic_add(&service->stats.n_sessions, 1); + atomic_add(&service->stats.n_current, 1); + CHK_SESSION(session); + + client_dcb->session = session; + return SESSION_STATE_TO_BE_FREED == session->state ? NULL : session; +} + +/** + * Allocate a dummy session so that DCBs can always have sessions. + * + * Only one dummy session exists, it is statically declared + * + * @param client_dcb The client side DCB + * @return The dummy created session + */ +SESSION * +session_set_dummy(DCB *client_dcb) +{ + SESSION *session; + + session = &session_dummy_struct; +#if defined(SS_DEBUG) + session->ses_chk_top = CHK_NUM_SESSION; + session->ses_chk_tail = CHK_NUM_SESSION; +#endif + session->ses_is_child = false; + spinlock_init(&session->ses_lock); + session->service = NULL; + session->client = NULL; + session->n_filters = 0; + memset(&session->stats, 0, sizeof(SESSION_STATS)); + session->stats.connect = 0; + session->state = SESSION_STATE_DUMMY; + session->data = NULL; + session->refcount = 1; + session->ses_id = 0; + session->next = NULL; + + client_dcb->session = session; + return session; } /** @@ -361,30 +354,67 @@ int session_unlink_dcb( return nlink; } +/** + * Deallocate the specified session, minimal actions during session_alloc + * Since changes to keep new session in existence until all related DCBs + * have been destroyed, this function is redundant. Just left until we are + * sure of the direction taken. + * + * @param session The session to deallocate + */ +static void +session_simple_free(SESSION *session, DCB *dcb) +{ + /* Does this possibly need a lock? */ + if (dcb->data && !DCB_IS_CLONE(dcb)) + { + void * clientdata = dcb->data; + dcb->data = NULL; + free(clientdata); + } + if (session) + { + if (SESSION_STATE_DUMMY == session->state) + { + return; + } + if (session && session->router_session) + { + session->service->router->freeSession( + session->service->router_instance, + session->router_session); + } + session->state = SESSION_STATE_STOPPING; + } + + free(session); +} + + /** * Deallocate the specified session * * @param session The session to deallocate */ -bool session_free( - SESSION *session) +bool +session_free(SESSION *session) { - bool succp = false; - SESSION *ptr; - int nlink; - int i; - + if (session && SESSION_STATE_DUMMY == session->state) + { + return true; + } CHK_SESSION(session); - /*< + + /* * Remove one reference. If there are no references left, * free session. */ - nlink = session_unlink_dcb(session, NULL); - - if (nlink != 0) { - ss_dassert(nlink > 0); - goto return_succp; + if (atomic_add(&session->refcount, -1) > 1) + { + /* Must be one or more references left */ + return false; } + session->state = SESSION_STATE_TO_BE_FREED; /* First of all remove from the linked list */ spinlock_acquire(&session_spin); @@ -394,13 +424,16 @@ bool session_free( } else { - ptr = allSessions; - while (ptr && ptr->next != session) + SESSION *chksession; + chksession = allSessions; + while (chksession && chksession->next != session) { - ptr = ptr->next; + chksession = chksession->next; } - if (ptr) - ptr->next = session->next; + if (chksession) + { + chksession->next = session->next; + } } spinlock_release(&session_spin); atomic_add(&session->service->stats.n_current, -1); @@ -417,6 +450,7 @@ bool session_free( } if (session->n_filters) { + int i; for (i = 0; i < session->n_filters; i++) { if (session->filters[i].filter) @@ -434,11 +468,9 @@ bool session_free( free(session->filters); } - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Stopped %s client session [%lu]", - session->service->name, - session->ses_id))); + MXS_INFO("Stopped %s client session [%lu]", + session->service->name, + session->ses_id); /** Disable trace and decrease trace logger counter */ session_disable_log(session, LT); @@ -454,10 +486,7 @@ bool session_free( } free(session); } - succp = true; - -return_succp : - return succp; + return true; } /** @@ -667,11 +696,11 @@ int i; ptr->client->user?ptr->client->user:"", ptr->client->user?"@":"", ptr->client->remote); - dcb_printf(dcb, "\tConnected: %s", + dcb_printf(dcb, "\tConnected: %s\n", asctime_r(localtime_r(&ptr->stats.connect, &result), buf)); if(ptr->client->state == DCB_STATE_POLLING) { - dcb_printf(dcb, "\tIdle: %.0f seconds",idle); + dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle); } } @@ -739,6 +768,8 @@ session_state(int state) { case SESSION_STATE_ALLOC: return "Session Allocated"; + case SESSION_STATE_DUMMY: + return "Dummy Session"; case SESSION_STATE_READY: return "Session Ready"; case SESSION_STATE_ROUTER_READY: @@ -800,33 +831,26 @@ int i; if ((session->filters = calloc(service->n_filters, sizeof(SESSION_FILTER))) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Insufficient memory to allocate session filter " - "tracking.\n"))); - return 0; + MXS_ERROR("Insufficient memory to allocate session filter " + "tracking.\n"); + return 0; } session->n_filters = service->n_filters; for (i = service->n_filters - 1; i >= 0; i--) { if (service->filters[i] == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Service '%s' contians an unresolved filter.\n", - service->name))); - return 0; + MXS_ERROR("Service '%s' contians an unresolved filter.", service->name); + return 0; } if ((head = filterApply(service->filters[i], session, &session->head)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create filter '%s' for " - "service '%s'.\n", - service->filters[i]->name, - service->name))); - return 0; + MXS_ERROR("Failed to create filter '%s' for " + "service '%s'.\n", + service->filters[i]->name, + service->name); + return 0; } session->filters[i].filter = service->filters[i]; session->filters[i].session = head->session; @@ -841,12 +865,10 @@ int i; session->filters[i].session, &session->tail)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Failed to create filter '%s' for service '%s'.\n", - service->filters[i]->name, - service->name))); - return 0; + MXS_ERROR("Failed to create filter '%s' for service '%s'.", + service->filters[i]->name, + service->name); + return 0; } /* diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 2d32d55b1..149a38f09 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -1,10 +1,10 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR}) add_executable(test_mysql_users test_mysql_users.c) -add_executable(test_hash testhash.c) -add_executable(test_hint testhint.c) -add_executable(test_spinlock testspinlock.c) +add_executable(test_hash testhash.c ../random_jkiss.c) +add_executable(test_hint testhint.c ../random_jkiss.c) +add_executable(test_spinlock testspinlock.c ../random_jkiss.c) add_executable(test_filter testfilter.c) -add_executable(test_buffer testbuffer.c) +add_executable(test_buffer testbuffer.c ../random_jkiss.c) add_executable(test_dcb testdcb.c) add_executable(test_modutil testmodutil.c) add_executable(test_poll testpoll.c) @@ -12,8 +12,9 @@ add_executable(test_service testservice.c) add_executable(test_server testserver.c) add_executable(test_users testusers.c) add_executable(test_adminusers testadminusers.c) -add_executable(testmemlog testmemlog.c) +add_executable(testmemlog testmemlog.c ../random_jkiss.c) add_executable(testfeedback testfeedback.c) +add_executable(testmaxscalepcre2 testmaxscalepcre2.c ../random_jkiss.c) target_link_libraries(test_mysql_users MySQLClient fullcore) target_link_libraries(test_hash fullcore log_manager) target_link_libraries(test_hint fullcore log_manager) @@ -29,6 +30,7 @@ target_link_libraries(test_users fullcore) target_link_libraries(test_adminusers fullcore) target_link_libraries(testmemlog fullcore log_manager) target_link_libraries(testfeedback fullcore) +target_link_libraries(testmaxscalepcre2 fullcore log_manager) add_test(Internal-TestMySQLUsers test_mysql_users) add_test(Internal-TestHash test_hash) add_test(Internal-TestHint test_hint) @@ -43,5 +45,6 @@ add_test(Internal-TestServer test_server) add_test(Internal-TestUsers test_users) add_test(Internal-TestAdminUsers test_adminusers) add_test(Internal-TestMemlog testmemlog) +add_test(Internal-TestMaxScalePCRE2 testmaxscalepcre2) add_test(TestFeedback testfeedback) set_tests_properties(TestFeedback PROPERTIES TIMEOUT 30) 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/testbuffer.c b/server/core/test/testbuffer.c index 179300be3..ac768fcbe 100644 --- a/server/core/test/testbuffer.c +++ b/server/core/test/testbuffer.c @@ -68,9 +68,7 @@ int buflen; ss_info_dassert(0 == GWBUF_EMPTY(buffer), "Buffer should not be empty"); ss_info_dassert(GWBUF_IS_TYPE_UNDEFINED(buffer), "Buffer type should be undefined"); ss_dfprintf(stderr, "\t..done\nSet a hint for the buffer"); - char* name = strdup("name"); - hint = hint_create_parameter(NULL, name, "value"); - free(name); + hint = hint_create_parameter(NULL, "name", "value"); gwbuf_add_hint(buffer, hint); ss_info_dassert(hint == buffer->hint, "Buffer should point to first and only hint"); ss_dfprintf(stderr, "\t..done\nSet a property for the buffer"); @@ -157,7 +155,24 @@ int buflen; ss_info_dassert(100000 == buflen, "Incorrect buffer size"); ss_info_dassert(buffer == extra, "The buffer pointer should now point to the extra buffer"); ss_dfprintf(stderr, "\t..done\n"); - + + /** gwbuf_clone_all test */ + size_t headsize = 10; + GWBUF* head = gwbuf_alloc(headsize); + size_t tailsize = 20; + GWBUF* tail = gwbuf_alloc(tailsize); + + ss_info_dassert(head && tail, "Head and tail buffers should both be non-NULL"); + GWBUF* append = gwbuf_append(head, tail); + ss_info_dassert(append == head, "gwbuf_append should return head"); + ss_info_dassert(append->next == tail, "After append tail should be in the next pointer of head"); + ss_info_dassert(append->tail == tail, "After append tail should be in the tail pointer of head"); + GWBUF* all_clones = gwbuf_clone_all(head); + ss_info_dassert(all_clones && all_clones->next, "Cloning all should work"); + ss_info_dassert(GWBUF_LENGTH(all_clones) == headsize, "First buffer should be 10 bytes"); + ss_info_dassert(GWBUF_LENGTH(all_clones->next) == tailsize, "Second buffer should be 20 bytes"); + ss_info_dassert(gwbuf_length(all_clones) == headsize + tailsize, "Total buffer length should be 30 bytes"); + return 0; } 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/testmaxscalepcre2.c b/server/core/test/testmaxscalepcre2.c new file mode 100644 index 000000000..c05b070e0 --- /dev/null +++ b/server/core/test/testmaxscalepcre2.c @@ -0,0 +1,111 @@ +/* + * This file is distributed as part of 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 2015 + */ + +/** + * + * @verbatim + * Revision History + * + * Date Who Description + * 05-11-2015 Markus Makela Initial implementation + * + * @endverbatim + */ + +// To ensure that ss_info_assert asserts also when builing in non-debug mode. +#ifndef SS_DEBUG +#define SS_DEBUG +#endif +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include +#include +#include +#include +#include + +#define test_assert(a, b) if(!(a)){fprintf(stderr, b);return 1;} + +/** + * Test PCRE2 regular expression simple matching function test + */ +static int test1() +{ + int error = 0; + mxs_pcre2_result_t result = mxs_pcre2_simple_match("brown.*dog", "The quick brown fox jumps over the lazy dog", 0, &error); + test_assert(result == MXS_PCRE2_MATCH, "Pattern should match"); + error = 0; + result = mxs_pcre2_simple_match("BROWN.*DOG", "The quick brown fox jumps over the lazy dog", PCRE2_CASELESS, &error); + test_assert(result == MXS_PCRE2_MATCH, "Pattern should match with PCRE2_CASELESS option"); + error = 0; + result = mxs_pcre2_simple_match("black.*dog", "The quick brown fox jumps over the lazy dog", 0, &error); + test_assert(result == MXS_PCRE2_NOMATCH && error == 0, "Pattern should not match"); + error = 0; + result = mxs_pcre2_simple_match("black.*[dog", "The quick brown fox jumps over the lazy dog", 0, &error); + test_assert(result == MXS_PCRE2_ERROR, "Pattern should not match and a failure should be retured"); + test_assert(error != 0, "Error number should be non-zero"); + return 0; +} + +/** + * Test PCRE2 string substitution + */ +static int test2() +{ + int err; + size_t erroff; + const char* pattern = "(.*)dog"; + const char* pattern2 = "(.*)duck"; + const char* good_replace = "$1cat"; + const char* bad_replace = "$6cat"; + const char* subject = "The quick brown fox jumps over the lazy dog"; + const char* expected = "The quick brown fox jumps over the lazy cat"; + + /** We'll assume malloc and the PCRE2 library works */ + pcre2_code *re = pcre2_compile((PCRE2_SPTR) pattern, PCRE2_ZERO_TERMINATED, + 0, &err, &erroff, NULL); + pcre2_code *re2 = pcre2_compile((PCRE2_SPTR) pattern2, PCRE2_ZERO_TERMINATED, + 0, &err, &erroff, NULL); + size_t size = 1000; + char* dest = malloc(size); + mxs_pcre2_result_t result = mxs_pcre2_substitute(re, subject, good_replace, &dest, &size); + + test_assert(result == MXS_PCRE2_MATCH, "Substitution should substitute"); + test_assert(strcmp(dest, expected) == 0, "Replaced text should match expected text"); + + result = mxs_pcre2_substitute(re2, subject, good_replace, &dest, &size); + test_assert(result == MXS_PCRE2_NOMATCH, "Non-matching substitution should not substitute"); + + result = mxs_pcre2_substitute(re, subject, bad_replace, &dest, &size); + test_assert(result == MXS_PCRE2_ERROR, "Bad substitution should return an error"); + return 0; +} + +int main(int argc, char **argv) +{ + int result = 0; + + result += test1(); + result += test2(); + + return result; +} + + 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 5fa7e0436..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,39 +73,17 @@ 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\nTiming out a session."); - - service->conn_timeout = 1; - result = serviceStart(service); - skygw_log_sync_all(); - ss_info_dassert(0 != result, "Start should succeed"); - serviceStop(service); - skygw_log_sync_all(); - ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed"); - - if((dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER)) == NULL) - return 1; - ss_info_dassert(dcb != NULL, "DCB allocation failed"); - - session = session_alloc(service,dcb); - ss_info_dassert(session != NULL, "Session allocation failed"); - dcb->state = DCB_STATE_POLLING; - sleep(15); - - ss_info_dassert(dcb->state != DCB_STATE_POLLING, "Session timeout failed"); - ss_dfprintf(stderr, "\t..done\nStopping Service."); serviceStop(service); ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed"); diff --git a/server/core/test/testsession.c b/server/core/test/testsession.c index 4d8d4cc04..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 @@ -59,7 +66,7 @@ int result; sleep(10); poll_shutdown(); ss_dfprintf(stderr, "\t..done\nTidy up."); - dcb_free(dcb); + dcb_close(dcb); ss_dfprintf(stderr, "\t..done\n"); return 0; 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/core/users.c b/server/core/users.c index 051524328..9910fb654 100644 --- a/server/core/users.c +++ b/server/core/users.c @@ -49,13 +49,13 @@ USERS *rval; if ((rval = calloc(1, sizeof(USERS))) == NULL) { - skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__); + MXS_ERROR("[%s:%d]: Memory allocation failed.", __FUNCTION__, __LINE__); return NULL; } if ((rval->data = hashtable_alloc(USERS_HASHTABLE_DEFAULT_SIZE, simple_str_hash, strcmp)) == NULL) { - skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__); + MXS_ERROR("[%s:%d]: Memory allocation failed.", __FUNCTION__, __LINE__); free(rval); return NULL; } @@ -75,7 +75,7 @@ users_free(USERS *users) { if(users == NULL) { - skygw_log_write(LE,"[%s:%d] Error: NULL parameter.",__FUNCTION__,__LINE__); + MXS_ERROR("[%s:%d]: NULL parameter.", __FUNCTION__, __LINE__); return; } diff --git a/server/core/utils.c b/server/core/utils.c index 8b8f4d55f..0718770c6 100644 --- a/server/core/utils.c +++ b/server/core/utils.c @@ -43,11 +43,7 @@ #include #include #include - -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; +#include /* used in the hex2bin function */ #define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\ @@ -69,23 +65,19 @@ int setnonblocking(int fd) { if ((fl = fcntl(fd, F_GETFL, 0)) == -1) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Can't GET fcntl for %i, errno = %d, %s.", - fd, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Can't GET fcntl for %i, errno = %d, %s.", + fd, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); return 1; } if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Can't SET fcntl for %i, errno = %d, %s", - fd, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Can't SET fcntl for %i, errno = %d, %s", + fd, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); return 1; } return 0; @@ -102,7 +94,7 @@ char *gw_strend(register const char *s) { * generate a random char *****************************************/ static char gw_randomchar() { - return (char)((rand() % 78) + 30); + return (char)((random_jkiss() % 78) + 30); } /***************************************** @@ -112,7 +104,6 @@ static char gw_randomchar() { int gw_generate_random_str(char *output, int len) { int i; - srand(time(0L)); for ( i = 0; i < len; ++i ) { output[i] = gw_randomchar(); diff --git a/server/include/buffer.h b/server/include/buffer.h index c0555bae4..0e1b34246 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -43,6 +43,7 @@ * 03/10/2014 Martin Brampton Pointer arithmetic standard conformity * Add more buffer handling macros * Add gwbuf_rtrim (handle chains) + * 09/11/2014 Martin Brampton Add dprintAllBuffers (conditional compilation) * * @endverbatim */ @@ -52,7 +53,6 @@ #include #include - EXTERN_C_BLOCK_BEGIN /** @@ -168,6 +168,9 @@ typedef struct gwbuf { /*< Consume a number of bytes in the buffer */ #define GWBUF_CONSUME(b, bytes) ((b)->start = bytes > ((char *)(b)->end - (char *)(b)->start) ? (b)->end : (void *)((char *)(b)->start + (bytes))); +/*< Check if a given pointer is within the buffer */ +#define GWBUF_POINTER_IN_BUFFER (ptr, b) ((char *)(ptr) >= (char *)(b)->start && (char *)(ptr) < (char *)(b)->end) + /*< Consume a complete buffer */ #define GWBUF_CONSUME_ALL(b) gwbuf_consume((b), GWBUF_LENGTH((b))) @@ -199,6 +202,9 @@ void gwbuf_add_buffer_object(GWBUF* buf, void* data, void (*donefun_fp)(void *)); void* gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id); +#if defined(BUFFER_TRACE) +extern void dprintAllBuffers(void *pdcb); +#endif EXTERN_C_BLOCK_END diff --git a/server/include/dbusers.h b/server/include/dbusers.h index ab7ff1638..17e5b97ef 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -62,6 +62,7 @@ typedef struct mysql_user_host_key { struct sockaddr_in ipv4; int netmask; char *resource; + char hostname[MYSQL_HOST_MAXLEN + 1]; } MYSQL_USER_HOST; extern int load_mysql_users(SERVICE *service); diff --git a/server/include/dcb.h b/server/include/dcb.h index 7339286a1..22e8d21aa 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -179,7 +179,6 @@ typedef enum { DCB_STATE_DISCONNECTED, /*< The socket is now closed */ DCB_STATE_NOPOLLING, /*< Removed from poll mask */ DCB_STATE_ZOMBIE, /*< DCB is no longer active, waiting to free it */ - DCB_STATE_FREED /*< Memory freed */ } dcb_state_t; typedef enum { @@ -227,6 +226,7 @@ typedef struct dcb_callback { typedef struct dcb { skygw_chk_t dcb_chk_top; bool dcb_errhandle_called; /*< this can be called only once */ + bool dcb_is_zombie; /**< Whether the DCB is in the zombie list */ dcb_role_t dcb_role; SPINLOCK dcb_initlock; DCBEVENTQ evq; /**< The event queue for this DCB */ @@ -339,6 +339,7 @@ int dcb_count_by_usage(DCB_USAGE); /* Return counts of DCBs */ int dcb_persistent_clean_count(DCB *, bool); /* Clean persistent and return count */ void dcb_call_foreach (struct server* server, DCB_REASON reason); +void dcb_hangup_foreach (struct server* server); size_t dcb_get_session_id(DCB* dcb); bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs); char *dcb_role_name(DCB *); /* Return the name of a role */ 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/filter.h b/server/include/filter.h index ad0b9a21a..853e1628c 100644 --- a/server/include/filter.h +++ b/server/include/filter.h @@ -107,6 +107,7 @@ typedef struct filter_def { FILTER_DEF *filter_alloc(char *, char *); void filter_free(FILTER_DEF *); +bool filter_load(FILTER_DEF* filter); FILTER_DEF *filter_find(char *); void filterAddOption(FILTER_DEF *, char *); void filterAddParameter(FILTER_DEF *, char *, char *); diff --git a/server/include/gwbitmask.h b/server/include/gwbitmask.h index baeb1ef77..26d5e3621 100644 --- a/server/include/gwbitmask.h +++ b/server/include/gwbitmask.h @@ -27,6 +27,7 @@ * * Date Who Description * 28/06/13 Mark Riddoch Initial implementation + * 17/10/15 Martin Brampton Add bitmask_render_readable * * @endverbatim */ @@ -51,4 +52,6 @@ extern void bitmask_clear(GWBITMASK *, int); extern int bitmask_isset(GWBITMASK *, int); extern int bitmask_isallclear(GWBITMASK *); extern void bitmask_copy(GWBITMASK *, GWBITMASK *); +extern char *bitmask_render_readable(GWBITMASK *bitmask); + #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/poll.h b/server/include/poll.h index e778c580c..f054667d4 100644 --- a/server/include/poll.h +++ b/server/include/poll.h @@ -29,6 +29,7 @@ * * Date Who Description * 19/06/13 Mark Riddoch Initial implementation + * 17/10/15 Martin Brampton Declare fake event functions * * @endverbatim */ @@ -60,9 +61,11 @@ extern void poll_set_maxwait(unsigned int); extern void poll_set_nonblocking_polls(unsigned int); extern void dprintPollStats(DCB *); extern void dShowThreads(DCB *dcb); -void poll_add_epollin_event_to_dcb(DCB* dcb, GWBUF* buf); +void poll_add_epollin_event_to_dcb(DCB* dcb, GWBUF* buf); extern void dShowEventQ(DCB *dcb); extern void dShowEventStats(DCB *dcb); -extern int poll_get_stat(POLL_STAT stat); +extern int poll_get_stat(POLL_STAT stat); extern RESULTSET *eventTimesGetList(); +extern void poll_fake_hangup_event(DCB *dcb); +extern void poll_fake_write_event(DCB *dcb); #endif diff --git a/server/include/random_jkiss.h b/server/include/random_jkiss.h new file mode 100644 index 000000000..21d638c82 --- /dev/null +++ b/server/include/random_jkiss.h @@ -0,0 +1,22 @@ +/* + * File: random_jkiss.h + * Author: mbrampton + * + * Created on 26 August 2015, 15:34 + */ + +#ifndef RANDOM_JKISS_H +#define RANDOM_JKISS_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned int random_jkiss(void); + +#ifdef __cplusplus +} +#endif + +#endif /* RANDOM_H */ + diff --git a/server/include/router.h b/server/include/router.h index 3815253bf..655075be5 100644 --- a/server/include/router.h +++ b/server/include/router.h @@ -30,6 +30,7 @@ * 15/07/2013 Massimiliano Pinto Added clientReply entry point * 16/07/2013 Massimiliano Pinto Added router commands values * 22/10/2013 Massimiliano Pinto Added router errorReply entry point + * 27/10/2015 Martin Brampton Add RCAP_TYPE_NO_RSESSION * */ #include @@ -45,8 +46,7 @@ typedef void *ROUTER; typedef enum error_action { ERRACT_NEW_CONNECTION = 0x001, - ERRACT_REPLY_CLIENT = 0x002, - ERRACT_RESET = 0x004 + ERRACT_REPLY_CLIENT = 0x002 } error_action_t; /** @@ -86,7 +86,7 @@ typedef struct router_object { DCB* backend_dcb, error_action_t action, bool* succp); - uint8_t (*getCapabilities)(ROUTER *instance, void* router_session); + int (*getCapabilities)(); } ROUTER_OBJECT; /** @@ -101,8 +101,9 @@ typedef struct router_object { */ typedef enum router_capability_t { RCAP_TYPE_UNDEFINED = 0x00, - RCAP_TYPE_STMT_INPUT = 0x01, /*< statement per buffer */ - RCAP_TYPE_PACKET_INPUT = 0x02 /*< data as it was read from DCB */ + RCAP_TYPE_STMT_INPUT = 0x01, /*< statement per buffer */ + RCAP_TYPE_PACKET_INPUT = 0x02, /*< data as it was read from DCB */ + RCAP_TYPE_NO_RSESSION = 0x04 /*< router does not use router sessions */ } router_capability_t; 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/service.h b/server/include/service.h index 7064468df..da72a781a 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -192,7 +192,7 @@ typedef struct service { char* ssl_ca_cert; /*< SSL CA certificate */ bool ssl_init_done; /*< If SSL has already been initialized for this service */ bool retry_start; /*< If starting of the service should be retried later */ - + bool log_auth_warnings; /*< Log authentication failures and warnings */ } SERVICE; typedef enum count_spec_t {COUNT_NONE=0, COUNT_ATLEAST, COUNT_EXACT, COUNT_ATMOST} count_spec_t; diff --git a/server/include/session.h b/server/include/session.h index 7a9381507..edb4af1d4 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -64,7 +64,8 @@ typedef enum { SESSION_STATE_LISTENER, /*< for listener session */ SESSION_STATE_LISTENER_STOPPED, /*< for listener session */ SESSION_STATE_TO_BE_FREED, /*< ready to be freed as soon as there are no references */ - SESSION_STATE_FREE /*< for all sessions */ + SESSION_STATE_FREE, /*< for all sessions */ + SESSION_STATE_DUMMY /*< dummy session for consistency */ } session_state_t; /** @@ -162,6 +163,7 @@ typedef struct session { SESSION *get_all_sessions(); SESSION *session_alloc(struct service *, struct dcb *); +SESSION *session_set_dummy(struct dcb *); bool session_free(SESSION *); int session_isvalid(SESSION *); int session_reply(void *inst, void *session, GWBUF *data); diff --git a/server/include/test_utils.h b/server/include/test_utils.h index 0ed271225..53bcbb379 100644 --- a/server/include/test_utils.h +++ b/server/include/test_utils.h @@ -8,19 +8,11 @@ void init_test_env(char *path) { - int argc = 5; - - char* argv[] = - { - "log_manager", - "-l", - "LOGFILE_ERROR", - "-j", - path? path:TEST_LOG_DIR, - NULL - }; + int argc = 3; - skygw_logmanager_init(argc,argv); + const char* logdir = path ? path : TEST_LOG_DIR; + + mxs_log_init(NULL, logdir, LOG_TARGET_DEFAULT); poll_init(); hkinit(); } diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index 34f77c7d8..2a79547ef 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -8,6 +8,7 @@ endif() add_library(regexfilter SHARED regexfilter.c) target_link_libraries(regexfilter log_manager ${PCRE2_LIBRARIES}) +add_dependencies(regexfilter pcre2) install(TARGETS regexfilter DESTINATION ${MAXSCALE_LIBDIR}) add_library(testfilter SHARED testfilter.c) diff --git a/server/modules/filter/dbfwfilter.c b/server/modules/filter/dbfwfilter.c index 8f64139c8..7334011a4 100644 --- a/server/modules/filter/dbfwfilter.c +++ b/server/modules/filter/dbfwfilter.c @@ -514,7 +514,7 @@ static TIMERANGE* parse_time(const char* str) } else { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: malloc returned NULL."); + MXS_ERROR("dbfwfilter: malloc returned NULL."); } } } @@ -596,7 +596,7 @@ RULE* find_rule(char* tok, FW_INSTANCE* instance) } rlist = rlist->next; } - skygw_log_write(LOGFILE_ERROR, "Error : Rule not found: %s",tok); + MXS_ERROR("Rule not found: %s",tok); return NULL; } @@ -611,7 +611,7 @@ void add_users(char* rule, FW_INSTANCE* instance) STRLINK* link = calloc(1,sizeof(STRLINK)); if(link == NULL){ - skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed"); + MXS_ERROR("Memory allocation failed"); return; } link->next = instance->userstrings; @@ -644,9 +644,9 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if((userptr == NULL || ruleptr == NULL || modeptr == NULL)|| (userptr > modeptr || userptr > ruleptr || modeptr > ruleptr)) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig); - rval = false; - goto parse_err; + MXS_ERROR("dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig); + rval = false; + goto parse_err; } *modeptr++ = '\0'; @@ -656,7 +656,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig); rval = false; goto parse_err; } @@ -665,7 +665,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) tok = strtok_r(NULL," ",&saveptr); if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, missing keyword after 'match': %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, missing keyword after 'match': %s",orig); rval = false; goto parse_err; } @@ -677,14 +677,14 @@ bool link_rules(char* orig, FW_INSTANCE* instance) match_any = false; strict = true; }else{ - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, 'match' was not followed by correct keyword: %s",orig); - rval = false; - goto parse_err; + MXS_ERROR("dbfwfilter: Rule syntax incorrect, 'match' was not followed by correct keyword: %s",orig); + rval = false; + goto parse_err; } } else { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, bad token: %s",tok); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, bad token: %s",tok); rval = false; goto parse_err; } @@ -693,7 +693,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if(tok != NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, extra token found after 'match' keyword: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, extra token found after 'match' keyword: %s",orig); rval = false; goto parse_err; } @@ -702,7 +702,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules given: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, no rules given: %s",orig); rval = false; goto parse_err; } @@ -711,7 +711,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules given: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, no rules given: %s",orig); rval = false; goto parse_err; } @@ -730,7 +730,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) } else { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, could not find rule '%s'.",tok); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, could not find rule '%s'.",tok); rval = false; goto parse_err; } @@ -747,14 +747,14 @@ bool link_rules(char* orig, FW_INSTANCE* instance) if(userptr == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no users given: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, no users given: %s",orig); rval = false; goto parse_err; } if(rulelist == NULL) { - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules found: %s",orig); + MXS_ERROR("dbfwfilter: Rule syntax incorrect, no rules found: %s",orig); rval = false; goto parse_err; } @@ -770,7 +770,7 @@ bool link_rules(char* orig, FW_INSTANCE* instance) user = (USER*)calloc(1,sizeof(USER)); if(user == NULL){ - skygw_log_write(LOGFILE_ERROR,"Error: dbfwfilter: failed to allocate memory when parsing rules."); + MXS_ERROR("dbfwfilter: failed to allocate memory when parsing rules."); rval = false; goto parse_err; } @@ -858,7 +858,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, no rule: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, no rule: %s",rule); rval = false; goto retblock; } @@ -872,7 +872,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, incomplete rule: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, incomplete rule: %s",rule); rval = false; goto retblock; } @@ -883,7 +883,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(ruledef == NULL) { - skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed."); + MXS_ERROR("Memory allocation failed."); rval = false; goto retblock; } @@ -893,7 +893,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(rlist == NULL) { free(ruledef); - skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed."); + MXS_ERROR("Memory allocation failed."); rval = false; goto retblock; } @@ -914,7 +914,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) } else { - skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule '%s': %s",rule,tok); + MXS_ERROR("Unknown token in rule '%s': %s",rule,tok); rval = false; goto retblock; } @@ -923,7 +923,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, no allow or deny: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, no allow or deny: %s",rule); rval = false; goto retblock; } @@ -948,7 +948,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { if(req_defined) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, Multiple non-optional rules: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, Multiple non-optional rules: %s",rule); rval = false; goto retblock; } @@ -985,7 +985,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { if(at_def) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'at_times' tokens: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, multiple 'at_times' tokens: %s",rule); rval = false; goto retblock; } @@ -997,7 +997,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) break; if(!check_time(tok)) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, malformed time definition: %s",tok); + MXS_ERROR("dbfwfilter: Rule parsing failed, malformed time definition: %s",tok); rval = false; goto retblock; } @@ -1006,7 +1006,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tmp == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, unexpected characters after time definition."); + MXS_ERROR("dbfwfilter: Rule parsing failed, unexpected characters after time definition."); rval = false; tr_free(tr); goto retblock; @@ -1038,14 +1038,14 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, No regex string."); + MXS_ERROR("dbfwfilter: Rule parsing failed, No regex string."); rval = false; goto retblock; } if(*tok != '\'' && *tok != '\"') { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, regex string not quoted."); + MXS_ERROR("dbfwfilter: Rule parsing failed, regex string not quoted."); rval = false; goto retblock; } @@ -1079,7 +1079,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(n_char >= 2048) { - skygw_log_write_flush(LOGFILE_ERROR, "dbfwfilter: Failed to parse rule, regular expression length is over 2048 characters."); + MXS_ERROR("dbfwfilter: Failed to parse rule, regular expression length is over 2048 characters."); rval = false; goto retblock; } @@ -1087,14 +1087,14 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) str = calloc(((tok - start) + 1),sizeof(char)); if(str == NULL) { - skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL."); + MXS_ERROR("Fatal Error: malloc returned NULL."); rval = false; goto retblock; } re = (regex_t*)malloc(sizeof(regex_t)); if(re == NULL){ - skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL."); + MXS_ERROR("Fatal Error: malloc returned NULL."); rval = false; free(str); goto retblock; @@ -1103,7 +1103,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) memcpy(str, start, (tok-start)); if(regcomp(re, str,REG_NOSUB|instance->regflags)){ - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Invalid regular expression '%s'.", str); + MXS_ERROR("dbfwfilter: Invalid regular expression '%s'.", str); rval = false; free(re); goto retblock; @@ -1130,7 +1130,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); + MXS_ERROR("dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); goto retblock; } @@ -1140,14 +1140,14 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); + MXS_ERROR("dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); goto retblock; } if(qs->limit < 1){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad query amount: %s", tok); + MXS_ERROR("dbfwfilter: Bad query amount: %s", tok); goto retblock; } @@ -1157,7 +1157,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); + MXS_ERROR("dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); goto retblock; } @@ -1167,14 +1167,14 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); + MXS_ERROR("dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); goto retblock; } if(qs->period < 1){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad time period: %s", tok); + MXS_ERROR("dbfwfilter: Bad time period: %s", tok); goto retblock; } @@ -1184,7 +1184,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); + MXS_ERROR("dbfwfilter: Missing parameter in limit_queries: '%s'.", rule); goto retblock; } qs->cooldown = strtod(tok,&errptr); @@ -1193,14 +1193,14 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); + MXS_ERROR("dbfwfilter: Rule parsing failed, not a number: '%s'.", tok); goto retblock; } if(qs->cooldown < 1){ free(qs); rval = false; - skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Bad blocking period: %s", tok); + MXS_ERROR("dbfwfilter: Bad blocking period: %s", tok); goto retblock; } @@ -1216,7 +1216,7 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) { if(oq_def) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, multiple 'on_queries' tokens: %s",rule); + MXS_ERROR("dbfwfilter: Rule parsing failed, multiple 'on_queries' tokens: %s",rule); rval = false; goto retblock; } @@ -1225,28 +1225,22 @@ bool parse_rule(char* rule, FW_INSTANCE* instance) if(tok == NULL) { - skygw_log_write(LOGFILE_ERROR, - "dbfwfilter: Missing parameter for 'on_queries'."); + MXS_ERROR("dbfwfilter: Missing parameter for 'on_queries'."); rval = false; goto retblock; } if(!parse_querytypes(tok,ruledef)){ - skygw_log_write(LOGFILE_ERROR, - "dbfwfilter: Invalid query type" - "requirements: %s." - ,tok); + MXS_ERROR("dbfwfilter: Invalid query type requirements: %s.", tok); rval = false; goto retblock; } } else { - skygw_log_write(LOGFILE_ERROR, - "dbfwfilter: Unknown rule type: %s" - ,tok); - rval = false; - goto retblock; + MXS_ERROR("dbfwfilter: Unknown rule type: %s", tok); + rval = false; + goto retblock; } tok = strtok_r(NULL," ,",&saveptr); } @@ -1298,16 +1292,16 @@ createInstance(char **options, FILTER_PARAMETER **params) if ((my_instance = calloc(1, sizeof(FW_INSTANCE))) == NULL || (my_instance->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){ - skygw_log_write(LOGFILE_ERROR, "Memory allocation for firewall filter failed."); - return NULL; + MXS_ERROR("Memory allocation for firewall filter failed."); + return NULL; } spinlock_init(my_instance->lock); if((ht = hashtable_alloc(100, hashkeyfun, hashcmpfun)) == NULL){ - skygw_log_write(LOGFILE_ERROR, "Unable to allocate hashtable."); - free(my_instance); - return NULL; + MXS_ERROR("Unable to allocate hashtable."); + free(my_instance); + return NULL; } hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,huserfree); @@ -1338,15 +1332,15 @@ createInstance(char **options, FILTER_PARAMETER **params) if(filename == NULL) { - skygw_log_write(LOGFILE_ERROR, "Unable to find rule file for firewall filter. Please provide the path with" - " rules="); + MXS_ERROR("Unable to find rule file for firewall filter. Please provide the path with" + " rules="); hashtable_free(my_instance->htable); free(my_instance); return NULL; } if((file = fopen(filename,"rb")) == NULL ){ - skygw_log_write(LOGFILE_ERROR, "Error while opening rule file for firewall filter."); + MXS_ERROR("Error while opening rule file for firewall filter."); hashtable_free(my_instance->htable); free(my_instance); free(filename); @@ -1361,7 +1355,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if(fgets(buffer,2048,file) == NULL){ if(ferror(file)){ - skygw_log_write(LOGFILE_ERROR, "Error while reading rule file for firewall filter."); + MXS_ERROR("Error while reading rule file for firewall filter."); fclose(file); hashtable_free(my_instance->htable); free(my_instance); @@ -1394,7 +1388,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if(file_empty) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: File is empty: %s"); + MXS_ERROR("dbfwfilter: File is empty: %s", filename); free(filename); err = true; goto retblock; @@ -1408,7 +1402,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if(ptr == NULL) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: No 'users' line found."); + MXS_ERROR("dbfwfilter: No 'users' line found."); err = true; goto retblock; } @@ -1417,7 +1411,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if(!link_rules(ptr->value,my_instance)) { - skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Failed to parse rule: %s",ptr->value); + MXS_ERROR("dbfwfilter: Failed to parse rule: %s",ptr->value); err = true; } tmp = ptr; @@ -1525,7 +1519,7 @@ GWBUF* gen_dummy_error(FW_SESSION* session, char* msg) session->session->data == NULL || session->session->client == NULL) { - skygw_log_write_flush(LOGFILE_ERROR, "Error : Firewall filter session missing data."); + MXS_ERROR("Firewall filter session missing data."); return NULL; } @@ -1535,8 +1529,8 @@ GWBUF* gen_dummy_error(FW_SESSION* session, char* msg) errmsg = (char*)malloc((512 + errlen)*sizeof(char)); if(errmsg == NULL){ - skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: Memory allocation failed."); - return NULL; + MXS_ERROR("Memory allocation failed."); + return NULL; } @@ -1673,7 +1667,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue switch(rulelist->rule->type){ case RT_UNDEFINED: - skygw_log_write_flush(LOGFILE_ERROR, "Error: Undefined rule type found."); + MXS_ERROR("Undefined rule type found."); break; case RT_REGEX: @@ -1684,7 +1678,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue if(!rulelist->rule->allow){ msg = strdup("Permission denied, query matched regular expression."); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': regex matched on query",rulelist->rule->name); + MXS_INFO("dbfwfilter: rule '%s': regex matched on query",rulelist->rule->name); goto queryresolved; }else{ break; @@ -1697,7 +1691,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue if(!rulelist->rule->allow){ matches = true; msg = strdup("Permission denied at this time."); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now)); + MXS_INFO("dbfwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now)); goto queryresolved; }else{ break; @@ -1724,7 +1718,8 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue if(!rulelist->rule->allow) { sprintf(emsg,"Permission denied to column '%s'.",strln->value); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value); + MXS_INFO("dbfwfilter: rule '%s': query targets forbidden column: %s", + rulelist->rule->name,strln->value); msg = strdup(emsg); goto queryresolved; } @@ -1755,7 +1750,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue matches = true; msg = strdup("Usage of wildcard denied."); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name); + MXS_INFO("dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name); goto queryresolved; } free(where); @@ -1808,7 +1803,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered); sprintf(emsg,"Queries denied for %f seconds",blocked_for); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for); + MXS_INFO("dbfwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for); msg = strdup(emsg); matches = true; @@ -1828,8 +1823,8 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue matches = true; queryspeed->active = true; - skygw_log_write(LOGFILE_TRACE, - "dbfwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user for %f seconds.", + MXS_INFO("dbfwfilter: rule '%s': query limit triggered (%d queries in %f seconds), " + "denying queries from user for %f seconds.", rulelist->rule->name, queryspeed->limit, queryspeed->period, @@ -1859,8 +1854,8 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue { matches = true; msg = strdup("Required WHERE/HAVING clause is missing."); - skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query has no where/having clause, query is denied.", - rulelist->rule->name); + MXS_INFO("dbfwfilter: rule '%s': query has no where/having clause, query is denied.", + rulelist->rule->name); } break; @@ -2213,7 +2208,7 @@ int main(int argc, char** argv) NULL }; - skygw_logmanager_init(argc_,argv_); + mxs_log_init(argc_,argv_); init_test_env(home); @@ -2231,7 +2226,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/hint/hintparser.c b/server/modules/filter/hint/hintparser.c index abf2fb141..f43c4caee 100644 --- a/server/modules/filter/hint/hintparser.c +++ b/server/modules/filter/hint/hintparser.c @@ -25,11 +25,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * hintparser.c - Find any comment in the SQL packet and look for MAXSCALE * hints in that comment. @@ -271,18 +266,10 @@ HINT_MODE mode = HM_EXECUTE; break; default: /* Error: expected hint, name or STOP */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " - "'route', 'stop' or hint name instead of " - "'%s'. Hint ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "'route', 'stop' or hint name instead of " - "'%s'. Hint ignored.", - token_get_keyword(tok)))); + MXS_ERROR("Syntax error in hint. Expected " + "'route', 'stop' or hint name instead of " + "'%s'. Hint ignored.", + token_get_keyword(tok)); token_free(tok); goto retblock; } @@ -291,16 +278,9 @@ HINT_MODE mode = HM_EXECUTE; if (tok->token != TOK_TO) { /* Error, expect TO */; - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " + MXS_ERROR("Syntax error in hint. Expected " "'to' instead of '%s'. Hint ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "'to' instead of '%s'. Hint ignored.", - token_get_keyword(tok)))); + token_get_keyword(tok)); token_free(tok); goto retblock; } @@ -322,18 +302,10 @@ HINT_MODE mode = HM_EXECUTE; break; default: /* Error expected MASTER, SLAVE or SERVER */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " + MXS_ERROR("Syntax error in hint. Expected " "'master', 'slave', or 'server' instead " "of '%s'. Hint ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "'master', 'slave', or 'server' instead " - "of '%s'. Hint ignored.", - token_get_keyword(tok)))); + token_get_keyword(tok)); token_free(tok); goto retblock; @@ -348,18 +320,10 @@ HINT_MODE mode = HM_EXECUTE; else { /* Error: Expected server name */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " + MXS_ERROR("Syntax error in hint. Expected " "server name instead of '%s'. Hint " "ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "server name instead of '%s'. Hint " - "ignored.", - token_get_keyword(tok)))); + token_get_keyword(tok)); token_free(tok); goto retblock; } @@ -383,18 +347,10 @@ HINT_MODE mode = HM_EXECUTE; break; default: /* Error, token tok->value not expected */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " - "'=', 'prepare', or 'start' instead of " - "'%s'. Hint ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "'=', 'prepare', or 'start' instead of " - "'%s'. Hint ignored.", - token_get_keyword(tok)))); + MXS_ERROR("Syntax error in hint. Expected " + "'=', 'prepare', or 'start' instead of " + "'%s'. Hint ignored.", + token_get_keyword(tok)); token_free(tok); goto retblock; } @@ -418,18 +374,10 @@ HINT_MODE mode = HM_EXECUTE; break; default: /* Error, token tok->value not expected */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Syntax error in hint. Expected " + MXS_ERROR("Syntax error in hint. Expected " "'route' or hint name instead of " "'%s'. Hint ignored.", - token_get_keyword(tok)))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Syntax error in hint. Expected " - "'route' or hint name instead of " - "'%s'. Hint ignored.", - token_get_keyword(tok)))); + token_get_keyword(tok)); token_free(tok); goto retblock; } diff --git a/server/modules/filter/mqfilter.c b/server/modules/filter/mqfilter.c index 7d78f20ea..1587240bd 100644 --- a/server/modules/filter/mqfilter.c +++ b/server/modules/filter/mqfilter.c @@ -307,53 +307,45 @@ init_conn(MQ_INSTANCE *my_instance) if((my_instance->sock = amqp_ssl_socket_new(my_instance->conn)) != NULL){ if((amqp_ok = amqp_ssl_socket_set_cacert(my_instance->sock,my_instance->ssl_CA_cert)) != AMQP_STATUS_OK){ - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to set CA certificate: %s", amqp_error_string2(amqp_ok)); - goto cleanup; + MXS_ERROR("Failed to set CA certificate: %s", amqp_error_string2(amqp_ok)); + goto cleanup; } if((amqp_ok = amqp_ssl_socket_set_key(my_instance->sock, my_instance->ssl_client_cert, my_instance->ssl_client_key)) != AMQP_STATUS_OK){ - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to set client certificate and key: %s", amqp_error_string2(amqp_ok)); - goto cleanup; + MXS_ERROR("Failed to set client certificate and key: %s", amqp_error_string2(amqp_ok)); + goto cleanup; } }else{ amqp_ok = AMQP_STATUS_SSL_CONNECTION_FAILED; - skygw_log_write(LOGFILE_ERROR, - "Error : SSL socket creation failed."); + MXS_ERROR("SSL socket creation failed."); goto cleanup; } /**SSL is not used, falling back to TCP*/ }else if((my_instance->sock = amqp_tcp_socket_new(my_instance->conn)) == NULL){ - skygw_log_write(LOGFILE_ERROR, - "Error : TCP socket creation failed."); - goto cleanup; + MXS_ERROR("TCP socket creation failed."); + goto cleanup; } /**Socket creation was successful, trying to open the socket*/ if((amqp_ok = amqp_socket_open(my_instance->sock,my_instance->hostname,my_instance->port)) != AMQP_STATUS_OK){ - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to open socket: %s", amqp_error_string2(amqp_ok)); - goto cleanup; + MXS_ERROR("Failed to open socket: %s", amqp_error_string2(amqp_ok)); + goto cleanup; } amqp_rpc_reply_t reply; reply = amqp_login(my_instance->conn,my_instance->vhost,0,AMQP_DEFAULT_FRAME_SIZE,0,AMQP_SASL_METHOD_PLAIN,my_instance->username,my_instance->password); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Login to RabbitMQ server failed."); - - goto cleanup; + MXS_ERROR("Login to RabbitMQ server failed."); + goto cleanup; } amqp_channel_open(my_instance->conn,my_instance->channel); reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Channel creation failed."); - goto cleanup; + MXS_ERROR("Channel creation failed."); + goto cleanup; } amqp_exchange_declare(my_instance->conn,my_instance->channel, @@ -365,8 +357,7 @@ init_conn(MQ_INSTANCE *my_instance) reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Exchange declaration failed,trying to redeclare the exchange."); + MXS_ERROR("Exchange declaration failed,trying to redeclare the exchange."); if(reply.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION){ if(reply.reply.id == AMQP_CHANNEL_CLOSE_METHOD){ amqp_send_method(my_instance->conn,my_instance->channel,AMQP_CHANNEL_CLOSE_OK_METHOD,NULL); @@ -386,9 +377,8 @@ init_conn(MQ_INSTANCE *my_instance) reply = amqp_get_rpc_reply(my_instance->conn); } if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Exchange redeclaration failed."); - goto cleanup; + MXS_ERROR("Exchange redeclaration failed."); + goto cleanup; } } @@ -402,9 +392,8 @@ init_conn(MQ_INSTANCE *my_instance) amqp_empty_table); reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Queue declaration failed."); - goto cleanup; + MXS_ERROR("Queue declaration failed."); + goto cleanup; } @@ -415,9 +404,8 @@ init_conn(MQ_INSTANCE *my_instance) amqp_empty_table); reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to bind queue to exchange."); - goto cleanup; + MXS_ERROR("Failed to bind queue to exchange."); + goto cleanup; } } rval = 1; @@ -451,10 +439,9 @@ char** parse_optstr(char* str, char* tok, int* szstore) arr = malloc(sizeof(char*)*size); if(arr == NULL){ - skygw_log_write(LOGFILE_ERROR, - "Error : Cannot allocate enough memory."); - *szstore = 0; - return NULL; + MXS_ERROR("Cannot allocate enough memory."); + *szstore = 0; + return NULL; } *szstore = size; @@ -552,7 +539,7 @@ createInstance(char **options, FILTER_PARAMETER **params) }else if(!strcmp(arr[x],"all")){ my_instance->trgtype = TRG_ALL; }else{ - skygw_log_write(LOGFILE_ERROR,"Error: Unknown option for 'logging_trigger':%s.",arr[x]); + MXS_ERROR("Unknown option for 'logging_trigger':%s.",arr[x]); } } @@ -719,8 +706,7 @@ int declareQueue(MQ_INSTANCE *my_instance, MQ_SESSION* my_session, char* qname) reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ success = 0; - skygw_log_write(LOGFILE_ERROR, - "Error : Queue declaration failed."); + MXS_ERROR("Queue declaration failed."); } @@ -733,8 +719,7 @@ int declareQueue(MQ_INSTANCE *my_instance, MQ_SESSION* my_session, char* qname) reply = amqp_get_rpc_reply(my_instance->conn); if(reply.reply_type != AMQP_RESPONSE_NORMAL){ success = 0; - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to bind queue to exchange."); + MXS_ERROR("Failed to bind queue to exchange."); } spinlock_release(&my_instance->rconn_lock); @@ -766,8 +751,7 @@ void sendMessage(void* data) }else{ instance->rconn_intv += 5.0; - skygw_log_write(LOGFILE_ERROR, - "Error : Failed to reconnect to the MQRabbit server "); + MXS_ERROR("Failed to reconnect to the MQRabbit server "); } } err_num = instance->conn_stat; @@ -853,11 +837,10 @@ void pushMessage(MQ_INSTANCE *instance, amqp_basic_properties_t* prop, char* msg newmsg->prop = prop; }else{ - skygw_log_write(LOGFILE_ERROR, - "Error : Cannot allocate enough memory."); - free(prop); - free(msg); - return; + MXS_ERROR("Cannot allocate enough memory."); + free(prop); + free(msg); + return; } spinlock_acquire(&instance->msg_lock); @@ -1033,8 +1016,8 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) } if(!success){ - skygw_log_write(LOGFILE_ERROR,"Error: Parsing query failed."); - goto send_downstream; + MXS_ERROR("Parsing query failed."); + goto send_downstream; } if(!my_instance->log_all){ @@ -1044,7 +1027,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) } if(my_instance->trgtype == TRG_ALL){ - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_ALL"); + MXS_INFO("Trigger is TRG_ALL"); schema_ok = true; src_ok = true; obj_ok = true; @@ -1064,8 +1047,8 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(strcmp(my_instance->src_trg->user[i],sessusr) == 0) { - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SOURCE: user: %s = %s",my_instance->src_trg->user[i],sessusr); - src_ok = true; + MXS_INFO("Trigger is TRG_SOURCE: user: %s = %s",my_instance->src_trg->user[i],sessusr); + src_ok = true; break; } @@ -1082,7 +1065,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(strcmp(my_instance->src_trg->host[i],sesshost) == 0) { - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SOURCE: host: %s = %s",my_instance->src_trg->host[i],sesshost); + MXS_INFO("Trigger is TRG_SOURCE: host: %s = %s",my_instance->src_trg->host[i],sesshost); src_ok = true; break; } @@ -1119,7 +1102,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(strcmp(tmp,my_instance->shm_trg->objects[i]) == 0){ - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SCHEMA: %s = %s",tmp,my_instance->shm_trg->objects[i]); + MXS_INFO("Trigger is TRG_SCHEMA: %s = %s",tmp,my_instance->shm_trg->objects[i]); schema_ok = true; break; @@ -1138,7 +1121,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(strcmp(my_session->db,my_instance->shm_trg->objects[i]) == 0){ - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_SCHEMA: %s = %s",my_session->db,my_instance->shm_trg->objects[i]); + MXS_INFO("Trigger is TRG_SCHEMA: %s = %s",my_session->db,my_instance->shm_trg->objects[i]); schema_ok = true; break; @@ -1178,7 +1161,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(!strcmp(tbnm,my_instance->obj_trg->objects[i])){ obj_ok = true; - skygw_log_write_flush(LOGFILE_TRACE,"Trigger is TRG_OBJECT: %s = %s",my_instance->obj_trg->objects[i],sesstbls[j]); + MXS_INFO("Trigger is TRG_OBJECT: %s = %s",my_instance->obj_trg->objects[i],sesstbls[j]); break; } @@ -1212,19 +1195,19 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) * Something matched the trigger, log the query */ - skygw_log_write_flush(LOGFILE_TRACE,"Routing message to: %s:%d %s as %s/%s, exchange: %s<%s> key:%s queue:%s", - my_instance->hostname,my_instance->port, - my_instance->vhost,my_instance->username, - my_instance->password,my_instance->exchange, - my_instance->exchange_type,my_instance->key, - my_instance->queue); + MXS_INFO("Routing message to: %s:%d %s as %s/%s, exchange: %s<%s> key:%s queue:%s", + my_instance->hostname,my_instance->port, + my_instance->vhost,my_instance->username, + my_instance->password,my_instance->exchange, + my_instance->exchange_type,my_instance->key, + my_instance->queue); if(my_session->uid == NULL){ my_session->uid = calloc(33,sizeof(char)); if(!my_session->uid){ - skygw_log_write(LOGFILE_ERROR,"Error : Out of memory."); + MXS_ERROR("Out of memory."); }else{ genkey(my_session->uid,32); } @@ -1257,8 +1240,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) /**Try to convert to a canonical form and use the plain query if unsuccessful*/ if((canon_q = skygw_get_canonical(queue)) == NULL){ - skygw_log_write_flush(LOGFILE_ERROR, - "Error: Cannot form canonical query."); + MXS_ERROR("Cannot form canonical query."); } } @@ -1268,8 +1250,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) int qlen = strnlen(canon_q,length) + strnlen(t_buf,128); if((combined = malloc((qlen+1)*sizeof(char))) == NULL){ - skygw_log_write_flush(LOGFILE_ERROR, - "Error: Out of memory"); + MXS_ERROR("Out of memory"); } strcpy(combined,t_buf); strncat(combined,canon_q,length); @@ -1409,8 +1390,7 @@ static int clientReply(FILTER* instance, void *session, GWBUF *reply) prop->message_id = amqp_cstring_bytes("reply"); } if(!(combined = calloc(GWBUF_LENGTH(reply) + 256,sizeof(char)))){ - skygw_log_write_flush(LOGFILE_ERROR, - "Error : Out of memory"); + MXS_ERROR("Out of memory"); } memset(t_buf,0,128); diff --git a/server/modules/filter/namedserverfilter.c b/server/modules/filter/namedserverfilter.c index 822b5eaf2..c634d8198 100644 --- a/server/modules/filter/namedserverfilter.c +++ b/server/modules/filter/namedserverfilter.c @@ -25,11 +25,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * @file namedserverfilter.c - a very simple regular expression based filter * that routes to a named server if a regular expression match is found. @@ -166,10 +161,8 @@ int i, cflags = REG_ICASE; my_instance->user = strdup(params[i]->value); else if (!filter_standard_parameter(params[i]->name)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "namedserverfilter: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("namedserverfilter: Unexpected parameter '%s'.", + params[i]->name); } } @@ -187,10 +180,8 @@ int i, cflags = REG_ICASE; } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "namedserverfilter: unsupported option '%s'.\n", - options[i]))); + MXS_ERROR("namedserverfilter: unsupported option '%s'.", + options[i]); } } } @@ -198,25 +189,22 @@ int i, cflags = REG_ICASE; if (my_instance->match == NULL || my_instance->server == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "namedserverfilter: Missing required configured" - " option. You must specify a match and server " - "option as a minimum."))); - free(my_instance); - return NULL; + MXS_ERROR("namedserverfilter: Missing required configured" + " option. You must specify a match and server " + "option as a minimum."); + free(my_instance); + return NULL; } if (regcomp(&my_instance->re, my_instance->match, my_instance->cflags)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "namedserverfilter: Invalid regular expression '%s'.\n", - my_instance->match))); - free(my_instance->match); - free(my_instance->server); - free(my_instance); - return NULL; + MXS_ERROR("namedserverfilter: Invalid regular expression '%s'.\n", + my_instance->match); + free(my_instance->match); + free(my_instance->server); + free(my_instance); + return NULL; } } return (FILTER *)my_instance; diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index e03dabf98..1b72326ea 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -52,11 +52,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - MODULE_INFO info = { MODULE_API_FILTER, MODULE_GA, @@ -210,10 +205,8 @@ int i; } else if (!filter_standard_parameter(params[i]->name)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "qlafilter: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("qlafilter: Unexpected parameter '%s'.", + params[i]->name); } } } @@ -221,26 +214,24 @@ int i; if (my_instance->match && regcomp(&my_instance->re, my_instance->match, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "qlafilter: Invalid regular expression '%s'" - " for the match parameter.\n", - my_instance->match))); - free(my_instance->match); - free(my_instance->source); - if(my_instance->filebase){ - free(my_instance->filebase); - } - free(my_instance); - return NULL; + MXS_ERROR("qlafilter: Invalid regular expression '%s'" + " for the match parameter.\n", + my_instance->match); + free(my_instance->match); + free(my_instance->source); + if(my_instance->filebase){ + free(my_instance->filebase); + } + free(my_instance); + return NULL; } if (my_instance->nomatch && regcomp(&my_instance->nore, my_instance->nomatch, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "qlafilter: Invalid regular expression '%s'" - " for the nomatch paramter.\n", - my_instance->match))); + MXS_ERROR("qlafilter: Invalid regular expression '%s'" + " for the nomatch paramter.", + my_instance->match); if (my_instance->match) regfree(&my_instance->re); free(my_instance->match); @@ -278,14 +269,12 @@ char *remote, *userName; == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Memory allocation for qla filter " - "file name failed due to %d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - free(my_session); - return NULL; + MXS_ERROR("Memory allocation for qla filter " + "file name failed due to %d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); + free(my_session); + return NULL; } my_session->active = 1; @@ -317,27 +306,23 @@ char *remote, *userName; if (my_session->fp == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Opening output file for qla " - "fileter failed due to %d, %s", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); - free(my_session->filename); - free(my_session); - my_session = NULL; + MXS_ERROR("Opening output file for qla " + "fileter failed due to %d, %s", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); + free(my_session->filename); + free(my_session); + my_session = NULL; } } } else { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Error : Memory allocation for qla filter failed due to " - "%d, %s.", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation for qla filter failed due to " + "%d, %s.", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } return my_session; } diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index c424ec666..ab35c0af2 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -27,11 +27,6 @@ #include #include "maxconfig.h" -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * @file regexfilter.c - a very simple regular expression rewrite filter. * @verbatim @@ -213,10 +208,8 @@ createInstance(char **options, FILTER_PARAMETER **params) } else if (!filter_standard_parameter(params[i]->name)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "regexfilter: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("regexfilter: Unexpected parameter '%s'.", + params[i]->name); } } @@ -234,10 +227,8 @@ createInstance(char **options, FILTER_PARAMETER **params) } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "regexfilter: unsupported option '%s'.\n", - options[i]))); + MXS_ERROR("regexfilter: unsupported option '%s'.", + options[i]); } } } @@ -246,9 +237,7 @@ createInstance(char **options, FILTER_PARAMETER **params) { if ((my_instance->logfile = fopen(logfile, "a")) == NULL) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "regexfilter: Failed to open file '%s'.\n", - logfile))); + MXS_ERROR("regexfilter: Failed to open file '%s'.", logfile); free_instance(my_instance); free(logfile); return NULL; @@ -274,9 +263,8 @@ createInstance(char **options, FILTER_PARAMETER **params) { char errbuffer[1024]; pcre2_get_error_message(errnumber, (PCRE2_UCHAR*) & errbuffer, sizeof(errbuffer)); - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error: regexfilter: Compiling regular expression '%s' failed at %d: %s\n", - my_instance->match, erroffset, errbuffer))); + MXS_ERROR("regexfilter: Compiling regular expression '%s' failed at %lu: %s", + my_instance->match, erroffset, errbuffer); free_instance(my_instance); return NULL; } @@ -284,9 +272,8 @@ createInstance(char **options, FILTER_PARAMETER **params) if((my_instance->match_data = pcre2_match_data_create_from_pattern( my_instance->re, NULL)) == NULL) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error: regexfilter: Failure to create PCRE2 matching data. " - "This is most likely caused by a lack of available memory."))); + MXS_ERROR("regexfilter: Failure to create PCRE2 matching data. " + "This is most likely caused by a lack of available memory."); free_instance(my_instance); return NULL; } @@ -516,7 +503,7 @@ void log_match(REGEX_INSTANCE* inst, char* re, char* old, char* new) } if(inst->log_trace) { - LOGIF(LT,(skygw_log_write(LT,"Match %s: [%s] -> [%s]",re,old,new))); + MXS_INFO("Match %s: [%s] -> [%s]",re,old,new); } } @@ -535,6 +522,6 @@ void log_nomatch(REGEX_INSTANCE* inst, char* re, char* old) } if(inst->log_trace) { - LOGIF(LT,(skygw_log_write(LT,"No match %s: [%s]",re,old))); + MXS_INFO("No match %s: [%s]",re,old); } } diff --git a/server/modules/filter/slavelag.c b/server/modules/filter/slavelag.c index eb6b9536d..df8b6331b 100644 --- a/server/modules/filter/slavelag.c +++ b/server/modules/filter/slavelag.c @@ -26,11 +26,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * @file slavelag.c - a very simple filter designed to send queries to the * master server after data modification has occurred. This is done to prevent @@ -184,10 +179,8 @@ int i,cflags = 0; my_instance->nomatch = strdup(params[i]->value); else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "lagfilter: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("lagfilter: Unexpected parameter '%s'.\n", + params[i]->name); } } @@ -205,10 +198,8 @@ int i,cflags = 0; } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "lagfilter: unsupported option '%s'.", - options[i]))); + MXS_ERROR("lagfilter: unsupported option '%s'.", + options[i]))); } } } @@ -217,20 +208,16 @@ int i,cflags = 0; { if(regcomp(&my_instance->re,my_instance->match,cflags)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "lagfilter: Failed to compile regex '%s'.", - my_instance->match))); + MXS_ERROR("lagfilter: Failed to compile regex '%s'.", + my_instance->match); } } if(my_instance->nomatch) { if(regcomp(&my_instance->nore,my_instance->nomatch,cflags)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "lagfilter: Failed to compile regex '%s'.", - my_instance->nomatch))); + MXS_ERROR("lagfilter: Failed to compile regex '%s'.", + my_instance->nomatch))); } } } diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index 1557166f9..880bc5c24 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -96,11 +96,6 @@ static unsigned char required_packets[] = { MYSQL_COM_CONNECT, 0 }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - MODULE_INFO info = { MODULE_API_FILTER, MODULE_GA, @@ -212,6 +207,7 @@ int route_single_query(TEE_INSTANCE* my_instance, GWBUF* buffer, GWBUF* clone); int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer); +void create_orphan(SESSION* ses); static void orphan_free(void* data) @@ -278,9 +274,9 @@ orphan_free(void* data) #ifdef SS_DEBUG if(o_stopping + o_ready > 0) - skygw_log_write(LOGFILE_DEBUG, "tee.c: %d orphans in " - "SESSION_STATE_STOPPING, %d orphans in " - "SESSION_STATE_ROUTER_READY. ", o_stopping, o_ready); + MXS_DEBUG("tee.c: %d orphans in " + "SESSION_STATE_STOPPING, %d orphans in " + "SESSION_STATE_ROUTER_READY. ", o_stopping, o_ready); #endif while(finished) @@ -301,7 +297,7 @@ orphan_free(void* data) } #ifdef SS_DEBUG - skygw_log_write(LOGFILE_DEBUG, "tee.c: %d orphans freed.", o_freed); + MXS_DEBUG("tee.c: %d orphans freed.", o_freed); #endif } @@ -362,9 +358,8 @@ int i; { if (options) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "tee: The tee filter has been passed an option, " - "this filter does not support any options.\n"))); + MXS_ERROR("tee: The tee filter has been passed an option, " + "this filter does not support any options."); } my_instance->service = NULL; my_instance->source = NULL; @@ -379,11 +374,8 @@ int i; { if ((my_instance->service = service_find(params[i]->value)) == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "tee: service '%s' " - "not found.\n", - params[i]->value))); + MXS_ERROR("tee: service '%s' not found.\n", + params[i]->value); } } else if (!strcmp(params[i]->name, "match")) @@ -400,10 +392,8 @@ int i; my_instance->userName = strdup(params[i]->value); else if (!filter_standard_parameter(params[i]->name)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "tee: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("tee: Unexpected parameter '%s'.", + params[i]->name); } } } @@ -418,29 +408,27 @@ int i; if (my_instance->match && regcomp(&my_instance->re, my_instance->match, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "tee: Invalid regular expression '%s'" - " for the match parameter.\n", - my_instance->match))); - free(my_instance->match); - free(my_instance->source); - free(my_instance); - return NULL; + MXS_ERROR("tee: Invalid regular expression '%s'" + " for the match parameter.", + my_instance->match); + free(my_instance->match); + free(my_instance->source); + free(my_instance); + return NULL; } if (my_instance->nomatch && regcomp(&my_instance->nore, my_instance->nomatch, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "tee: Invalid regular expression '%s'" - " for the nomatch paramter.\n", - my_instance->match))); - if (my_instance->match) - regfree(&my_instance->re); - free(my_instance->match); - free(my_instance->source); - free(my_instance); - return NULL; + MXS_ERROR("tee: Invalid regular expression '%s'" + " for the nomatch paramter.\n", + my_instance->match); + if (my_instance->match) + regfree(&my_instance->re); + free(my_instance->match); + free(my_instance->source); + free(my_instance); + return NULL; } } return (FILTER *)my_instance; @@ -464,11 +452,10 @@ char *remote, *userName; if (strcmp(my_instance->service->name, session->service->name) == 0) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : %s: Recursive use of tee filter in service.", - session->service->name))); - my_session = NULL; - goto retblock; + MXS_ERROR("%s: Recursive use of tee filter in service.", + session->service->name); + my_session = NULL; + goto retblock; } HASHTABLE* ht = hashtable_alloc(100,simple_str_hash,strcmp); @@ -477,11 +464,10 @@ char *remote, *userName; if(is_loop) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : %s: Recursive use of tee filter in service.", - session->service->name))); - my_session = NULL; - goto retblock; + MXS_ERROR("%s: Recursive use of tee filter in service.", + session->service->name); + my_session = NULL; + goto retblock; } if ((my_session = calloc(1, sizeof(TEE_SESSION))) != NULL) @@ -501,9 +487,7 @@ char *remote, *userName; { my_session->active = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : Tee filter is not active."))); + MXS_WARNING("Tee filter is not active."); } } userName = session_getUser(session); @@ -514,9 +498,7 @@ char *remote, *userName; { my_session->active = 0; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Warning : Tee filter is not active."))); + MXS_WARNING("Tee filter is not active."); } if (my_session->active) @@ -531,10 +513,8 @@ char *remote, *userName; freeSession(instance, (void *)my_session); my_session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Creating client DCB for Tee " - "filter failed. Terminating session."))); + MXS_ERROR("Creating client DCB for Tee " + "filter failed. Terminating session."); goto retblock; } @@ -544,11 +524,9 @@ char *remote, *userName; dcb_close(dcb); freeSession(instance, (void *)my_session); my_session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : tee: Allocating memory for " - "dummy filter definition failed." - " Terminating session."))); + MXS_ERROR("tee: Allocating memory for " + "dummy filter definition failed." + " Terminating session."); goto retblock; } @@ -557,13 +535,12 @@ char *remote, *userName; if ((ses = session_alloc(my_instance->service, dcb)) == NULL) { + filter_free(dummy); dcb_close(dcb); freeSession(instance, (void *)my_session); my_session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Creating client session for Tee " - "filter failed. Terminating session."))); + MXS_ERROR("Creating client session for Tee " + "filter failed. Terminating session."); goto retblock; } @@ -572,39 +549,25 @@ char *remote, *userName; dummy->obj = GetModuleObject(); dummy->filter = NULL; - + my_session->branch_session = ses; + my_session->branch_dcb = dcb; + my_session->dummy_filterdef = dummy; if((dummy_upstream = filterUpstream( dummy, my_session, &ses->tail)) == NULL) { - spinlock_acquire(&ses->ses_lock); - ses->state = SESSION_STATE_STOPPING; - spinlock_release(&ses->ses_lock); - - ses->service->router->closeSession( - ses->service->router_instance, - ses->router_session); - - ses->client = NULL; - dcb->session = NULL; - session_free(ses); + filter_free(dummy); + closeSession(instance,(void*)my_session); dcb_close(dcb); - freeSession(instance, (void *) my_session); - my_session = NULL; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : tee: Allocating memory for" - "dummy upstream failed." - " Terminating session."))); + free(my_session); + MXS_ERROR("tee: Allocating memory for" + "dummy upstream failed." + " Terminating session."); - goto retblock; + return NULL; } ses->tail = *dummy_upstream; - my_session->branch_session = ses; - my_session->branch_dcb = dcb; - my_session->dummy_filterdef = dummy; - MySQLProtocol* protocol = (MySQLProtocol*)session->client->protocol; my_session->use_ok = protocol->client_capabilities & (1 << 6); free(dummy_upstream); @@ -631,7 +594,7 @@ ROUTER_OBJECT *router; void *router_instance, *rsession; SESSION *bsession; #ifdef SS_DEBUG -skygw_log_write(LOGFILE_TRACE,"Tee close: %d", atomic_add(&debug_seq,1)); +MXS_INFO("Tee close: %d", atomic_add(&debug_seq,1)); #endif if (my_session->active) { @@ -664,7 +627,7 @@ skygw_log_write(LOGFILE_TRACE,"Tee close: %d", atomic_add(&debug_seq,1)); my_session->client_dcb && my_session->client_dcb->state == DCB_STATE_POLLING) { - skygw_log_write(LOGFILE_TRACE,"Tee session closed mid-query."); + MXS_INFO("Tee session closed mid-query."); GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"00000","Session closed."); my_session->client_dcb->func.write(my_session->client_dcb,errbuf); } @@ -688,7 +651,7 @@ TEE_SESSION *my_session = (TEE_SESSION *)session; SESSION* ses = my_session->branch_session; session_state_t state; #ifdef SS_DEBUG -skygw_log_write(LOGFILE_TRACE,"Tee free: %d", atomic_add(&debug_seq,1)); +MXS_INFO("Tee free: %d", atomic_add(&debug_seq,1)); #endif if (ses != NULL) { @@ -712,19 +675,7 @@ skygw_log_write(LOGFILE_TRACE,"Tee free: %d", atomic_add(&debug_seq,1)); } else if(state == SESSION_STATE_STOPPING) { - orphan_session_t* orphan; - if((orphan = malloc(sizeof(orphan_session_t))) == NULL) - { - skygw_log_write(LOGFILE_ERROR,"Error : Failed to " - "allocate memory for orphan session struct, " - "child session might leak memory."); - }else{ - orphan->session = ses; - spinlock_acquire(&orphanLock); - orphan->next = allOrphans; - allOrphans = orphan; - spinlock_release(&orphanLock); - } + create_orphan(ses); } } if (my_session->dummy_filterdef) @@ -799,9 +750,10 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) *((unsigned char*)queue->start + 4) : 1; #ifdef SS_DEBUG - skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s", - atomic_add(&debug_seq,1), - ((char*)queue->start + 5)); + int prev_debug_seq = atomic_add(&debug_seq,1); + MXS_INFO("Tee routeQuery: %d : %s", + prev_debug_seq, + ((char*)queue->start + 5)); #endif @@ -809,7 +761,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) if(!my_session->active) { - skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed."); + MXS_INFO("Tee: Received a reply when the session was closed."); gwbuf_free(queue); spinlock_release(&my_session->tee_lock); return 0; @@ -936,19 +888,20 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) int min_eof = my_session->command != 0x04 ? 2 : 1; int more_results = 0; #ifdef SS_DEBUG + int prev_debug_seq = atomic_add(&debug_seq,1); ptr = (unsigned char*) reply->start; - skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d", - instance ? "parent":"child", - my_session->active ? "open" : "closed", - PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET", - atomic_add(&debug_seq,1)); + MXS_INFO("Tee clientReply [%s] [%s] [%s]: %d", + instance ? "parent":"child", + my_session->active ? "open" : "closed", + PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET", + prev_debug_seq); #endif spinlock_acquire(&my_session->tee_lock); if(!my_session->active) { - skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed"); + MXS_INFO("Tee: Failed to return reply, session is closed"); gwbuf_free(reply); rc = 0; if(my_session->waiting[PARENT]) @@ -971,8 +924,8 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) if(complete == NULL) { /** Incomplete packet */ - skygw_log_write(LOGFILE_DEBUG,"tee.c: Incomplete packet, " - "waiting for a complete packet before forwarding."); + MXS_DEBUG("tee.c: Incomplete packet, " + "waiting for a complete packet before forwarding."); rc = 1; goto retblock; } @@ -990,7 +943,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) if(my_session->replies[branch] == 0) { - skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD"); + MXS_INFO("Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD"); /* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet. * Otherwise the reply is a result set and the amount of packets is unknown. */ @@ -1005,17 +958,16 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) more_results = (flags & 0x08) && my_session->client_multistatement; if(more_results) { - skygw_log_write(LOGFILE_TRACE, - "Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD"); + MXS_INFO("Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD"); } } } #ifdef SS_DEBUG else { - skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.", - my_session->d_id, - branch == PARENT?"parent":"child"); + MXS_DEBUG("tee.c: [%ld] Waiting for a result set from %s session.", + my_session->d_id, + branch == PARENT?"parent":"child"); } #endif } @@ -1029,9 +981,9 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) if(my_session->eof[branch] >= min_eof) { #ifdef SS_DEBUG - skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet", - my_session->d_id, - branch == PARENT?"parent":"child"); + MXS_DEBUG("tee.c [%ld] %s received last EOF packet", + my_session->d_id, + branch == PARENT?"parent":"child"); #endif my_session->waiting[branch] = more_results; if(more_results) @@ -1062,7 +1014,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) rc = 0; gwbuf_free(my_session->tee_replybuf); my_session->tee_replybuf = NULL; - skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed."); + MXS_ERROR("Tee child session was closed."); } if(mpkt) @@ -1078,7 +1030,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) { route = true; #ifdef SS_DEBUG - skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id); + MXS_DEBUG("tee.c:[%ld] Routing final packet of response set.",my_session->d_id); #endif } } @@ -1086,7 +1038,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) !my_session->waiting[CHILD]) { #ifdef SS_DEBUG - skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id); + MXS_DEBUG("tee.c:[%ld] Routing single packet response.",my_session->d_id); #endif route = true; } @@ -1095,8 +1047,8 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) if(route) { #ifdef SS_DEBUG - skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])" - " child(waiting [%s] replies[%d] eof [%d])", + MXS_DEBUG("tee.c:[%ld] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])" + " child(waiting [%s] replies[%d] eof [%d])", my_session->d_id, my_session->tee_replybuf, my_session->waiting[PARENT] ? "true":"false", @@ -1121,7 +1073,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) GWBUF* clone = clone_query(my_session->instance,my_session,buffer); reset_session_state(my_session,buffer); route_single_query(my_session->instance,my_session,buffer,clone); - LOGIF(LT,(skygw_log_write(LT,"tee: routing queued query"))); + MXS_INFO("tee: routing queued query"); } @@ -1341,9 +1293,7 @@ int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF /** Close tee session */ my_session->active = 0; rval = 0; - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Closed tee filter session: Child session in invalid state."))); + MXS_INFO("Closed tee filter session: Child session in invalid state."); gwbuf_free(clone); } } @@ -1351,9 +1301,7 @@ int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF { if (my_session->active) { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Closed tee filter session: Child session is NULL."))); + MXS_INFO("Closed tee filter session: Child session is NULL."); my_session->active = 0; rval = 0; } @@ -1379,8 +1327,8 @@ int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer) { case 0x1b: my_session->client_multistatement = *((unsigned char*) buffer->start + 5); - LOGIF(LT,(skygw_log_write(LT,"Tee: client %s multistatements", - my_session->client_multistatement ? "enabled":"disabled"))); + MXS_INFO("tee: client %s multistatements", + my_session->client_multistatement ? "enabled":"disabled"); case 0x03: case 0x16: case 0x17: @@ -1400,4 +1348,21 @@ int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer) my_session->command = command; return 1; -} \ No newline at end of file +} + +void create_orphan(SESSION* ses) +{ + orphan_session_t* orphan; + if((orphan = malloc(sizeof(orphan_session_t))) == NULL) + { + MXS_ERROR("Failed to " + "allocate memory for orphan session struct, " + "child session might leak memory."); + }else{ + orphan->session = ses; + spinlock_acquire(&orphanLock); + orphan->next = allOrphans; + allOrphans = orphan; + spinlock_release(&orphanLock); + } +} diff --git a/server/modules/filter/test/harness_common.c b/server/modules/filter/test/harness_common.c index e0326d040..12d0e40a8 100644 --- a/server/modules/filter/test/harness_common.c +++ b/server/modules/filter/test/harness_common.c @@ -15,16 +15,15 @@ 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(0,NULL); + mxs_log_init(NULL,NULL,LOG_TARGET_DEFAULT); } if(!(instance.head = calloc(1,sizeof(FILTERCHAIN)))) { printf("Error: Out of memory\n"); - skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n"); + MXS_ERROR("Out of memory\n"); return 1; } @@ -52,19 +51,13 @@ int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){ getcwd(cwd,sizeof(cwd)); sprintf(tmp,"%s",cwd); - optstr = (char**)malloc(sizeof(char*)*4); - optstr[0] = strdup("log_manager"); - optstr[1] = strdup("-j"); - optstr[2] = strdup(tmp); - optstr[3] = NULL; - skygw_logmanager_init( 3, optstr); - free(optstr); + mxs_log_init(NULL, tmp, LOG_TARGET_DEFAULT); rval = process_opts(argc,argv); if(!(instance.thrpool = malloc(instance.thrcount * sizeof(pthread_t)))){ printf("Error: Out of memory\n"); - skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n"); + MXS_ERROR("Out of memory\n"); return 1; } @@ -266,7 +259,7 @@ int routeQuery(void* ins, void* session, GWBUF* queue) }else{ printf("Error: cannot allocate enough memory.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); + MXS_ERROR("cannot allocate enough memory.\n"); return 0; } @@ -349,14 +342,14 @@ int load_query() buffer = (char*)calloc(4092,sizeof(char)); if(buffer == NULL){ printf("Error: cannot allocate enough memory.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); + MXS_ERROR("cannot allocate enough memory.\n"); return 1; } query_list = calloc(qbuff_sz,sizeof(char*)); if(query_list == NULL){ printf("Error: cannot allocate enough memory.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); + MXS_ERROR("cannot allocate enough memory.\n"); free(buffer); return 1; } @@ -368,7 +361,7 @@ int load_query() char** tmpbuff = realloc(query_list,sizeof(char*)*qbuff_sz*2); if(tmpbuff == NULL){ printf("Error: cannot allocate enough memory.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); + MXS_ERROR("cannot allocate enough memory.\n"); rval = 1; goto retblock; } @@ -395,7 +388,7 @@ int load_query() if(tmpbff[i] == NULL) { printf("Error: cannot allocate a new buffer.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate a new buffer.\n"); + MXS_ERROR("cannot allocate a new buffer.\n"); int x; for(x = 0;xvalue); - skygw_log_write(LOGFILE_ERROR,"Error creating filter instance!\nModule: %s\n",item->value); + MXS_ERROR("Error creating filter instance!\nModule: %s\n",item->value); config_ok = 0; goto cleanup; @@ -744,8 +737,7 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf) if(fc->instance->setUpstream && fc->instance->clientReply){ fc->instance->setUpstream(fc->filter, fc->session[i], fc->up[i]); }else{ - skygw_log_write(LOGFILE_MESSAGE, - "Warning: The filter %s does not support client replies.\n",fc->name); + MXS_WARNING("The filter %s does not support client replies.\n",fc->name); } if(fc->next && fc->next->next){ @@ -828,7 +820,7 @@ FILTERCHAIN* load_filter_module(char* str) if( (flt_ptr->instance = (FILTER_OBJECT*)load_module(str, MODULE_FILTER)) == NULL) { printf("Error: Module loading failed: %s\n",str); - skygw_log_write(LOGFILE_ERROR,"Error: Module loading failed: %s\n",str); + MXS_ERROR("Module loading failed: %s\n",str); free(flt_ptr->down); free(flt_ptr); return NULL; diff --git a/server/modules/filter/test/harness_ui.c b/server/modules/filter/test/harness_ui.c index fa8d14e3d..4f84a8600 100644 --- a/server/modules/filter/test/harness_ui.c +++ b/server/modules/filter/test/harness_ui.c @@ -10,9 +10,8 @@ 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_ERROR("Initialization failed.\n"); + mxs_log_finish(); return 1; } @@ -49,7 +48,7 @@ int main(int argc, char** argv){ tmp_chn = load_filter_module(tk); if(!tmp_chn || !load_filter(tmp_chn,instance.conf)){ printf("Error creating filter instance.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: Error creating filter instance.\n"); + MXS_ERROR("Error creating filter instance.\n"); }else{ instance.head = tmp_chn; } @@ -181,7 +180,7 @@ int main(int argc, char** argv){ if(!(t_thr_pool = realloc(instance.thrpool,instance.thrcount * sizeof(pthread_t)))){ printf("Error: Out of memory\n"); - skygw_log_write(LOGFILE_ERROR,"Error: Out of memory\n"); + MXS_ERROR("Out of memory\n"); instance.running = 0; break; } @@ -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; @@ -350,7 +348,7 @@ void manual_query() qlen = strnlen(query, 1024); if((tmpbuf = malloc(sizeof(GWBUF*)))== NULL){ printf("Error: cannot allocate enough memory.\n"); - skygw_log_write(LOGFILE_ERROR,"Error: cannot allocate enough memory.\n"); + MXS_ERROR("Cannot allocate enough memory.\n"); return; } instance.buffer = tmpbuf; diff --git a/server/modules/filter/test/harness_util.c b/server/modules/filter/test/harness_util.c index 5b3baf31e..c23e8c192 100644 --- a/server/modules/filter/test/harness_util.c +++ b/server/modules/filter/test/harness_util.c @@ -34,9 +34,8 @@ 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_ERROR("Initialization failed.\n"); + mxs_log_finish(); return 1; } diff --git a/server/modules/filter/topfilter.c b/server/modules/filter/topfilter.c index 38eab05c6..457375a83 100644 --- a/server/modules/filter/topfilter.c +++ b/server/modules/filter/topfilter.c @@ -49,11 +49,6 @@ #include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - MODULE_INFO info = { MODULE_API_FILTER, MODULE_GA, @@ -222,48 +217,43 @@ TOPN_INSTANCE *my_instance; my_instance->user = strdup(params[i]->value); else if (!filter_standard_parameter(params[i]->name)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "topfilter: Unexpected parameter '%s'.\n", - params[i]->name))); + MXS_ERROR("topfilter: Unexpected parameter '%s'.", + params[i]->name); } } if (options) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "topfilter: Options are not supported by this " - " filter. They will be ignored\n"))); + MXS_ERROR("topfilter: Options are not supported by this " + " filter. They will be ignored."); } my_instance->sessions = 0; if (my_instance->match && regcomp(&my_instance->re, my_instance->match, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "topfilter: Invalid regular expression '%s'" - " for the match parameter.\n", - my_instance->match))); - free(my_instance->match); - free(my_instance->source); - free(my_instance->user); - free(my_instance->filebase); - free(my_instance); - return NULL; + MXS_ERROR("topfilter: Invalid regular expression '%s'" + " for the match parameter.", + my_instance->match); + free(my_instance->match); + free(my_instance->source); + free(my_instance->user); + free(my_instance->filebase); + free(my_instance); + return NULL; } if (my_instance->exclude && regcomp(&my_instance->exre, my_instance->exclude, REG_ICASE)) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "qlafilter: Invalid regular expression '%s'" - " for the nomatch paramter.\n", - my_instance->match))); - regfree(&my_instance->re); - free(my_instance->match); - free(my_instance->source); - free(my_instance->user); - free(my_instance->filebase); - free(my_instance); - return NULL; + MXS_ERROR("qlafilter: Invalid regular expression '%s'" + " for the nomatch paramter.\n", + my_instance->match); + regfree(&my_instance->re); + free(my_instance->match); + free(my_instance->source); + free(my_instance->user); + free(my_instance->filebase); + free(my_instance); + return NULL; } } return (FILTER *)my_instance; 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/include/readwritesplit.h b/server/modules/include/readwritesplit.h index 9f9866e5d..0e229f807 100644 --- a/server/modules/include/readwritesplit.h +++ b/server/modules/include/readwritesplit.h @@ -290,9 +290,11 @@ struct router_client_session { rwsplit_config_t rses_config; /*< copied config info from router instance */ int rses_nbackends; int rses_nsescmd; /*< Number of executed session commands */ - int rses_capabilities; /*< input type, for example */ bool rses_autocommit_enabled; bool rses_transaction_active; + bool rses_load_active; /*< If LOAD DATA LOCAL INFILE is + * being currently executed */ + uint64_t rses_load_data_sent; /*< How much data has been sent */ DCB* client_dcb; int pos_generator; #if defined(PREP_STMT_CACHING) diff --git a/server/modules/include/schemarouter.h b/server/modules/include/schemarouter.h index 16fb8640b..d4f220903 100644 --- a/server/modules/include/schemarouter.h +++ b/server/modules/include/schemarouter.h @@ -56,6 +56,26 @@ typedef enum showdb_response SHOWDB_DUPLICATE_DATABASES, SHOWDB_FATAL_ERROR } showdb_response_t; + +enum shard_map_state +{ + SHMAP_UNINIT, /*< No databases have been added to this shard map */ + SHMAP_READY, /*< All available databases have been added */ + SHMAP_STALE /*< The shard map has old data or has not been updated recently */ +}; + +/** + * A map of the shards tied to a single user. + */ +typedef struct shard_map +{ + HASHTABLE *hash; /*< A hashtable of database names and the servers which + * have these databases. */ + SPINLOCK lock; + time_t last_updated; + enum shard_map_state state; /*< State of the shard map */ +} shard_map_t; + /** * The state of the backend server reference */ @@ -274,6 +294,8 @@ typedef struct { double ses_longest; /*< Longest session */ double ses_shortest; /*< Shortest session */ double ses_average; /*< Average session length */ + int shmap_cache_hit; /*< Shard map was found from the cache */ + int shmap_cache_miss;/*< No shard map found from the cache */ } ROUTER_STATS; /** @@ -294,13 +316,13 @@ struct router_client_session { backend_ref_t* rses_backend_ref; /*< Pointer to backend reference array */ schemarouter_config_t rses_config; /*< Copied config info from router instance */ int rses_nbackends; /*< Number of backends */ - int rses_capabilities; /*< Input type, for example */ bool rses_autocommit_enabled; /*< Is autocommit enabled */ bool rses_transaction_active; /*< Is a transaction active */ struct router_instance *router; /*< The router instance */ struct router_client_session* next; /*< List of router sessions */ - HASHTABLE* dbhash; /*< Database hash containing names of the databases mapped to the servers that contain them */ + shard_map_t* shardmap; /*< Database hash containing names of the databases mapped to the servers that contain them */ char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */ + char current_db[MYSQL_DATABASE_MAXLEN + 1]; /*< Current active database */ init_mask_t init; /*< Initialization state bitmask */ GWBUF* queue; /*< Query that was received before the session was ready */ DCB* dcb_route; /*< Internal DCB used to trigger re-routing of buffers */ @@ -318,6 +340,7 @@ struct router_client_session { * The per instance data for the router. */ typedef struct router_instance { + HASHTABLE* shard_maps; /*< Shard maps hashed by user name */ SERVICE* service; /*< Pointer to service */ ROUTER_CLIENT_SES* connections; /*< List of client connections */ SPINLOCK lock; /*< Lock for the instance data */ diff --git a/server/modules/include/sharding_common.h b/server/modules/include/sharding_common.h index dd30a74ea..b0d917f0a 100644 --- a/server/modules/include/sharding_common.h +++ b/server/modules/include/sharding_common.h @@ -12,8 +12,6 @@ bool extract_database(GWBUF* buf, char* str); void create_error_reply(char* fail_str,DCB* dcb); -bool change_current_db(MYSQL_session* mysql_session, - HASHTABLE* dbhash, - GWBUF* buf); +bool change_current_db(char* dest, HASHTABLE* dbhash, GWBUF* buf); #endif diff --git a/server/modules/include/shardrouter.h b/server/modules/include/shardrouter.h index 63572671e..f288505c1 100644 --- a/server/modules/include/shardrouter.h +++ b/server/modules/include/shardrouter.h @@ -192,7 +192,6 @@ struct router_client_session { rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT]; shard_config_t rses_config; /*< copied config info from router instance */ - int rses_capabilities; /*< input type, for example */ bool rses_autocommit_enabled; bool rses_transaction_active; struct router_instance *router; /*< The router instance */ @@ -204,6 +203,7 @@ struct router_client_session { SESSION* session; GWBUF* queue; char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */ + char current_db[MYSQL_DATABASE_MAXLEN + 1]; /*< Current active database */ shard_init_mask_t init; /*< Initialization state bitmask */ #if defined(SS_DEBUG) skygw_chk_t rses_chk_tail; diff --git a/server/modules/monitor/CMakeLists.txt b/server/modules/monitor/CMakeLists.txt index ab275cf46..58a59e1e1 100644 --- a/server/modules/monitor/CMakeLists.txt +++ b/server/modules/monitor/CMakeLists.txt @@ -1,16 +1,21 @@ add_library(mysqlmon SHARED mysql_mon.c monitor_common.c) target_link_libraries(mysqlmon log_manager) +add_dependencies(mysqlmon pcre2) install(TARGETS mysqlmon DESTINATION ${MAXSCALE_LIBDIR}) add_library(galeramon SHARED galeramon.c monitor_common.c) target_link_libraries(galeramon log_manager) +add_dependencies(galeramon pcre2) install(TARGETS galeramon DESTINATION ${MAXSCALE_LIBDIR}) add_library(ndbclustermon SHARED ndbclustermon.c monitor_common.c) target_link_libraries(ndbclustermon log_manager) +add_dependencies(ndbclustermon pcre2) install(TARGETS ndbclustermon DESTINATION ${MAXSCALE_LIBDIR}) + if(BUILD_MMMON) add_library(mmmon SHARED mmmon.c monitor_common.c) target_link_libraries(mmmon log_manager) + add_dependencies(mmmon pcre2) install(TARGETS mmmon DESTINATION ${MAXSCALE_LIBDIR}) endif() diff --git a/server/modules/monitor/galeramon.c b/server/modules/monitor/galeramon.c index 8e7e7e24b..34840e9a2 100644 --- a/server/modules/monitor/galeramon.c +++ b/server/modules/monitor/galeramon.c @@ -35,41 +35,40 @@ * 20/04/15 Guillaume Lefranc Added availableWhenDonor feature * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting * 08/05/15 Markus Makela Addition of launchable scripts + * 17/10/15 Martin Brampton Change DCB callback to hangup * * @endverbatim */ #include +#include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static void monitorMain(void *); +static void monitorMain(void *); static char *version_str = "V2.0.0"; -MODULE_INFO info = { - MODULE_API_MONITOR, - MODULE_GA, - MONITOR_VERSION, - "A Galera cluster monitor" +MODULE_INFO info = +{ + MODULE_API_MONITOR, + MODULE_GA, + MONITOR_VERSION, + "A Galera cluster monitor" }; -static void *startMonitor(void *,void*); -static void stopMonitor(void *); -static void diagnostics(DCB *, void *); -static MONITOR_SERVERS *get_candidate_master(MONITOR*); -static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int); -static void disableMasterFailback(void *, int); +static void *startMonitor(void *, void*); +static void stopMonitor(void *); +static void diagnostics(DCB *, void *); +static MONITOR_SERVERS *get_candidate_master(MONITOR*); +static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int); +static void disableMasterFailback(void *, int); bool isGaleraEvent(monitor_event_t event); -static MONITOR_OBJECT MyObject = { - startMonitor, - stopMonitor, - diagnostics +static MONITOR_OBJECT MyObject = +{ + startMonitor, + stopMonitor, + diagnostics }; /** @@ -80,7 +79,7 @@ static MONITOR_OBJECT MyObject = { char * version() { - return version_str; + return version_str; } /** @@ -90,10 +89,9 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise the MySQL Galera Monitor module %s.\n", - version_str))); + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Initialise the MySQL Galera Monitor module %s.\n", + version_str))); } /** @@ -107,7 +105,7 @@ ModuleInit() MONITOR_OBJECT * GetModuleObject() { - return &MyObject; + return &MyObject; } /** @@ -117,96 +115,94 @@ GetModuleObject() * * @return A handle to use when interacting with the monitor */ -static void * -startMonitor(void *arg,void* opt) +static void * +startMonitor(void *arg, void* opt) { MONITOR* mon = arg; GALERA_MONITOR *handle = mon->handle; - CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false,script_error = false; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*) opt; + bool have_events = false, script_error = false; if (handle != NULL) { - handle->shutdown = 0; + handle->shutdown = 0; } else { - if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - handle->disableMasterFailback = 0; - handle->availableWhenDonor = 0; - handle->disableMasterRoleSetting = 0; - handle->master = NULL; - handle->script = NULL; - handle->use_priority = false; - memset(handle->events,false,sizeof(handle->events)); - spinlock_init(&handle->lock); + if ((handle = (GALERA_MONITOR *) malloc(sizeof(GALERA_MONITOR))) == NULL) + { + return NULL; + } + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + handle->disableMasterFailback = 0; + handle->availableWhenDonor = 0; + handle->disableMasterRoleSetting = 0; + handle->master = NULL; + handle->script = NULL; + handle->use_priority = false; + memset(handle->events, false, sizeof(handle->events)); + spinlock_init(&handle->lock); } - while(params) + while (params) { - if(!strcmp(params->name,"disable_master_failback")) - handle->disableMasterFailback = config_truth_value(params->value); - else if(!strcmp(params->name,"available_when_donor")) - handle->availableWhenDonor = config_truth_value(params->value); - else if(!strcmp(params->name,"disable_master_role_setting")) - handle->disableMasterRoleSetting = config_truth_value(params->value); - else if(!strcmp(params->name,"use_priority")) - 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); - } - } - } - else if(!strcmp(params->name,"events")) - { - if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) - script_error = true; - else - have_events = true; - } - params = params->next; + if (!strcmp(params->name, "disable_master_failback")) + { + handle->disableMasterFailback = config_truth_value(params->value); + } + else if (!strcmp(params->name, "available_when_donor")) + { + handle->availableWhenDonor = config_truth_value(params->value); + } + else if (!strcmp(params->name, "disable_master_role_setting")) + { + handle->disableMasterRoleSetting = config_truth_value(params->value); + } + else if (!strcmp(params->name, "use_priority")) + { + handle->use_priority = config_truth_value(params->value); + } + 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) + { + script_error = true; + } + else + { + have_events = true; + } + } + params = params->next; } - if(script_error) + if (script_error) { - skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " - "for the monitor '%s'. The script will not be used.",mon->name); - free(handle->script); - handle->script = NULL; + skygw_log_write(LE, "Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.", mon->name); + free(handle->script); + handle->script = NULL; } /** If no specific events are given, enable them all */ - if(!have_events) + if (!have_events) { - memset(handle->events,true,sizeof(handle->events)); + memset(handle->events, true, sizeof(handle->events)); } - handle->tid = (THREAD)thread_start(monitorMain, mon); + handle->tid = (THREAD) thread_start(monitorMain, mon); return handle; } @@ -215,14 +211,14 @@ startMonitor(void *arg,void* opt) * * @param arg Handle on thr running monior */ -static void +static void stopMonitor(void *arg) { - MONITOR* mon = (MONITOR*)arg; -GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; + MONITOR* mon = (MONITOR*) arg; + GALERA_MONITOR *handle = (GALERA_MONITOR *) mon->handle; - handle->shutdown = 1; - thread_wait((void *)handle->tid); + handle->shutdown = 1; + thread_wait((void *) handle->tid); } /** @@ -234,42 +230,42 @@ GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; static void diagnostics(DCB *dcb, void *arg) { - MONITOR* mon = (MONITOR*)arg; -GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle; -MONITOR_SERVERS *db; -char *sep; + MONITOR* mon = (MONITOR*) arg; + GALERA_MONITOR *handle = (GALERA_MONITOR *) mon->handle; + MONITOR_SERVERS *db; + char *sep; - switch (handle->status) - { - case MONITOR_RUNNING: - dcb_printf(dcb, "\tMonitor running\n"); - break; - case MONITOR_STOPPING: - dcb_printf(dcb, "\tMonitor stopping\n"); - break; - case MONITOR_STOPPED: - dcb_printf(dcb, "\tMonitor stopped\n"); - break; - } + switch (handle->status) + { + case MONITOR_RUNNING: + dcb_printf(dcb, "\tMonitor running\n"); + break; + case MONITOR_STOPPING: + dcb_printf(dcb, "\tMonitor stopping\n"); + break; + case MONITOR_STOPPED: + dcb_printf(dcb, "\tMonitor stopped\n"); + break; + } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); - dcb_printf(dcb,"\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on"); - dcb_printf(dcb,"\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off"); - dcb_printf(dcb,"\tMaster Role Setting Disabled:\t%s\n", (handle->disableMasterRoleSetting == 1) ? "on" : "off"); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); - dcb_printf(dcb, "\tMonitored servers: "); + dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb, "\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on"); + dcb_printf(dcb, "\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off"); + dcb_printf(dcb, "\tMaster Role Setting Disabled:\t%s\n", (handle->disableMasterRoleSetting == 1) ? "on" : "off"); + dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); + dcb_printf(dcb, "\tMonitored servers: "); - db = mon->databases; - sep = ""; - while (db) - { - dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port); - sep = ", "; - db = db->next; - } - dcb_printf(dcb, "\n"); + db = mon->databases; + sep = ""; + while (db) + { + dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port); + sep = ", "; + db = db->next; + } + dcb_printf(dcb, "\n"); } /** @@ -281,19 +277,19 @@ char *sep; static void monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { - GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle; -MYSQL_ROW row; -MYSQL_RES *result,*result2; -int isjoined = 0; -unsigned long int server_version = 0; -char *server_string; + GALERA_MONITOR* handle = (GALERA_MONITOR*) mon->handle; + MYSQL_ROW row; + MYSQL_RES *result, *result2; + int isjoined = 0; + unsigned long int server_version = 0; + char *server_string; - /* Don't even probe server flagged as in maintenance */ - if (SERVER_IN_MAINT(database->server)) - return; + /* Don't even probe server flagged as in maintenance */ + if (SERVER_IN_MAINT(database->server)) + return; - /** Store previous status */ - database->mon_prev_status = database->server->status; + /** Store previous status */ + database->mon_prev_status = database->server->status; server_clear_status(database->server, SERVER_RUNNING); @@ -325,90 +321,94 @@ char *server_string; return; } - /* If we get this far then we have a working connection */ - server_set_status(database->server, SERVER_RUNNING); + /* If we get this far then we have a working connection */ + server_set_status(database->server, SERVER_RUNNING); - /* 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); - } + /* get server version string */ + server_string = (char *) mysql_get_server_info(database->con); + 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 - && (result = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_state'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } + /* 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 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_state'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - if (strcmp(row[1], "4") == 0) - isjoined = 1; - - /* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */ - else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) { - if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 - && (result2 = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - mysql_free_result(result2); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } - while ((row = mysql_fetch_row(result2))) - { - if (strncmp(row[1], "xtrabackup", 10) == 0) - isjoined = 1; - } - mysql_free_result(result2); - } - } - } - mysql_free_result(result); - } + while ((row = mysql_fetch_row(result))) + { + if (strcmp(row[1], "4") == 0) + isjoined = 1; - /* Check the the Galera node index in the cluster */ - if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_index'") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - long local_index = -1; + /* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */ + else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) + { + if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0 + && (result2 = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + mysql_free_result(result2); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } + while ((row = mysql_fetch_row(result2))) + { + if (strncmp(row[1], "xtrabackup", 10) == 0) + isjoined = 1; + } + mysql_free_result(result2); + } + } + } + mysql_free_result(result); + } - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_index'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } + /* Check the the Galera node index in the cluster */ + if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_index'") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + long local_index = -1; - while ((row = mysql_fetch_row(result))) - { - local_index = strtol(row[1], NULL, 10); - if ((errno == ERANGE && (local_index == LONG_MAX - || local_index == LONG_MIN)) || (errno != 0 && local_index == 0)) - { - local_index = -1; - } - database->server->node_id = local_index; - } - mysql_free_result(result); - } + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_index'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } - if (isjoined) - server_set_status(database->server, SERVER_JOINED); - else - server_clear_status(database->server, SERVER_JOINED); + while ((row = mysql_fetch_row(result))) + { + local_index = strtol(row[1], NULL, 10); + if ((errno == ERANGE && (local_index == LONG_MAX + || local_index == LONG_MIN)) || (errno != 0 && local_index == 0)) + { + local_index = -1; + } + database->server->node_id = local_index; + } + mysql_free_result(result); + } + + if (isjoined) + { + server_set_status(database->server, SERVER_JOINED); + } + else + { + server_clear_status(database->server, SERVER_JOINED); + } } /** @@ -419,205 +419,217 @@ char *server_string; static void monitorMain(void *arg) { - MONITOR* mon = (MONITOR*)arg; -GALERA_MONITOR *handle; -MONITOR_SERVERS *ptr; -size_t nrounds = 0; -MONITOR_SERVERS *candidate_master = NULL; -int master_stickiness; -int is_cluster=0; -int log_no_members = 1; -monitor_event_t evtype; + MONITOR* mon = (MONITOR*) arg; + GALERA_MONITOR *handle; + MONITOR_SERVERS *ptr; + size_t nrounds = 0; + MONITOR_SERVERS *candidate_master = NULL; + int master_stickiness; + int is_cluster = 0; + int log_no_members = 1; + monitor_event_t evtype; spinlock_acquire(&mon->lock); - handle = (GALERA_MONITOR *)mon->handle; + handle = (GALERA_MONITOR *) mon->handle; spinlock_release(&mon->lock); master_stickiness = handle->disableMasterFailback; - if (mysql_thread_init()) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal : mysql_thread_init failed in monitor " - "module. Exiting.\n"))); - return; - } - handle->status = MONITOR_RUNNING; - - while (1) - { - if (handle->shutdown) - { - handle->status = MONITOR_STOPPING; - mysql_thread_end(); - handle->status = MONITOR_STOPPED; - return; - } - /** Wait base interval */ - thread_millisleep(MON_BASE_INTERVAL_MS); - /** - * Calculate how far away the monitor interval is from its full - * cycle and if monitor interval time further than the base - * interval, then skip monitoring checks. Excluding the first - * round. - */ + if (mysql_thread_init()) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Fatal : mysql_thread_init failed in monitor " + "module. Exiting.\n"))); + return; + } + handle->status = MONITOR_RUNNING; - if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS) - { - nrounds += 1; - continue; - } + while (1) + { + if (handle->shutdown) + { + handle->status = MONITOR_STOPPING; + mysql_thread_end(); + handle->status = MONITOR_STOPPED; + return; + } + /** Wait base interval */ + thread_millisleep(MON_BASE_INTERVAL_MS); + /** + * Calculate how far away the monitor interval is from its full + * cycle and if monitor interval time further than the base + * interval, then skip monitoring checks. Excluding the first + * round. + */ - nrounds += 1; + if (nrounds != 0 && ((nrounds * MON_BASE_INTERVAL_MS) % mon->interval) >= MON_BASE_INTERVAL_MS) + { + nrounds += 1; + continue; + } - /* reset cluster members counter */ - is_cluster=0; + nrounds += 1; - ptr = mon->databases; + /* reset cluster members counter */ + is_cluster = 0; - while (ptr) - { - ptr->mon_prev_status = ptr->server->status; + ptr = mon->databases; - monitorDatabase(mon, ptr); + while (ptr) + { + ptr->mon_prev_status = ptr->server->status; - /* clear bits for non member nodes */ - if ( ! SERVER_IN_MAINT(ptr->server) && (! SERVER_IS_JOINED(ptr->server))) { - ptr->server->depth = -1; + monitorDatabase(mon, ptr); - /* clear M/S status */ - server_clear_status(ptr->server, SERVER_SLAVE); - server_clear_status(ptr->server, SERVER_MASTER); - - /* clear master sticky status */ - server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); - } + /* clear bits for non member nodes */ + if (!SERVER_IN_MAINT(ptr->server) && (!SERVER_IS_JOINED(ptr->server))) + { + ptr->server->depth = -1; - /* Log server status change */ - if (mon_status_changed(ptr)) - { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Backend server %s:%d state : %s", - ptr->server->name, - ptr->server->port, - STRSRVSTATUS(ptr->server)))); - } - - if (!(SERVER_IS_RUNNING(ptr->server)) || - !(SERVER_IS_IN_CLUSTER(ptr->server))) - { - dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); - } + /* clear M/S status */ + server_clear_status(ptr->server, SERVER_SLAVE); + server_clear_status(ptr->server, SERVER_MASTER); - if (SERVER_IS_DOWN(ptr->server)) - { - /** Increase this server'e error count */ - dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); - ptr->mon_err_count += 1; + /* clear master sticky status */ + server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); + } - } - else - { - /** Reset this server's error count */ - ptr->mon_err_count = 0; - } + /* Log server status change */ + if (mon_status_changed(ptr)) + { + LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); + } - ptr = ptr->next; - } + if (!(SERVER_IS_RUNNING(ptr->server)) || + !(SERVER_IS_IN_CLUSTER(ptr->server))) + { + dcb_hangup_foreach(ptr->server); + } - /* - * Let's select a master server: - * it could be the candidate master following MIN(node_id) rule or - * the server that was master in the previous monitor polling cycle - * Decision depends on master_stickiness value set in configuration - */ + if (SERVER_IS_DOWN(ptr->server)) + { + /** Increase this server'e error count */ + dcb_hangup_foreach(ptr->server); + ptr->mon_err_count += 1; - /* get the candidate master, following MIN(node_id) rule */ - candidate_master = get_candidate_master(mon); + } + else + { + /** Reset this server's error count */ + ptr->mon_err_count = 0; + } - /* Select the master, based on master_stickiness */ - if (1 == handle->disableMasterRoleSetting) { - handle->master = NULL; + ptr = ptr->next; + } + + /* + * Let's select a master server: + * it could be the candidate master following MIN(node_id) rule or + * the server that was master in the previous monitor polling cycle + * Decision depends on master_stickiness value set in configuration + */ + + /* get the candidate master, following MIN(node_id) rule */ + candidate_master = get_candidate_master(mon); + + /* Select the master, based on master_stickiness */ + if (1 == handle->disableMasterRoleSetting) + { + handle->master = NULL; + } + else + { + handle->master = set_cluster_master(handle->master, candidate_master, master_stickiness); + } + + ptr = mon->databases; + + while (ptr) + { + if (!SERVER_IS_JOINED(ptr->server) || SERVER_IN_MAINT(ptr->server)) + { + ptr = ptr->next; + continue; + } + + if (handle->master) + { + if (ptr != handle->master) + { + /* set the Slave role */ + server_set_status(ptr->server, SERVER_SLAVE); + server_clear_status(ptr->server, SERVER_MASTER); + + /* clear master stickiness */ + server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); } - else { - handle->master = set_cluster_master(handle->master, candidate_master, master_stickiness); + else + { + /* set the Master role */ + server_set_status(handle->master->server, SERVER_MASTER); + server_clear_status(handle->master->server, SERVER_SLAVE); + + if (candidate_master && handle->master->server->node_id != candidate_master->server->node_id) + { + /* set master stickiness */ + server_set_status(handle->master->server, SERVER_MASTER_STICKINESS); + } + else + { + /* clear master stickiness */ + server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); + } } + } - ptr = mon->databases; + is_cluster++; - while (ptr) { - if (!SERVER_IS_JOINED(ptr->server) || SERVER_IN_MAINT(ptr->server)) { - ptr = ptr->next; - continue; - } + ptr = ptr->next; + } - if (handle->master) { - if (ptr != handle->master) { - /* set the Slave role */ - server_set_status(ptr->server, SERVER_SLAVE); - server_clear_status(ptr->server, SERVER_MASTER); - - /* clear master stickiness */ - server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); - } else { - /* set the Master role */ - server_set_status(handle->master->server, SERVER_MASTER); - server_clear_status(handle->master->server, SERVER_SLAVE); - - if (candidate_master && handle->master->server->node_id != candidate_master->server->node_id) { - /* set master stickiness */ - server_set_status(handle->master->server, SERVER_MASTER_STICKINESS); - } else { - /* clear master stickiness */ - server_clear_status(ptr->server, SERVER_MASTER_STICKINESS); - } - } - } - - is_cluster++; - - ptr = ptr->next; - } - - if (is_cluster == 0 && log_no_members) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: there are no cluster members"))); - log_no_members = 0; - } else { - if (is_cluster > 0 && log_no_members == 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Info: found cluster members"))); - log_no_members = 1; - } - } + if (is_cluster == 0 && log_no_members) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error: there are no cluster members"))); + log_no_members = 0; + } + else + { + if (is_cluster > 0 && log_no_members == 0) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Info: found cluster members"))); + log_no_members = 1; + } + } - ptr = mon->databases; + ptr = mon->databases; - while(ptr) - { + while (ptr) + { - /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr)) - { - evtype = mon_get_event_type(ptr); - if(isGaleraEvent(evtype)) - { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) - { - monitor_launch_script(mon,ptr,handle->script); - } - } - } - ptr = ptr->next; - } - } + /** Execute monitor script if a server state has changed */ + if (mon_status_changed(ptr)) + { + evtype = mon_get_event_type(ptr); + if (isGaleraEvent(evtype)) + { + skygw_log_write(LOGFILE_TRACE, "Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name, ptr->server->port, + mon_get_event_name(ptr)); + if (handle->script && handle->events[evtype]) + { + monitor_launch_script(mon, ptr, handle->script); + } + } + } + ptr = ptr->next; + } + } } /** @@ -629,45 +641,50 @@ monitor_event_t evtype; * @param servers The monitored servers list * @return The candidate master on success, NULL on failure */ -static MONITOR_SERVERS *get_candidate_master(MONITOR* mon) { - MONITOR_SERVERS *moitor_servers = mon->databases; - MONITOR_SERVERS *candidate_master = NULL; - GALERA_MONITOR* handle = mon->handle; - long min_id = -1; - int minval = INT_MAX; - int currval; - char* value; - /* set min_id to the lowest value of moitor_servers->server->node_id */ - while(moitor_servers) { - if (!SERVER_IN_MAINT(moitor_servers->server) && SERVER_IS_JOINED(moitor_servers->server)) { +static MONITOR_SERVERS *get_candidate_master(MONITOR* mon) +{ + MONITOR_SERVERS *moitor_servers = mon->databases; + MONITOR_SERVERS *candidate_master = NULL; + GALERA_MONITOR* handle = mon->handle; + long min_id = -1; + int minval = INT_MAX; + int currval; + char* value; + /* set min_id to the lowest value of moitor_servers->server->node_id */ + while (moitor_servers) + { + if (!SERVER_IN_MAINT(moitor_servers->server) && SERVER_IS_JOINED(moitor_servers->server)) + { - moitor_servers->server->depth = 0; + moitor_servers->server->depth = 0; - if(handle->use_priority && (value = serverGetParameter(moitor_servers->server,"priority")) != NULL) - { - currval = atoi(value); - if(currval < minval && currval > 0) - { - minval = currval; - candidate_master = moitor_servers; - } - } - else if(moitor_servers->server->node_id >= 0 && - (!handle->use_priority || /** Server priority disabled*/ - candidate_master == NULL || /** No candidate chosen */ - serverGetParameter(candidate_master->server,"priority") == NULL)) /** Candidate has no priority */ - { - if (min_id < 0 || moitor_servers->server->node_id < min_id) { - min_id = moitor_servers->server->node_id; - candidate_master = moitor_servers; - } - } - } - moitor_servers = moitor_servers->next; - } + if (handle->use_priority && (value = serverGetParameter(moitor_servers->server, "priority")) != NULL) + { + currval = atoi(value); + if (currval < minval && currval > 0) + { + minval = currval; + candidate_master = moitor_servers; + } + } + else if (moitor_servers->server->node_id >= 0 && + (!handle->use_priority || /** Server priority disabled*/ + candidate_master == NULL || /** No candidate chosen */ + serverGetParameter(candidate_master->server, "priority") == NULL)) /** Candidate has no priority */ + { + if (min_id < 0 || moitor_servers->server->node_id < min_id) + { + min_id = moitor_servers->server->node_id; + candidate_master = moitor_servers; + } + } + } + moitor_servers = moitor_servers->next; + } - return candidate_master; + return candidate_master; } + /** * set the master server in the cluster * @@ -682,23 +699,31 @@ static MONITOR_SERVERS *get_candidate_master(MONITOR* mon) { * @param candidate_master The candidate master server accordingly to the selection rule * @return The master node pointer (could be NULL) */ -static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONITOR_SERVERS *candidate_master, int master_stickiness) { - /* - * if current master is not set or master_stickiness is not enable - * just return candidate_master. - */ - if (current_master == NULL || master_stickiness == 0) { - return candidate_master; - } else { - /* - * if current_master is still a cluster member use it - * - */ - if (SERVER_IS_JOINED(current_master->server) && (! SERVER_IN_MAINT(current_master->server))) { - return current_master; - } else - return candidate_master; - } +static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONITOR_SERVERS *candidate_master, int master_stickiness) +{ + /* + * if current master is not set or master_stickiness is not enable + * just return candidate_master. + */ + if (current_master == NULL || master_stickiness == 0) + { + return candidate_master; + } + else + { + /* + * if current_master is still a cluster member use it + * + */ + if (SERVER_IS_JOINED(current_master->server) && (!SERVER_IN_MAINT(current_master->server))) + { + return current_master; + } + else + { + return candidate_master; + } + } } /** @@ -715,8 +740,8 @@ static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONI static void disableMasterFailback(void *arg, int disable) { -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; - memcpy(&handle->disableMasterFailback, &disable, sizeof(int)); + GALERA_MONITOR *handle = (GALERA_MONITOR *) arg; + memcpy(&handle->disableMasterFailback, &disable, sizeof(int)); } /** @@ -731,11 +756,12 @@ GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; static void availableWhenDonor(void *arg, int disable) { -GALERA_MONITOR *handle = (GALERA_MONITOR *)arg; - memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); + GALERA_MONITOR *handle = (GALERA_MONITOR *) arg; + memcpy(&handle->availableWhenDonor, &disable, sizeof(int)); } -static monitor_event_t galera_events[] = { +static monitor_event_t galera_events[] = +{ MASTER_DOWN_EVENT, MASTER_UP_EVENT, SLAVE_DOWN_EVENT, @@ -756,6 +782,7 @@ static monitor_event_t galera_events[] = { NEW_DONOR_EVENT, MAX_MONITOR_EVENT }; + /** * Check if the Galera monitor is monitoring this event type. * @param event Event to check @@ -764,10 +791,12 @@ static monitor_event_t galera_events[] = { bool isGaleraEvent(monitor_event_t event) { int i; - for(i = 0;galera_events[i] != MAX_MONITOR_EVENT;i++) + for (i = 0; galera_events[i] != MAX_MONITOR_EVENT; i++) { - if(event == galera_events[i]) - return true; + if (event == galera_events[i]) + { + return true; + } } return false; -} \ No newline at end of file +} diff --git a/server/modules/monitor/galeramon.h b/server/modules/monitor/galeramon.h index 5a209f01e..b92e97015 100644 --- a/server/modules/monitor/galeramon.h +++ b/server/modules/monitor/galeramon.h @@ -46,23 +46,23 @@ * @endverbatim */ - /** - * The handle for an instance of a MySQL Monitor module + * The handle for an instance of a Galera Monitor module */ -typedef struct { - SPINLOCK lock; /**< The monitor spinlock */ - pthread_t tid; /**< id of monitor thread */ - int shutdown; /**< Flag to shutdown the monitor thread */ - int status; /**< Monitor status */ - unsigned long id; /**< Monitor ID */ - int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ - int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ - int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ - MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - char* script; - bool use_priority; /*< Use server priorities */ - bool events[MAX_MONITOR_EVENT]; /*< enabled events */ +typedef struct +{ + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ + int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ + int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ + char* script; + bool use_priority; /*< Use server priorities */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } GALERA_MONITOR; #endif diff --git a/server/modules/monitor/mmmon.c b/server/modules/monitor/mmmon.c index 3bbb3c588..dd60c4a64 100644 --- a/server/modules/monitor/mmmon.c +++ b/server/modules/monitor/mmmon.c @@ -25,40 +25,38 @@ * Date Who Description * 08/09/14 Massimiliano Pinto Initial implementation * 08/05/15 Markus Makela Addition of launchable scripts + * 17/10/15 Martin Brampton Change DCB callback to hangup * * @endverbatim */ - #include +#include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static void monitorMain(void *); +static void monitorMain(void *); static char *version_str = "V1.1.1"; -MODULE_INFO info = { - MODULE_API_MONITOR, - MODULE_BETA_RELEASE, - MONITOR_VERSION, - "A Multi-Master Multi Master monitor" +MODULE_INFO info = +{ + MODULE_API_MONITOR, + MODULE_BETA_RELEASE, + MONITOR_VERSION, + "A Multi-Master Multi Master monitor" }; -static void *startMonitor(void *,void*); -static void stopMonitor(void *); -static void diagnostics(DCB *, void *); -static void detectStaleMaster(void *, int); +static void *startMonitor(void *, void*); +static void stopMonitor(void *); +static void diagnostics(DCB *, void *); +static void detectStaleMaster(void *, int); static MONITOR_SERVERS *get_current_master(MONITOR *); bool isMySQLEvent(monitor_event_t event); -static MONITOR_OBJECT MyObject = { - startMonitor, - stopMonitor, - diagnostics +static MONITOR_OBJECT MyObject = +{ + startMonitor, + stopMonitor, + diagnostics }; /** @@ -69,7 +67,7 @@ static MONITOR_OBJECT MyObject = { char * version() { - return version_str; + return version_str; } /** @@ -79,10 +77,10 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise the Multi-Master Monitor module %s.", - version_str))); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Initialise the Multi-Master Monitor module %s.", + version_str))); } /** @@ -96,7 +94,7 @@ ModuleInit() MONITOR_OBJECT * GetModuleObject() { - return &MyObject; + return &MyObject; } /** @@ -107,87 +105,78 @@ GetModuleObject() * @param arg The current handle - NULL if first start * @return A handle to use when interacting with the monitor */ -static void * -startMonitor(void *arg,void* opt) +static void * +startMonitor(void *arg, void* opt) { - MONITOR* mon = (MONITOR*)arg; + MONITOR* mon = (MONITOR*) arg; MM_MONITOR *handle = mon->handle; - CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false,script_error = false; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*) opt; + bool have_events = false, script_error = false; if (handle) { - handle->shutdown = 0; + handle->shutdown = 0; } else { - if ((handle = (MM_MONITOR *)malloc(sizeof(MM_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - handle->master = NULL; - handle->script = NULL; - handle->detectStaleMaster = false; - memset(handle->events,false,sizeof(handle->events)); - spinlock_init(&handle->lock); + if ((handle = (MM_MONITOR *) malloc(sizeof(MM_MONITOR))) == NULL) + { + return NULL; + } + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + handle->master = NULL; + handle->script = NULL; + handle->detectStaleMaster = false; + memset(handle->events, false, sizeof(handle->events)); + spinlock_init(&handle->lock); } - while(params) + while (params) { - if(!strcmp(params->name,"detect_stale_master")) - { - handle->detectStaleMaster = 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,"events")) - { - if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) - script_error = true; - else - have_events = true; - } - params = params->next; + if (!strcmp(params->name, "detect_stale_master")) + { + handle->detectStaleMaster = config_truth_value(params->value); + } + 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) + { + script_error = true; + } + else + { + have_events = true; + } + } + params = params->next; } - if(script_error) + if (script_error) { - skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " - "for the monitor '%s'. The script will not be used.",mon->name); - free(handle->script); - handle->script = NULL; + skygw_log_write(LE, "Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.", mon->name); + free(handle->script); + handle->script = NULL; } /** If no specific events are given, enable them all */ - if(!have_events) + if (!have_events) { - memset(handle->events,true,sizeof(handle->events)); + memset(handle->events, true, sizeof(handle->events)); } - handle->tid = (THREAD)thread_start(monitorMain, mon); + handle->tid = (THREAD) thread_start(monitorMain, mon); return handle; } @@ -196,14 +185,14 @@ startMonitor(void *arg,void* opt) * * @param arg Handle on thr running monior */ -static void +static void stopMonitor(void *arg) { MONITOR* mon = arg; - MM_MONITOR *handle = (MM_MONITOR *)mon->handle; + MM_MONITOR *handle = (MM_MONITOR *) mon->handle; - handle->shutdown = 1; - thread_wait((void *)handle->tid); + handle->shutdown = 1; + thread_wait((void *) handle->tid); } /** @@ -214,41 +203,41 @@ stopMonitor(void *arg) */ static void diagnostics(DCB *dcb, void *arg) { - MONITOR* mon = (MONITOR*)arg; -MM_MONITOR *handle = (MM_MONITOR *)mon->handle; -MONITOR_SERVERS *db; -char *sep; + MONITOR* mon = (MONITOR*) arg; + MM_MONITOR *handle = (MM_MONITOR *) mon->handle; + MONITOR_SERVERS *db; + char *sep; - switch (handle->status) - { - case MONITOR_RUNNING: - dcb_printf(dcb, "\tMonitor running\n"); - break; - case MONITOR_STOPPING: - dcb_printf(dcb, "\tMonitor stopping\n"); - break; - case MONITOR_STOPPED: - dcb_printf(dcb, "\tMonitor stopped\n"); - break; - } + switch (handle->status) + { + case MONITOR_RUNNING: + dcb_printf(dcb, "\tMonitor running\n"); + break; + case MONITOR_STOPPING: + dcb_printf(dcb, "\tMonitor stopping\n"); + break; + case MONITOR_STOPPED: + dcb_printf(dcb, "\tMonitor stopped\n"); + break; + } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); - dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); - dcb_printf(dcb, "\tMonitored servers: "); + dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb, "\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); + dcb_printf(dcb, "\tMonitored servers: "); - db = mon->databases; - sep = ""; - while (db) - { - dcb_printf(dcb, - "%s%s:%d", - sep, - db->server->name, - db->server->port); - sep = ", "; - db = db->next; - } - dcb_printf(dcb, "\n"); + db = mon->databases; + sep = ""; + while (db) + { + dcb_printf(dcb, + "%s%s:%d", + sep, + db->server->name, + db->server->port); + sep = ", "; + db = db->next; + } + dcb_printf(dcb, "\n"); } /** @@ -260,16 +249,18 @@ char *sep; static void monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database) { -MYSQL_ROW row; -MYSQL_RES *result; -int isslave = 0; -int ismaster = 0; -unsigned long int server_version = 0; -char *server_string; + MYSQL_ROW row; + MYSQL_RES *result; + int isslave = 0; + int ismaster = 0; + unsigned long int server_version = 0; + char *server_string; /* Don't probe servers in maintenance mode */ if (SERVER_IN_MAINT(database->server)) + { return; + } /** Store previous status */ database->mon_prev_status = database->server->status; @@ -308,209 +299,228 @@ char *server_string; } /* Store current status in both server and monitor server pending struct */ - server_set_status(database->server, SERVER_RUNNING); - monitor_set_pending_status(database, SERVER_RUNNING); + server_set_status(database->server, SERVER_RUNNING); + monitor_set_pending_status(database, SERVER_RUNNING); - /* get server version from current server */ - server_version = mysql_get_server_version(database->con); + /* get server version from current server */ + server_version = mysql_get_server_version(database->con); - /* 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); - } + /* get server version string */ + server_string = (char *) mysql_get_server_info(database->con); + 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 - && (result = mysql_store_result(database->con)) != NULL) + /* get server_id form current node */ + if (mysql_query(database->con, "SELECT @@server_id") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + long server_id = -1; + + if (mysql_field_count(database->con) != 1) { - long server_id = -1; + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." + " MySQL Version: %s", version_str); + return; + } - if(mysql_field_count(database->con) != 1) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." - " MySQL Version: %s",version_str); - return; - } + while ((row = mysql_fetch_row(result))) + { + server_id = strtol(row[0], NULL, 10); + if ((errno == ERANGE && (server_id == LONG_MAX + || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) + { + server_id = -1; + } + database->server->node_id = server_id; + } + mysql_free_result(result); + } - while ((row = mysql_fetch_row(result))) - { - server_id = strtol(row[0], NULL, 10); - if ((errno == ERANGE && (server_id == LONG_MAX - || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) - { - server_id = -1; - } - database->server->node_id = server_id; - } + /* Check if the Slave_SQL_Running and Slave_IO_Running status is + * set to Yes + */ + + /* Check first for MariaDB 10.x.x and get status for multimaster replication */ + if (server_version >= 100000) + { + + if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + int i = 0; + long master_id = -1; + + if (mysql_field_count(database->con) < 42) + { mysql_free_result(result); + skygw_log_write(LE, "Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns" + " MySQL Version: %s", version_str); + return; + } + + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[12], "Yes", 3) == 0 + && strncmp(row[13], "Yes", 3) == 0) + { + isslave += 1; + } + + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[12], "Yes", 3) == 0) + { + /* get Master_Server_Id values */ + master_id = atol(row[41]); + if (master_id == 0) + { + master_id = -1; + } + } + + i++; + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); + + mysql_free_result(result); + + /* If all configured slaves are running set this node as slave */ + if (isslave > 0 && isslave == i) + { + isslave = 1; + } + else + { + isslave = 0; + } } - - /* Check if the Slave_SQL_Running and Slave_IO_Running status is - * set to Yes - */ - - /* Check first for MariaDB 10.x.x and get status for multimaster replication */ - if (server_version >= 100000) { - - if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - int i = 0; - long master_id = -1; - - if(mysql_field_count(database->con) < 42) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " - "returned less than the expected amount of columns. Expected 42 columns" - " MySQL Version: %s",version_str); - return; - } - - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[12], "Yes", 3) == 0 - && strncmp(row[13], "Yes", 3) == 0) { - isslave += 1; - } - - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[12], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[41]); - if (master_id == 0) - master_id = -1; - } - - i++; - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); - - mysql_free_result(result); - - /* If all configured slaves are running set this node as slave */ - if (isslave > 0 && isslave == i) - isslave = 1; - else - isslave = 0; - } - } else { - if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - long master_id = -1; - - if(mysql_field_count(database->con) < 40) - { - mysql_free_result(result); - - if(server_version < 5*10000 + 5*100) - { - if(database->log_version_err) - { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - " for versions less than 5.5 does not have master_server_id, " - "replication tree cannot be resolved for server %s." - " MySQL Version: %s",database->server->unique_name,version_str); - database->log_version_err = false; - } - } - else - { - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - "returned less than the expected amount of columns. Expected 40 columns." - " MySQL Version: %s",version_str); - } - return; - } - - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[10], "Yes", 3) == 0 - && strncmp(row[11], "Yes", 3) == 0) { - isslave = 1; - } - - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[10], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[39]); - if (master_id == 0) - master_id = -1; - } - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); - - mysql_free_result(result); - } - } - - /* get variable 'read_only' set by an external component */ - if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } - - while ((row = mysql_fetch_row(result))) - { - if (strncasecmp(row[1], "OFF", 3) == 0) { - ismaster = 1; - } else { - isslave = 1; - } - } - mysql_free_result(result); - } - - /* Remove addition info */ - monitor_clear_pending_status(database, SERVER_STALE_STATUS); - - /* Set the Slave Role */ - if (isslave) - { - monitor_set_pending_status(database, SERVER_SLAVE); - /* Avoid any possible stale Master state */ - monitor_clear_pending_status(database, SERVER_MASTER); - - /* Set replication depth to 1 */ - database->server->depth = 1; - } else { - /* Avoid any possible Master/Slave stale state */ - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); - } - - /* Set the Master role */ - if (ismaster) + } + else + { + if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 + && (result = mysql_store_result(database->con)) != NULL) { - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_set_pending_status(database, SERVER_MASTER); + long master_id = -1; - /* Set replication depth to 0 */ - database->server->depth = 0; + if (mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + + if (server_version < 5 * 10000 + 5 * 100) + { + if (database->log_version_err) + { + skygw_log_write(LE, "Error: \"SHOW SLAVE STATUS\" " + " for versions less than 5.5 does not have master_server_id, " + "replication tree cannot be resolved for server %s." + " MySQL Version: %s", database->server->unique_name, version_str); + database->log_version_err = false; + } + } + else + { + skygw_log_write(LE, "Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s", version_str); + } + return; + } + + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[10], "Yes", 3) == 0 + && strncmp(row[11], "Yes", 3) == 0) + { + isslave = 1; + } + + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[10], "Yes", 3) == 0) + { + /* get Master_Server_Id values */ + master_id = atol(row[39]); + if (master_id == 0) + { + master_id = -1; + } + } + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); + + mysql_free_result(result); } + } + + /* get variable 'read_only' set by an external component */ + if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } + + while ((row = mysql_fetch_row(result))) + { + if (strncasecmp(row[1], "OFF", 3) == 0) + { + ismaster = 1; + } + else + { + isslave = 1; + } + } + mysql_free_result(result); + } + + /* Remove addition info */ + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + + /* Set the Slave Role */ + if (isslave) + { + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + + /* Set replication depth to 1 */ + database->server->depth = 1; + } + else + { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + } + + /* Set the Master role */ + if (ismaster) + { + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_set_pending_status(database, SERVER_MASTER); + + /* Set replication depth to 0 */ + database->server->depth = 0; + } } @@ -522,140 +532,144 @@ char *server_string; static void monitorMain(void *arg) { - MONITOR* mon = (MONITOR*)arg; -MM_MONITOR *handle; -MONITOR_SERVERS *ptr; -int detect_stale_master = false; -MONITOR_SERVERS *root_master = NULL; -size_t nrounds = 0; + MONITOR* mon = (MONITOR*) arg; + MM_MONITOR *handle; + MONITOR_SERVERS *ptr; + int detect_stale_master = false; + MONITOR_SERVERS *root_master = NULL; + size_t nrounds = 0; -spinlock_acquire(&mon->lock); -handle = (MM_MONITOR *)mon->handle; -spinlock_release(&mon->lock); -detect_stale_master = handle->detectStaleMaster; + spinlock_acquire(&mon->lock); + handle = (MM_MONITOR *) mon->handle; + spinlock_release(&mon->lock); + detect_stale_master = handle->detectStaleMaster; - if (mysql_thread_init()) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal : mysql_thread_init failed in monitor " - "module. Exiting.\n"))); - return; - } + if (mysql_thread_init()) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Fatal : mysql_thread_init failed in monitor " + "module. Exiting.\n"))); + return; + } - handle->status = MONITOR_RUNNING; - while (1) - { - if (handle->shutdown) - { - handle->status = MONITOR_STOPPING; - mysql_thread_end(); - handle->status = MONITOR_STOPPED; - return; - } + handle->status = MONITOR_RUNNING; + while (1) + { + if (handle->shutdown) + { + handle->status = MONITOR_STOPPING; + mysql_thread_end(); + handle->status = MONITOR_STOPPED; + return; + } - /** Wait base interval */ - thread_millisleep(MON_BASE_INTERVAL_MS); - /** - * Calculate how far away the monitor interval is from its full - * cycle and if monitor interval time further than the base - * interval, then skip monitoring checks. Excluding the first - * round. - */ - if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= - MON_BASE_INTERVAL_MS) + /** Wait base interval */ + thread_millisleep(MON_BASE_INTERVAL_MS); + /** + * Calculate how far away the monitor interval is from its full + * cycle and if monitor interval time further than the base + * interval, then skip monitoring checks. Excluding the first + * round. + */ + if (nrounds != 0 && + ((nrounds * MON_BASE_INTERVAL_MS) % mon->interval) >= + MON_BASE_INTERVAL_MS) + { + nrounds += 1; + continue; + } + nrounds += 1; + + /* start from the first server in the list */ + ptr = mon->databases; + + while (ptr) + { + /* copy server status into monitor pending_status */ + ptr->pending_status = ptr->server->status; + + /* monitor current node */ + monitorDatabase(mon, ptr); + + if (mon_status_changed(ptr)) + { + dcb_hangup_foreach(ptr->server); + } + + if (mon_status_changed(ptr) || + mon_print_fail_status(ptr)) + { + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); + } + if (SERVER_IS_DOWN(ptr->server)) + { + /** Increase this server'e error count */ + ptr->mon_err_count += 1; + } + else + { + /** Reset this server's error count */ + ptr->mon_err_count = 0; + } + + ptr = ptr->next; + } + + /* Get Master server pointer */ + root_master = get_current_master(mon); + + /* Update server status from monitor pending status on that server*/ + + ptr = mon->databases; + while (ptr) + { + if (!SERVER_IN_MAINT(ptr->server)) + { + /* If "detect_stale_master" option is On, let's use the previus master */ + if (detect_stale_master && root_master && (!strcmp(ptr->server->name, root_master->server->name) && ptr->server->port == root_master->server->port) && (ptr->server->status & SERVER_MASTER) && !(ptr->pending_status & SERVER_MASTER)) { - nrounds += 1; - continue; + /* in this case server->status will not be updated from pending_status */ + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, "[mysql_mon]: root server [%s:%i] is no longer Master, let's use it again even if it could be a stale master, you have been warned!", ptr->server->name, ptr->server->port))); + /* Set the STALE bit for this server in server struct */ + server_set_status(ptr->server, SERVER_STALE_STATUS); } - nrounds += 1; + else + { + ptr->server->status = ptr->pending_status; + } + } + ptr = ptr->next; + } - /* start from the first server in the list */ - ptr = mon->databases; - - while (ptr) - { - /* copy server status into monitor pending_status */ - ptr->pending_status = ptr->server->status; - - /* monitor current node */ - monitorDatabase(mon, ptr); - - if (mon_status_changed(ptr)) - { - dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); - } - - if (mon_status_changed(ptr) || - mon_print_fail_status(ptr)) - { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Backend server %s:%d state : %s", - ptr->server->name, - ptr->server->port, - STRSRVSTATUS(ptr->server)))); - } - if (SERVER_IS_DOWN(ptr->server)) - { - /** Increase this server'e error count */ - ptr->mon_err_count += 1; - } - else - { - /** Reset this server's error count */ - ptr->mon_err_count = 0; - } - - ptr = ptr->next; - } - - /* Get Master server pointer */ - root_master = get_current_master(mon); - - /* Update server status from monitor pending status on that server*/ - - ptr = mon->databases; - while (ptr) - { - if (! SERVER_IN_MAINT(ptr->server)) { - /* If "detect_stale_master" option is On, let's use the previus master */ - if (detect_stale_master && root_master && (!strcmp(ptr->server->name, root_master->server->name) && ptr->server->port == root_master->server->port) && (ptr->server->status & SERVER_MASTER) && !(ptr->pending_status & SERVER_MASTER)) { - /* in this case server->status will not be updated from pending_status */ - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, "[mysql_mon]: root server [%s:%i] is no longer Master, let's use it again even if it could be a stale master, you have been warned!", ptr->server->name, ptr->server->port))); - /* Set the STALE bit for this server in server struct */ - server_set_status(ptr->server, SERVER_STALE_STATUS); - } else { - ptr->server->status = ptr->pending_status; - } - } - ptr = ptr->next; - } - - ptr = mon->databases; - monitor_event_t evtype; - while(ptr) - { - if(mon_status_changed(ptr)) - { - evtype = mon_get_event_type(ptr); - if(isMySQLEvent(evtype)) - { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) - { - monitor_launch_script(mon,ptr,handle->script); - } - } - } - ptr = ptr->next; - } - } + ptr = mon->databases; + monitor_event_t evtype; + while (ptr) + { + if (mon_status_changed(ptr)) + { + evtype = mon_get_event_type(ptr); + if (isMySQLEvent(evtype)) + { + skygw_log_write(LOGFILE_TRACE, "Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name, ptr->server->port, + mon_get_event_name(ptr)); + if (handle->script && handle->events[evtype]) + { + monitor_launch_script(mon, ptr, handle->script); + } + } + } + ptr = ptr->next; + } + } } /** @@ -669,9 +683,9 @@ detect_stale_master = handle->detectStaleMaster; static void detectStaleMaster(void *arg, int enable) { - MONITOR* mon = (MONITOR*)arg; -MM_MONITOR *handle = (MM_MONITOR *)mon->handle; - memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); + MONITOR* mon = (MONITOR*) arg; + MM_MONITOR *handle = (MM_MONITOR *) mon->handle; + memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); } /******* @@ -684,61 +698,72 @@ MM_MONITOR *handle = (MM_MONITOR *)mon->handle; * @return The server at root level with SERVER_MASTER bit */ -static MONITOR_SERVERS *get_current_master(MONITOR *mon) { +static MONITOR_SERVERS *get_current_master(MONITOR *mon) +{ MM_MONITOR* handle = mon->handle; -MONITOR_SERVERS *ptr; + MONITOR_SERVERS *ptr; - ptr = mon->databases; + ptr = mon->databases; - while (ptr) - { - /* The server could be in SERVER_IN_MAINT - * that means SERVER_IS_RUNNING returns 0 - * Let's check only for SERVER_IS_DOWN: server is not running - */ - if (SERVER_IS_DOWN(ptr->server)) { - ptr = ptr->next; - continue; - } + while (ptr) + { + /* The server could be in SERVER_IN_MAINT + * that means SERVER_IS_RUNNING returns 0 + * Let's check only for SERVER_IS_DOWN: server is not running + */ + if (SERVER_IS_DOWN(ptr->server)) + { + ptr = ptr->next; + continue; + } - if (ptr->server->depth == 0) { - handle->master = ptr; - } + if (ptr->server->depth == 0) + { + handle->master = ptr; + } - ptr = ptr->next; - } + ptr = ptr->next; + } - /* - * Return the root master - */ + /* + * Return the root master + */ - if (handle->master != NULL) { - /* If the root master is in MAINT, return NULL */ - if (SERVER_IN_MAINT(handle->master->server)) { - return NULL; - } else { - return handle->master; - } - } else { - return NULL; - } + if (handle->master != NULL) + { + /* If the root master is in MAINT, return NULL */ + if (SERVER_IN_MAINT(handle->master->server)) + { + return NULL; + } + else + { + return handle->master; + } + } + else + { + return NULL; + } } -static monitor_event_t mysql_events[] = { - MASTER_DOWN_EVENT, - MASTER_UP_EVENT, - SLAVE_DOWN_EVENT, - SLAVE_UP_EVENT, - SERVER_DOWN_EVENT, - SERVER_UP_EVENT, - LOST_MASTER_EVENT, - LOST_SLAVE_EVENT, - NEW_MASTER_EVENT, - NEW_SLAVE_EVENT, - MAX_MONITOR_EVENT +static monitor_event_t mysql_events[] = +{ + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + MAX_MONITOR_EVENT }; + /** * Check if the MM monitor is monitoring this event type. * @param event Event to check @@ -747,10 +772,12 @@ static monitor_event_t mysql_events[] = { bool isMySQLEvent(monitor_event_t event) { int i; - for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++) + for (i = 0; mysql_events[i] != MAX_MONITOR_EVENT; i++) { - if(event == mysql_events[i]) - return true; + if (event == mysql_events[i]) + { + return true; + } } return false; -} \ No newline at end of file +} diff --git a/server/modules/monitor/mmmon.h b/server/modules/monitor/mmmon.h index 8a9edfb56..9e1f4d962 100644 --- a/server/modules/monitor/mmmon.h +++ b/server/modules/monitor/mmmon.h @@ -17,6 +17,7 @@ * * Copyright MariaDB Corporation Ab 2015 */ + #include #include #include @@ -34,17 +35,22 @@ #include #include +/** + * @file mmmon.h - The Multi-Master monitor + */ + /** * The handle for an instance of a Multi-Master Monitor module */ -typedef struct { - SPINLOCK lock; /**< The monitor spinlock */ - pthread_t tid; /**< id of monitor thread */ - int shutdown; /**< Flag to shutdown the monitor thread */ - int status; /**< Monitor status */ - unsigned long id; /**< Monitor ID */ - int detectStaleMaster; /**< Monitor flag for Stale Master detection */ - MONITOR_SERVERS *master; /**< Master server for Master/Slave replication */ +typedef struct +{ + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + int detectStaleMaster; /**< Monitor flag for Stale Master detection */ + MONITOR_SERVERS *master; /**< Master server for Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MM_MONITOR; diff --git a/server/modules/monitor/monitor_common.c b/server/modules/monitor/monitor_common.c index c5db84fa7..8638fd2c1 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); @@ -28,7 +29,7 @@ monitor_event_t mon_name_to_event(char* tok); */ void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit) { - ptr->pending_status |= bit; + ptr->pending_status |= bit; } /** @@ -39,103 +40,102 @@ void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit) */ void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) { - ptr->pending_status &= ~bit; + ptr->pending_status &= ~bit; } - - monitor_event_t mon_get_event_type(MONITOR_SERVERS* node) +monitor_event_t mon_get_event_type(MONITOR_SERVERS* node) { unsigned int prev = node->mon_prev_status; - if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) + if ((prev & (SERVER_MASTER | SERVER_RUNNING)) == (SERVER_MASTER | SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) { - return MASTER_DOWN_EVENT; + return MASTER_DOWN_EVENT; } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + if ((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) { - return MASTER_UP_EVENT; + return MASTER_UP_EVENT; } - if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) + if ((prev & (SERVER_SLAVE | SERVER_RUNNING)) == (SERVER_SLAVE | SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) { - return SLAVE_DOWN_EVENT; + return SLAVE_DOWN_EVENT; } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + if ((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) { - return SLAVE_UP_EVENT; + return SLAVE_UP_EVENT; } /** Galera specific events */ - if((prev & (SERVER_JOINED|SERVER_RUNNING)) == (SERVER_JOINED|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) + if ((prev & (SERVER_JOINED | SERVER_RUNNING)) == (SERVER_JOINED | SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) { - return SYNCED_DOWN_EVENT; + return SYNCED_DOWN_EVENT; } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_JOINED(node->server)) + if ((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_JOINED(node->server)) { - return SYNCED_UP_EVENT; + return SYNCED_UP_EVENT; } /** NDB events*/ - if((prev & (SERVER_NDB|SERVER_RUNNING)) == (SERVER_NDB|SERVER_RUNNING) && - SERVER_IS_DOWN(node->server)) + if ((prev & (SERVER_NDB | SERVER_RUNNING)) == (SERVER_NDB | SERVER_RUNNING) && + SERVER_IS_DOWN(node->server)) { - return NDB_DOWN_EVENT; + return NDB_DOWN_EVENT; } - if((prev & (SERVER_RUNNING)) == 0 && - SERVER_IS_RUNNING(node->server) && SERVER_IS_NDB(node->server)) + if ((prev & (SERVER_RUNNING)) == 0 && + SERVER_IS_RUNNING(node->server) && SERVER_IS_NDB(node->server)) { - return NDB_UP_EVENT; + return NDB_UP_EVENT; } - if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && - SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) + if ((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server)) { - return NEW_MASTER_EVENT; + return NEW_MASTER_EVENT; } - if((prev & (SERVER_RUNNING)) == SERVER_RUNNING && - SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) + if ((prev & (SERVER_RUNNING)) == SERVER_RUNNING && + SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server)) { - return NEW_SLAVE_EVENT; + return NEW_SLAVE_EVENT; } /** Status loss events */ - if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) + if ((prev & (SERVER_RUNNING | SERVER_MASTER)) == (SERVER_RUNNING | SERVER_MASTER) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server)) { - return LOST_MASTER_EVENT; + return LOST_MASTER_EVENT; } - if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) + if ((prev & (SERVER_RUNNING | SERVER_SLAVE)) == (SERVER_RUNNING | SERVER_SLAVE) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server)) { - return LOST_SLAVE_EVENT; + return LOST_SLAVE_EVENT; } - if((prev & (SERVER_RUNNING|SERVER_JOINED)) == (SERVER_RUNNING|SERVER_JOINED) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_JOINED(node->server)) + if ((prev & (SERVER_RUNNING | SERVER_JOINED)) == (SERVER_RUNNING | SERVER_JOINED) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_JOINED(node->server)) { - return LOST_SYNCED_EVENT; + return LOST_SYNCED_EVENT; } - if((prev & (SERVER_RUNNING|SERVER_NDB)) == (SERVER_RUNNING|SERVER_NDB) && - SERVER_IS_RUNNING(node->server) && !SERVER_IS_NDB(node->server)) + if ((prev & (SERVER_RUNNING | SERVER_NDB)) == (SERVER_RUNNING | SERVER_NDB) && + SERVER_IS_RUNNING(node->server) && !SERVER_IS_NDB(node->server)) { - return LOST_NDB_EVENT; + return LOST_NDB_EVENT; } /** Generic server failure */ - if((prev & SERVER_RUNNING) == 0 && - SERVER_IS_RUNNING(node->server)) + if ((prev & SERVER_RUNNING) == 0 && + SERVER_IS_RUNNING(node->server)) { - return SERVER_UP_EVENT; + return SERVER_UP_EVENT; } - if((prev & SERVER_RUNNING) == SERVER_RUNNING && - SERVER_IS_DOWN(node->server)) + if ((prev & SERVER_RUNNING) == SERVER_RUNNING && + SERVER_IS_DOWN(node->server)) { - return SERVER_DOWN_EVENT; + return SERVER_DOWN_EVENT; } /** Something else, most likely a state that does not matter. @@ -146,102 +146,111 @@ void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit) char* mon_get_event_name(MONITOR_SERVERS* node) { - switch(mon_get_event_type(node)) + switch (mon_get_event_type(node)) { -case UNDEFINED_MONITOR_EVENT: - return "undefined"; + case UNDEFINED_MONITOR_EVENT: + return "undefined"; -case MASTER_DOWN_EVENT: - return "master_down"; + case MASTER_DOWN_EVENT: + return "master_down"; -case MASTER_UP_EVENT: - return "master_up"; + case MASTER_UP_EVENT: + return "master_up"; -case SLAVE_DOWN_EVENT: - return "slave_down"; + case SLAVE_DOWN_EVENT: + return "slave_down"; -case SLAVE_UP_EVENT: - return "slave_up"; + case SLAVE_UP_EVENT: + return "slave_up"; -case SERVER_DOWN_EVENT: - return "server_down"; + case SERVER_DOWN_EVENT: + return "server_down"; -case SERVER_UP_EVENT: - return "server_up"; + case SERVER_UP_EVENT: + return "server_up"; -case SYNCED_DOWN_EVENT: - return "synced_down"; + case SYNCED_DOWN_EVENT: + return "synced_down"; -case SYNCED_UP_EVENT: - return "synced_up"; + case SYNCED_UP_EVENT: + return "synced_up"; -case DONOR_DOWN_EVENT: - return "donor_down"; + case DONOR_DOWN_EVENT: + return "donor_down"; -case DONOR_UP_EVENT: - return "donor_up"; + case DONOR_UP_EVENT: + return "donor_up"; -case NDB_DOWN_EVENT: - return "ndb_down"; + case NDB_DOWN_EVENT: + return "ndb_down"; -case NDB_UP_EVENT: - return "ndb_up"; + case NDB_UP_EVENT: + return "ndb_up"; -case LOST_MASTER_EVENT: - return "lost_master"; + case LOST_MASTER_EVENT: + return "lost_master"; -case LOST_SLAVE_EVENT: - return "lost_slave"; + case LOST_SLAVE_EVENT: + return "lost_slave"; -case LOST_SYNCED_EVENT: - return "lost_synced"; + case LOST_SYNCED_EVENT: + return "lost_synced"; -case LOST_DONOR_EVENT: - return "lost_donor"; + case LOST_DONOR_EVENT: + return "lost_donor"; -case LOST_NDB_EVENT: - return "lost_ndb"; + case LOST_NDB_EVENT: + return "lost_ndb"; -case NEW_MASTER_EVENT: - return "new_master"; + case NEW_MASTER_EVENT: + return "new_master"; -case NEW_SLAVE_EVENT: - return "new_slave"; + case NEW_SLAVE_EVENT: + return "new_slave"; -case NEW_SYNCED_EVENT: - return "new_synced"; + case NEW_SYNCED_EVENT: + return "new_synced"; -case NEW_DONOR_EVENT: - return "new_donor"; + case NEW_DONOR_EVENT: + return "new_donor"; - case NEW_NDB_EVENT: - return "new_ndb"; + case NEW_NDB_EVENT: + return "new_ndb"; - default: - return "MONITOR_EVENT_FAILURE"; + default: + return "MONITOR_EVENT_FAILURE"; } - + } -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; } } @@ -252,23 +261,25 @@ void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len) * @return true if status has changed or false */ bool mon_status_changed( - MONITOR_SERVERS* mon_srv) + MONITOR_SERVERS* mon_srv) { - bool succp; + bool succp; - /** This is the first time the server was set with a status*/ - if (mon_srv->mon_prev_status == -1) - return false; + /** This is the first time the server was set with a status*/ + if (mon_srv->mon_prev_status == -1) + { + return false; + } - if (mon_srv->mon_prev_status != mon_srv->server->status) - { - succp = true; - } - else - { - succp = false; - } - return succp; + if (mon_srv->mon_prev_status != mon_srv->server->status) + { + succp = true; + } + else + { + succp = false; + } + return succp; } /** @@ -278,20 +289,20 @@ bool mon_status_changed( * @return true if failed status can be logged or false */ bool mon_print_fail_status( - MONITOR_SERVERS* mon_srv) + MONITOR_SERVERS* mon_srv) { - bool succp; - int errcount = mon_srv->mon_err_count; + bool succp; + int errcount = mon_srv->mon_err_count; - if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) - { - succp = true; - } - else - { - succp = false; - } - return succp; + if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0) + { + succp = true; + } + else + { + succp = false; + } + return succp; } /** @@ -302,23 +313,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, @@ -337,26 +351,28 @@ void monitor_launch_script(MONITOR* mon, MONITOR_SERVERS* ptr, char* script) * @return 0 on success. 1 when an error has occurred or an unexpected event was * found. */ -int mon_parse_event_string(bool* events, size_t count,char* string) +int mon_parse_event_string(bool* events, size_t count, char* string) { - char *tok,*saved; + char *tok, *saved; monitor_event_t event; - tok = strtok_r(string,",| ",&saved); + tok = strtok_r(string, ",| ", &saved); - if(tok == NULL) - return -1; - - while(tok) + if (tok == NULL) { - event = mon_name_to_event(tok); - if(event == UNDEFINED_MONITOR_EVENT) - { - skygw_log_write(LE,"Error: Invalid event name %s",tok); - return -1; - } - events[event] = true; - tok = strtok_r(NULL,",| ",&saved); + return -1; + } + + while (tok) + { + event = mon_name_to_event(tok); + if (event == UNDEFINED_MONITOR_EVENT) + { + skygw_log_write(LE, "Error: Invalid event name %s", tok); + return -1; + } + events[event] = true; + tok = strtok_r(NULL, ",| ", &saved); } return 0; @@ -364,54 +380,97 @@ int mon_parse_event_string(bool* events, size_t count,char* string) monitor_event_t mon_name_to_event(char* tok) { - if(!strcasecmp("master_down",tok)) - return MASTER_DOWN_EVENT; - else if(!strcasecmp("master_up",tok)) - return MASTER_UP_EVENT; - else if(!strcasecmp("slave_down",tok)) - return SLAVE_DOWN_EVENT; - else if(!strcasecmp("slave_up",tok)) - return SLAVE_UP_EVENT; - else if(!strcasecmp("server_down",tok)) - return SERVER_DOWN_EVENT; - else if(!strcasecmp("server_up",tok)) - return SERVER_UP_EVENT; - else if(!strcasecmp("synced_down",tok)) - return SYNCED_DOWN_EVENT; - else if(!strcasecmp("synced_up",tok)) - return SYNCED_UP_EVENT; - else if(!strcasecmp("donor_down",tok)) - return DONOR_DOWN_EVENT; - else if(!strcasecmp("donor_up",tok)) - return DONOR_UP_EVENT; - else if(!strcasecmp("ndb_down",tok)) - return NDB_DOWN_EVENT; - else if(!strcasecmp("ndb_up",tok)) - return NDB_UP_EVENT; - else if(!strcasecmp("lost_master",tok)) - return LOST_MASTER_EVENT; - else if(!strcasecmp("lost_slave",tok)) - return LOST_SLAVE_EVENT; - else if(!strcasecmp("lost_synced",tok)) - return LOST_SYNCED_EVENT; - else if(!strcasecmp("lost_donor",tok)) - return LOST_DONOR_EVENT; - else if(!strcasecmp("lost_ndb",tok)) - return LOST_NDB_EVENT; - else if(!strcasecmp("new_master",tok)) - return NEW_MASTER_EVENT; - else if(!strcasecmp("new_slave",tok)) - return NEW_SLAVE_EVENT; - else if(!strcasecmp("new_synced",tok)) - return NEW_SYNCED_EVENT; - else if(!strcasecmp("new_donor",tok)) - return NEW_DONOR_EVENT; - else if(!strcasecmp("new_ndb",tok)) - return NEW_NDB_EVENT; - else - return UNDEFINED_MONITOR_EVENT; - + if (!strcasecmp("master_down", tok)) + { + return MASTER_DOWN_EVENT; } + else if (!strcasecmp("master_up", tok)) + { + return MASTER_UP_EVENT; + } + else if (!strcasecmp("slave_down", tok)) + { + return SLAVE_DOWN_EVENT; + } + else if (!strcasecmp("slave_up", tok)) + { + return SLAVE_UP_EVENT; + } + else if (!strcasecmp("server_down", tok)) + { + return SERVER_DOWN_EVENT; + } + else if (!strcasecmp("server_up", tok)) + { + return SERVER_UP_EVENT; + } + else if (!strcasecmp("synced_down", tok)) + { + return SYNCED_DOWN_EVENT; + } + else if (!strcasecmp("synced_up", tok)) + { + return SYNCED_UP_EVENT; + } + else if (!strcasecmp("donor_down", tok)) + { + return DONOR_DOWN_EVENT; + } + else if (!strcasecmp("donor_up", tok)) + { + return DONOR_UP_EVENT; + } + else if (!strcasecmp("ndb_down", tok)) + { + return NDB_DOWN_EVENT; + } + else if (!strcasecmp("ndb_up", tok)) + { + return NDB_UP_EVENT; + } + else if (!strcasecmp("lost_master", tok)) + { + return LOST_MASTER_EVENT; + } + else if (!strcasecmp("lost_slave", tok)) + { + return LOST_SLAVE_EVENT; + } + else if (!strcasecmp("lost_synced", tok)) + { + return LOST_SYNCED_EVENT; + } + else if (!strcasecmp("lost_donor", tok)) + { + return LOST_DONOR_EVENT; + } + else if (!strcasecmp("lost_ndb", tok)) + { + return LOST_NDB_EVENT; + } + else if (!strcasecmp("new_master", tok)) + { + return NEW_MASTER_EVENT; + } + else if (!strcasecmp("new_slave", tok)) + { + return NEW_SLAVE_EVENT; + } + else if (!strcasecmp("new_synced", tok)) + { + return NEW_SYNCED_EVENT; + } + else if (!strcasecmp("new_donor", tok)) + { + return NEW_DONOR_EVENT; + } + else if (!strcasecmp("new_ndb", tok)) + { + return NEW_NDB_EVENT; + } + + return UNDEFINED_MONITOR_EVENT; +} /** * Connect to a database. This will always leave a valid database handle in the diff --git a/server/modules/monitor/monitor_common.h b/server/modules/monitor/monitor_common.h index 3f944befc..13b191001 100644 --- a/server/modules/monitor/monitor_common.h +++ b/server/modules/monitor/monitor_common.h @@ -38,32 +38,33 @@ /** Monitor events that are caused by servers moving from * one state to another.*/ -typedef enum { - UNDEFINED_MONITOR_EVENT, - MASTER_DOWN_EVENT, - MASTER_UP_EVENT, - SLAVE_DOWN_EVENT, - SLAVE_UP_EVENT, - SERVER_DOWN_EVENT, - SERVER_UP_EVENT, - SYNCED_DOWN_EVENT, - SYNCED_UP_EVENT, - DONOR_DOWN_EVENT, - DONOR_UP_EVENT, - NDB_DOWN_EVENT, - NDB_UP_EVENT, - LOST_MASTER_EVENT, - LOST_SLAVE_EVENT, - LOST_SYNCED_EVENT, - LOST_DONOR_EVENT, - LOST_NDB_EVENT, - NEW_MASTER_EVENT, - NEW_SLAVE_EVENT, - NEW_SYNCED_EVENT, - NEW_DONOR_EVENT, - NEW_NDB_EVENT, - MAX_MONITOR_EVENT -}monitor_event_t; +typedef enum +{ + UNDEFINED_MONITOR_EVENT, + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + SYNCED_DOWN_EVENT, + SYNCED_UP_EVENT, + DONOR_DOWN_EVENT, + DONOR_UP_EVENT, + NDB_DOWN_EVENT, + NDB_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + LOST_SYNCED_EVENT, + LOST_DONOR_EVENT, + LOST_NDB_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + NEW_SYNCED_EVENT, + NEW_DONOR_EVENT, + NEW_NDB_EVENT, + MAX_MONITOR_EVENT +} monitor_event_t; typedef enum { @@ -72,15 +73,15 @@ typedef enum MONITOR_CONN_TIMEOUT } connect_result_t; -void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len); +void mon_append_node_names(MONITOR_SERVERS* start, char* str, int len); monitor_event_t mon_get_event_type(MONITOR_SERVERS* node); char* mon_get_event_name(MONITOR_SERVERS* node); void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit); void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit); bool mon_status_changed(MONITOR_SERVERS* mon_srv); bool mon_print_fail_status(MONITOR_SERVERS* mon_srv); -void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script); -int mon_parse_event_string(bool* events, size_t count,char* string); +void monitor_launch_script(MONITOR* mon, MONITOR_SERVERS* ptr, char* script); +int mon_parse_event_string(bool* events, size_t count, char* string); connect_result_t mon_connect_to_db(MONITOR* mon, MONITOR_SERVERS *database); void mon_log_connect_error(MONITOR_SERVERS* database, connect_result_t rval); #endif diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index fccd8b767..a638b1c1f 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -47,35 +47,33 @@ * 18/11/14 Massimiliano Pinto One server only in configuration becomes master. * servers=server1 must be present in mysql_mon and in router sections as well. * 08/05/15 Markus Makela Added launchable scripts + * 17/10/15 Martin Brampton Change DCB callback to hangup * * @endverbatim */ #include +#include #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static void monitorMain(void *); +static void monitorMain(void *); static char *version_str = "V1.4.0"; -MODULE_INFO info = { - MODULE_API_MONITOR, - MODULE_GA, - MONITOR_VERSION, - "A MySQL Master/Slave replication monitor" +MODULE_INFO info = +{ + MODULE_API_MONITOR, + MODULE_GA, + MONITOR_VERSION, + "A MySQL Master/Slave replication monitor" }; -static void *startMonitor(void *,void*); -static void stopMonitor(void *); -static void diagnostics(DCB *, void *); -static void defaultId(void *, unsigned long); -static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long); -static MONITOR_SERVERS *getSlaveOfNodeId(MONITOR_SERVERS *, long); +static void *startMonitor(void *, void*); +static void stopMonitor(void *); +static void diagnostics(DCB *, void *); +static void defaultId(void *, unsigned long); +static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long); +static MONITOR_SERVERS *getSlaveOfNodeId(MONITOR_SERVERS *, long); static MONITOR_SERVERS *get_replication_tree(MONITOR *, int); static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *); static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *); @@ -85,10 +83,11 @@ void check_maxscale_schema_replication(MONITOR *monitor); static bool report_version_err = true; static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; -static MONITOR_OBJECT MyObject = { - startMonitor, - stopMonitor, - diagnostics +static MONITOR_OBJECT MyObject = +{ + startMonitor, + stopMonitor, + diagnostics }; /** @@ -99,7 +98,7 @@ static MONITOR_OBJECT MyObject = { char * version() { - return version_str; + return version_str; } /** @@ -109,10 +108,9 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise the MySQL Monitor module %s.", - version_str))); + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Initialise the MySQL Monitor module %s.", + version_str))); } /** @@ -126,7 +124,7 @@ ModuleInit() MONITOR_OBJECT * GetModuleObject() { - return &MyObject; + return &MyObject; } /** @@ -138,92 +136,78 @@ GetModuleObject() * @param opt Configuration parameters * @return A handle to use when interacting with the monitor */ -static void * +static void * startMonitor(void *arg, void* opt) { - MONITOR* monitor = (MONITOR*)arg; - MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle; - CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false,script_error = false; + MONITOR* monitor = (MONITOR*) arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) monitor->handle; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*) opt; + bool have_events = false, script_error = false; if (handle) { - handle->shutdown = 0; + handle->shutdown = 0; } else { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = config_get_gateway_id(); - handle->replicationHeartbeat = 0; - handle->detectStaleMaster = 0; - handle->master = NULL; - handle->script = NULL; - handle->mysql51_replication = false; - memset(handle->events,false,sizeof(handle->events)); - spinlock_init(&handle->lock); + if ((handle = (MYSQL_MONITOR *) malloc(sizeof(MYSQL_MONITOR))) == NULL) + return NULL; + handle->shutdown = 0; + handle->id = config_get_gateway_id(); + handle->replicationHeartbeat = 0; + handle->detectStaleMaster = 0; + handle->master = NULL; + handle->script = NULL; + handle->mysql51_replication = false; + memset(handle->events, false, sizeof(handle->events)); + spinlock_init(&handle->lock); } - while(params) + while (params) { - if(!strcmp(params->name,"detect_stale_master")) - 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,"events")) - { - if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) - script_error = true; - else - have_events = true; - } - else if(!strcmp(params->name,"mysql51_replication")) - { - handle->mysql51_replication = config_truth_value(params->value); - } - params = params->next; + if (!strcmp(params->name, "detect_stale_master")) + 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 (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) + script_error = true; + else + have_events = true; + } + else if (!strcmp(params->name, "mysql51_replication")) + { + handle->mysql51_replication = config_truth_value(params->value); + } + params = params->next; } - if(script_error) + if (script_error) { - skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " - "for the monitor '%s'. The script will not be used.",monitor->name); - free(handle->script); - handle->script = NULL; + skygw_log_write(LE, "Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.", monitor->name); + free(handle->script); + handle->script = NULL; } /** If no specific events are given, enable them all */ - if(!have_events) + if (!have_events) { - memset(handle->events,true,sizeof(handle->events)); + memset(handle->events, true, sizeof(handle->events)); } - handle->tid = (THREAD)thread_start(monitorMain, monitor); + handle->tid = (THREAD) thread_start(monitorMain, monitor); return handle; } @@ -232,14 +216,14 @@ startMonitor(void *arg, void* opt) * * @param arg Handle on thr running monior */ -static void +static void stopMonitor(void *arg) { - MONITOR* mon = (MONITOR*)arg; - MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; + MONITOR* mon = (MONITOR*) arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; handle->shutdown = 1; - thread_wait((void *)handle->tid); + thread_wait((void *) handle->tid); } /** @@ -250,44 +234,44 @@ stopMonitor(void *arg) */ static void diagnostics(DCB *dcb, void *arg) { - MONITOR* mon = (MONITOR*)arg; - MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; - MONITOR_SERVERS *db; - char *sep; + MONITOR* mon = (MONITOR*) arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; + MONITOR_SERVERS *db; + char *sep; switch (handle->status) { - case MONITOR_RUNNING: - dcb_printf(dcb, "\tMonitor running\n"); - break; - case MONITOR_STOPPING: - dcb_printf(dcb, "\tMonitor stopping\n"); - break; - case MONITOR_STOPPED: - dcb_printf(dcb, "\tMonitor stopped\n"); - break; + case MONITOR_RUNNING: + dcb_printf(dcb, "\tMonitor running\n"); + break; + case MONITOR_STOPPING: + dcb_printf(dcb, "\tMonitor stopping\n"); + break; + case MONITOR_STOPPED: + dcb_printf(dcb, "\tMonitor stopped\n"); + break; } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); - dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id); - dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled"); - dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); + dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb, "\tMaxScale MonitorId:\t%lu\n", handle->id); + dcb_printf(dcb, "\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled"); + dcb_printf(dcb, "\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled"); + dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); dcb_printf(dcb, "\tMonitored servers: "); - + db = mon->databases; sep = ""; while (db) { - dcb_printf(dcb, - "%s%s:%d", - sep, - db->server->name, - db->server->port); - sep = ", "; - db = db->next; + dcb_printf(dcb, + "%s%s:%d", + sep, + db->server->name, + db->server->port); + sep = ", "; + db = db->next; } dcb_printf(dcb, "\n"); } @@ -299,52 +283,58 @@ static inline void monitor_mysql100_db(MONITOR_SERVERS* database) MYSQL_ROW row; if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) + && (result = mysql_store_result(database->con)) != NULL) { - int i = 0; - long master_id = -1; + int i = 0; + long master_id = -1; - if(mysql_field_count(database->con) < 42) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" " - "returned less than the expected amount of columns. Expected 42 columns." - " MySQL Version: %s",version_str); - return; - } + if (mysql_field_count(database->con) < 42) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: \"SHOW ALL SLAVES STATUS\" " + "returned less than the expected amount of columns. Expected 42 columns." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[12], "Yes", 3) == 0 - && strncmp(row[13], "Yes", 3) == 0) { - isslave += 1; - } + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[12], "Yes", 3) == 0 + && strncmp(row[13], "Yes", 3) == 0) + { + isslave += 1; + } - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[12], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[41]); - if (master_id == 0) - master_id = -1; - } + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[12], "Yes", 3) == 0) + { + /* get Master_Server_Id values */ + master_id = atol(row[41]); + if (master_id == 0) + master_id = -1; + } - i++; - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); + i++; + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); - mysql_free_result(result); + mysql_free_result(result); - /* If all configured slaves are running set this node as slave */ - if (isslave > 0 && isslave == i) - isslave = 1; - else - isslave = 0; + /* If all configured slaves are running set this node as slave */ + if (isslave > 0 && isslave == i) + { + isslave = 1; + } + else + { + isslave = 0; + } } /* Remove addition info */ @@ -358,13 +348,15 @@ static inline void monitor_mysql100_db(MONITOR_SERVERS* database) /* Set the Slave Role */ if (isslave) { - monitor_set_pending_status(database, SERVER_SLAVE); - /* Avoid any possible stale Master state */ - monitor_clear_pending_status(database, SERVER_MASTER); - } else { - /* Avoid any possible Master/Slave stale state */ - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } + else + { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); } } @@ -375,42 +367,44 @@ static inline void monitor_mysql55_db(MONITOR_SERVERS* database) MYSQL_ROW row; if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) + && (result = mysql_store_result(database->con)) != NULL) { - long master_id = -1; - if(mysql_field_count(database->con) < 40) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - "returned less than the expected amount of columns. Expected 40 columns." - " MySQL Version: %s",version_str); - return; - } + long master_id = -1; + if (mysql_field_count(database->con) < 40) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 40 columns." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[10], "Yes", 3) == 0 - && strncmp(row[11], "Yes", 3) == 0) { - isslave = 1; - } + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[10], "Yes", 3) == 0 + && strncmp(row[11], "Yes", 3) == 0) + { + isslave = 1; + } - /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building - * the replication tree, slaves ids will be added to master(s) and we will have at least the - * root master server. - * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' - */ - if (strncmp(row[10], "Yes", 3) == 0) { - /* get Master_Server_Id values */ - master_id = atol(row[39]); - if (master_id == 0) - master_id = -1; - } - } - /* store master_id of current node */ - memcpy(&database->server->master_id, &master_id, sizeof(long)); + /* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building + * the replication tree, slaves ids will be added to master(s) and we will have at least the + * root master server. + * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' + */ + if (strncmp(row[10], "Yes", 3) == 0) + { + /* get Master_Server_Id values */ + master_id = atol(row[39]); + if (master_id == 0) + master_id = -1; + } + } + /* store master_id of current node */ + memcpy(&database->server->master_id, &master_id, sizeof(long)); - mysql_free_result(result); + mysql_free_result(result); } /* Remove addition info */ @@ -424,13 +418,15 @@ static inline void monitor_mysql55_db(MONITOR_SERVERS* database) /* Set the Slave Role */ if (isslave) { - monitor_set_pending_status(database, SERVER_SLAVE); - /* Avoid any possible stale Master state */ - monitor_clear_pending_status(database, SERVER_MASTER); - } else { - /* Avoid any possible Master/Slave stale state */ - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } + else + { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); } } @@ -441,27 +437,28 @@ static inline void monitor_mysql51_db(MONITOR_SERVERS* database) MYSQL_ROW row; if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0 - && (result = mysql_store_result(database->con)) != NULL) + && (result = mysql_store_result(database->con)) != NULL) { - if(mysql_field_count(database->con) < 38) - { - mysql_free_result(result); + if (mysql_field_count(database->con) < 38) + { + mysql_free_result(result); - skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" " - "returned less than the expected amount of columns. Expected 38 columns." - " MySQL Version: %s",version_str); - return; - } + skygw_log_write(LE, "Error: \"SHOW SLAVE STATUS\" " + "returned less than the expected amount of columns. Expected 38 columns." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - if (strncmp(row[10], "Yes", 3) == 0 - && strncmp(row[11], "Yes", 3) == 0) { - isslave = 1; - } - } - mysql_free_result(result); + while ((row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + if (strncmp(row[10], "Yes", 3) == 0 + && strncmp(row[11], "Yes", 3) == 0) + { + isslave = 1; + } + } + mysql_free_result(result); } /* Remove addition info */ @@ -475,13 +472,15 @@ static inline void monitor_mysql51_db(MONITOR_SERVERS* database) /* Set the Slave Role */ if (isslave) { - monitor_set_pending_status(database, SERVER_SLAVE); - /* Avoid any possible stale Master state */ - monitor_clear_pending_status(database, SERVER_MASTER); - } else { - /* Avoid any possible Master/Slave stale state */ - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); + monitor_set_pending_status(database, SERVER_SLAVE); + /* Avoid any possible stale Master state */ + monitor_clear_pending_status(database, SERVER_MASTER); + } + else + { + /* Avoid any possible Master/Slave stale state */ + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); } } @@ -496,87 +495,88 @@ static inline void monitor_mysql51_db(MONITOR_SERVERS* database) static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon) { MONITOR_SERVERS* database = mon->databases; - MONITOR_SERVERS *ptr,*rval = NULL; + MONITOR_SERVERS *ptr, *rval = NULL; int i; - while(database) + while (database) { - bool ismaster = false; - MYSQL_RES* result; - MYSQL_ROW row; - int nslaves = 0; - if(database->con) - { - if (mysql_query(database->con, "SHOW SLAVE HOSTS") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 4) - { - mysql_free_result(result); - skygw_log_write_flush(LE,"Error: \"SHOW SLAVE HOSTS\" " - "returned less than the expected amount of columns. Expected 4 columns." - " MySQL Version: %s",version_str); - return NULL; - } + bool ismaster = false; + MYSQL_RES* result; + MYSQL_ROW row; + int nslaves = 0; + if (database->con) + { + if (mysql_query(database->con, "SHOW SLAVE HOSTS") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 4) + { + mysql_free_result(result); + skygw_log_write_flush(LE, "Error: \"SHOW SLAVE HOSTS\" " + "returned less than the expected amount of columns. Expected 4 columns." + " MySQL Version: %s", version_str); + return NULL; + } - if(mysql_num_rows(result) > 0) - { - ismaster = true; - while (nslaves < MONITOR_MAX_NUM_SLAVES && (row = mysql_fetch_row(result))) - { - /* get Slave_IO_Running and Slave_SQL_Running values*/ - database->server->slaves[nslaves] = atol(row[0]); - nslaves++; - LOGIF(LD,(skygw_log_write_flush(LD,"Found slave at %s:%d",row[1],row[2]))); - } - database->server->slaves[nslaves] = 0; - } + if (mysql_num_rows(result) > 0) + { + ismaster = true; + while (nslaves < MONITOR_MAX_NUM_SLAVES && (row = mysql_fetch_row(result))) + { + /* get Slave_IO_Running and Slave_SQL_Running values*/ + database->server->slaves[nslaves] = atol(row[0]); + nslaves++; + LOGIF(LD, (skygw_log_write_flush(LD, "Found slave at %s:%s", row[1], row[2]))); + } + database->server->slaves[nslaves] = 0; + } - mysql_free_result(result); - } + mysql_free_result(result); + } - - /* Set the Slave Role */ - if (ismaster) - { - LOGIF(LD,(skygw_log_write(LD,"Master server found at %s:%d with %d slaves", - database->server->name, - database->server->port, - nslaves))); - monitor_set_pending_status(database, SERVER_MASTER); - if(rval == NULL || rval->server->node_id > database->server->node_id) - rval = database; - } - } - database = database->next; + + /* Set the Slave Role */ + if (ismaster) + { + LOGIF(LD, (skygw_log_write(LD, "Master server found at %s:%d with %d slaves", + database->server->name, + database->server->port, + nslaves))); + monitor_set_pending_status(database, SERVER_MASTER); + if (rval == NULL || rval->server->node_id > database->server->node_id) + rval = database; + } + } + database = database->next; } database = mon->databases; /** Set master server IDs */ - while(database) + while (database) { - ptr = mon->databases; + ptr = mon->databases; - while(ptr) - { - for(i = 0;ptr->server->slaves[i];i++) - { - if(ptr->server->slaves[i] == database->server->node_id) - { - database->server->master_id = ptr->server->node_id; - break; - } - } - ptr = ptr->next; - } - if(database->server->master_id <= 0 && SERVER_IS_SLAVE(database->server)) - { - monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); - } - database = database->next; + while (ptr) + { + for (i = 0; ptr->server->slaves[i]; i++) + { + if (ptr->server->slaves[i] == database->server->node_id) + { + database->server->master_id = ptr->server->node_id; + break; + } + } + ptr = ptr->next; + } + if (database->server->master_id <= 0 && SERVER_IS_SLAVE(database->server)) + { + monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + database = database->next; } return rval; } + /** * Monitor an individual server * @@ -587,71 +587,71 @@ static void monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) { MYSQL_MONITOR* handle = mon->handle; - MYSQL_ROW row; - MYSQL_RES *result; - int isslave = 0; - char *uname = mon->user; + MYSQL_ROW row; + MYSQL_RES *result; + int isslave = 0; + char *uname = mon->user; unsigned long int server_version = 0; - char *server_string; + char *server_string; if (database->server->monuser != NULL) { - uname = database->server->monuser; + uname = database->server->monuser; } if (uname == NULL) - return; + return; /* Don't probe servers in maintenance mode */ if (SERVER_IN_MAINT(database->server)) - return; + return; /** Store previous status */ database->mon_prev_status = database->server->status; if (database->con == NULL || mysql_ping(database->con) != 0) { - connect_result_t rval; - if ((rval = mon_connect_to_db(mon, database)) == MONITOR_CONN_OK) - { - server_clear_status(database->server, SERVER_AUTH_ERROR); - monitor_clear_pending_status(database, SERVER_AUTH_ERROR); - } - else - { - /* The current server is not running - * - * Store server NOT running in server and monitor server pending struct - * - */ - if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) - { - server_set_status(database->server, SERVER_AUTH_ERROR); - monitor_set_pending_status(database, SERVER_AUTH_ERROR); - } - server_clear_status(database->server, SERVER_RUNNING); - monitor_clear_pending_status(database, SERVER_RUNNING); - - /* Also clear M/S state in both server and monitor server pending struct */ - server_clear_status(database->server, SERVER_SLAVE); - server_clear_status(database->server, SERVER_MASTER); - monitor_clear_pending_status(database, SERVER_SLAVE); - monitor_clear_pending_status(database, SERVER_MASTER); - - /* Clean addition status too */ - server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER); - server_clear_status(database->server, SERVER_STALE_STATUS); - monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); - monitor_clear_pending_status(database, SERVER_STALE_STATUS); - - /* Log connect failure only once */ - if (mon_status_changed(database) && mon_print_fail_status(database)) + connect_result_t rval; + if ((rval = mon_connect_to_db(mon, database)) == MONITOR_CONN_OK) { - mon_log_connect_error(database, rval); + server_clear_status(database->server, SERVER_AUTH_ERROR); + monitor_clear_pending_status(database, SERVER_AUTH_ERROR); } + else + { + /* The current server is not running + * + * Store server NOT running in server and monitor server pending struct + * + */ + if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR) + { + server_set_status(database->server, SERVER_AUTH_ERROR); + monitor_set_pending_status(database, SERVER_AUTH_ERROR); + } + server_clear_status(database->server, SERVER_RUNNING); + monitor_clear_pending_status(database, SERVER_RUNNING); - return; - } + /* Also clear M/S state in both server and monitor server pending struct */ + server_clear_status(database->server, SERVER_SLAVE); + server_clear_status(database->server, SERVER_MASTER); + monitor_clear_pending_status(database, SERVER_SLAVE); + monitor_clear_pending_status(database, SERVER_MASTER); + + /* Clean addition status too */ + server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER); + server_clear_status(database->server, SERVER_STALE_STATUS); + monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER); + monitor_clear_pending_status(database, SERVER_STALE_STATUS); + + /* Log connect failure only once */ + if (mon_status_changed(database) && mon_print_fail_status(database)) + { + mon_log_connect_error(database, rval); + } + + return; + } } /* Store current status in both server and monitor server pending struct */ server_set_status(database->server, SERVER_RUNNING); @@ -661,62 +661,61 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) server_version = mysql_get_server_version(database->con); /* 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); + server_string = (char *) mysql_get_server_info(database->con); + 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 - && (result = mysql_store_result(database->con)) != NULL) + && (result = mysql_store_result(database->con)) != NULL) { - long server_id = -1; + long server_id = -1; - if(mysql_field_count(database->con) != 1) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." - " MySQL Version: %s",version_str); - return; - } + if (mysql_field_count(database->con) != 1) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - server_id = strtol(row[0], NULL, 10); - if ((errno == ERANGE && (server_id == LONG_MAX - || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) - { - server_id = -1; - } - database->server->node_id = server_id; - } - mysql_free_result(result); + while ((row = mysql_fetch_row(result))) + { + server_id = strtol(row[0], NULL, 10); + if ((errno == ERANGE && (server_id == LONG_MAX + || server_id == LONG_MIN)) || (errno != 0 && server_id == 0)) + { + server_id = -1; + } + database->server->node_id = server_id; + } + mysql_free_result(result); } /* Check first for MariaDB 10.x.x and get status for multi-master replication */ if (server_version >= 100000) { - monitor_mysql100_db(database); + monitor_mysql100_db(database); } - else if(server_version >= 5*10000 + 5*100) + else if (server_version >= 5 * 10000 + 5 * 100) { - monitor_mysql55_db(database); + monitor_mysql55_db(database); } else { - if(handle->mysql51_replication) - { - monitor_mysql51_db(database); - } - else if(report_version_err) - { - report_version_err = false; - skygw_log_write(LE,"Error: MySQL version is lower than 5.5 and 'mysql51_replication' option is not enabled," - " replication tree cannot be resolved. To enable MySQL 5.1 replication detection, " - "add 'mysql51_replication=true' to the monitor section."); - } + if (handle->mysql51_replication) + { + monitor_mysql51_db(database); + } + else if (report_version_err) + { + report_version_err = false; + skygw_log_write(LE, "Error: MySQL version is lower than 5.5 and 'mysql51_replication' option is not enabled," + " replication tree cannot be resolved. To enable MySQL 5.1 replication detection, " + "add 'mysql51_replication=true' to the monitor section."); + } } } @@ -730,296 +729,302 @@ static void monitorMain(void *arg) { MONITOR* mon = (MONITOR*) arg; -MYSQL_MONITOR *handle; -MONITOR_SERVERS *ptr; -int replication_heartbeat; -int detect_stale_master; -int num_servers=0; -MONITOR_SERVERS *root_master = NULL; -size_t nrounds = 0; -int log_no_master = 1; -bool heartbeat_checked = false; + MYSQL_MONITOR *handle; + MONITOR_SERVERS *ptr; + int replication_heartbeat; + int detect_stale_master; + int num_servers = 0; + MONITOR_SERVERS *root_master = NULL; + size_t nrounds = 0; + int log_no_master = 1; + bool heartbeat_checked = false; -spinlock_acquire(&mon->lock); -handle = (MYSQL_MONITOR *)mon->handle; -spinlock_release(&mon->lock); -replication_heartbeat = handle->replicationHeartbeat; -detect_stale_master = handle->detectStaleMaster; + spinlock_acquire(&mon->lock); + handle = (MYSQL_MONITOR *) mon->handle; + spinlock_release(&mon->lock); + replication_heartbeat = handle->replicationHeartbeat; + detect_stale_master = handle->detectStaleMaster; - if (mysql_thread_init()) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal : mysql_thread_init failed in monitor " - "module. Exiting.\n"))); - return; - } - handle->status = MONITOR_RUNNING; - - while (1) - { - if (handle->shutdown) - { - handle->status = MONITOR_STOPPING; - mysql_thread_end(); - handle->status = MONITOR_STOPPED; - return; - } - /** Wait base interval */ - thread_millisleep(MON_BASE_INTERVAL_MS); + if (mysql_thread_init()) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Fatal : mysql_thread_init failed in monitor " + "module. Exiting.\n"))); + return; + } + handle->status = MONITOR_RUNNING; - if (handle->replicationHeartbeat && !heartbeat_checked) - { - check_maxscale_schema_replication(mon); - heartbeat_checked = true; - } + while (1) + { + if (handle->shutdown) + { + handle->status = MONITOR_STOPPING; + mysql_thread_end(); + handle->status = MONITOR_STOPPED; + return; + } + /** Wait base interval */ + thread_millisleep(MON_BASE_INTERVAL_MS); - /** - * Calculate how far away the monitor interval is from its full - * cycle and if monitor interval time further than the base - * interval, then skip monitoring checks. Excluding the first - * round. - */ - if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= - MON_BASE_INTERVAL_MS) - { - nrounds += 1; - continue; - } - nrounds += 1; - /* reset num_servers */ - num_servers = 0; + if (handle->replicationHeartbeat && !heartbeat_checked) + { + check_maxscale_schema_replication(mon); + heartbeat_checked = true; + } - /* start from the first server in the list */ - ptr = mon->databases; + /** + * Calculate how far away the monitor interval is from its full + * cycle and if monitor interval time further than the base + * interval, then skip monitoring checks. Excluding the first + * round. + */ + if (nrounds != 0 && + ((nrounds * MON_BASE_INTERVAL_MS) % mon->interval) >= + MON_BASE_INTERVAL_MS) + { + nrounds += 1; + continue; + } + nrounds += 1; + /* reset num_servers */ + num_servers = 0; - while (ptr) - { - ptr->mon_prev_status = ptr->server->status; + /* start from the first server in the list */ + ptr = mon->databases; - /* copy server status into monitor pending_status */ - ptr->pending_status = ptr->server->status; + while (ptr) + { + ptr->mon_prev_status = ptr->server->status; - /* monitor current node */ - monitorDatabase(mon, ptr); + /* copy server status into monitor pending_status */ + ptr->pending_status = ptr->server->status; - /* reset the slave list of current node */ - if (ptr->server->slaves) { - free(ptr->server->slaves); - } - /* create a new slave list */ - ptr->server->slaves = (long *) calloc(MONITOR_MAX_NUM_SLAVES, sizeof(long)); + /* monitor current node */ + monitorDatabase(mon, ptr); - num_servers++; + /* reset the slave list of current node */ + if (ptr->server->slaves) + { + free(ptr->server->slaves); + } + /* create a new slave list */ + ptr->server->slaves = (long *) calloc(MONITOR_MAX_NUM_SLAVES, sizeof(long)); - if (mon_status_changed(ptr)) - { - if (SRV_MASTER_STATUS(ptr->mon_prev_status)) - { - /** Master failed, can't recover */ - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Server %s:%d lost the master status.", - ptr->server->name, - ptr->server->port))); - } - /** - * Here we say: If the server's state changed - * so that it isn't running or some other way - * lost cluster membership, call call-back function - * of every DCB for which such callback was - * registered for this kind of issue (DCB_REASON_...) - */ - if (!(SERVER_IS_RUNNING(ptr->server)) || - !(SERVER_IS_IN_CLUSTER(ptr->server))) - { - dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING); + num_servers++; - } - - - - - } - - if (mon_status_changed(ptr)) - { -#if defined(SS_DEBUG) - LOGIF(LT, (skygw_log_write_flush( - LOGFILE_TRACE, - "Backend server %s:%d state : %s", - ptr->server->name, - ptr->server->port, - STRSRVSTATUS(ptr->server)))); -#else - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Backend server %s:%d state : %s", - ptr->server->name, - ptr->server->port, - STRSRVSTATUS(ptr->server)))); -#endif - } - - if (SERVER_IS_DOWN(ptr->server)) - { - /** Increase this server'e error count */ - ptr->mon_err_count += 1; - } - else - { - /** Reset this server's error count */ - ptr->mon_err_count = 0; - } - - ptr = ptr->next; - } - - ptr = mon->databases; - /* if only one server is configured, that's is Master */ - if (num_servers == 1) { - if (SERVER_IS_RUNNING(ptr->server)) { - ptr->server->depth = 0; - /* status cleanup */ - monitor_clear_pending_status(ptr, SERVER_SLAVE); - - /* master status set */ - monitor_set_pending_status(ptr, SERVER_MASTER); - - ptr->server->depth = 0; - handle->master = ptr; - root_master = ptr; - } - } else { - /* Compute the replication tree */ - if(handle->mysql51_replication) - root_master = build_mysql51_replication_tree(mon); - else - root_master = get_replication_tree(mon, num_servers); - - } - - /* Update server status from monitor pending status on that server*/ - - ptr = mon->databases; - while (ptr) - { - if (! SERVER_IN_MAINT(ptr->server)) { - /* If "detect_stale_master" option is On, let's use the previus master */ - if (detect_stale_master && - root_master && - (!strcmp(ptr->server->name, root_master->server->name) && - ptr->server->port == root_master->server->port) && - (ptr->server->status & SERVER_MASTER) && - !(ptr->pending_status & SERVER_MASTER)) - { - /** - * In this case server->status will not be updated from pending_statu - * Set the STALE bit for this server in server struct - */ - server_set_status(ptr->server, SERVER_STALE_STATUS); - - /* log it once */ - if (mon_status_changed(ptr)) { - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[mysql_mon]: root server " - "[%s:%i] is no longer Master," - " let's use it again even " - " if it could be a stale master," - " you have been warned!", - ptr->server->name, - ptr->server->port))); - } - } else { - ptr->server->status = ptr->pending_status; - } - } - ptr = ptr->next; - } - - ptr = mon->databases; - monitor_event_t evtype; - while(ptr) - { - /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr)) - { - evtype = mon_get_event_type(ptr); - if(isMySQLEvent(evtype)) - { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) - { - monitor_launch_script(mon,ptr,handle->script); - } - } - } - ptr = ptr->next; - } - - /* log master detection failure of first master becomes available after failure */ - if (root_master && - mon_status_changed(root_master) && - !(root_master->server->status & SERVER_STALE_STATUS)) - { - if (root_master->pending_status & (SERVER_MASTER) && SERVER_IS_RUNNING(root_master->server)) { - if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) && - !(root_master->server->status & SERVER_MAINT)) - { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Info : A Master Server is now available: %s:%i", - root_master->server->name, - root_master->server->port))); - } - } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : No Master can be determined. Last known was %s:%i", - root_master->server->name, - root_master->server->port))); - } - log_no_master = 1; - } else { - if (!root_master && log_no_master) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : No Master can be determined"))); - log_no_master = 0; - } - } - - /* Do now the heartbeat replication set/get for MySQL Replication Consistency */ - if (replication_heartbeat && - root_master && - (SERVER_IS_MASTER(root_master->server) || - SERVER_IS_RELAY_SERVER(root_master->server))) - { - set_master_heartbeat(handle, root_master); - ptr = mon->databases; - - while (ptr) { - if( (! SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server)) - { - if (ptr->server->node_id != root_master->server->node_id && - (SERVER_IS_SLAVE(ptr->server) || - SERVER_IS_RELAY_SERVER(ptr->server))) - { - set_slave_heartbeat(mon, ptr); - } - } - ptr = ptr->next; - } + if (mon_status_changed(ptr)) + { + if (SRV_MASTER_STATUS(ptr->mon_prev_status)) + { + /** Master failed, can't recover */ + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Server %s:%d lost the master status.", + ptr->server->name, + ptr->server->port))); } - } /*< while (1) */ + /** + * Here we say: If the server's state changed + * so that it isn't running or some other way + * lost cluster membership, call call-back function + * of every DCB for which such callback was + * registered for this kind of issue (DCB_REASON_...) + */ + if (!(SERVER_IS_RUNNING(ptr->server)) || + !(SERVER_IS_IN_CLUSTER(ptr->server))) + { + dcb_hangup_foreach(ptr->server); + } + + + + + } + + if (mon_status_changed(ptr)) + { +#if defined(SS_DEBUG) + LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); +#else + LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); +#endif + } + + if (SERVER_IS_DOWN(ptr->server)) + { + /** Increase this server'e error count */ + ptr->mon_err_count += 1; + } + else + { + /** Reset this server's error count */ + ptr->mon_err_count = 0; + } + + ptr = ptr->next; + } + + ptr = mon->databases; + /* if only one server is configured, that's is Master */ + if (num_servers == 1) + { + if (SERVER_IS_RUNNING(ptr->server)) + { + ptr->server->depth = 0; + /* status cleanup */ + monitor_clear_pending_status(ptr, SERVER_SLAVE); + + /* master status set */ + monitor_set_pending_status(ptr, SERVER_MASTER); + + ptr->server->depth = 0; + handle->master = ptr; + root_master = ptr; + } + } + else + { + /* Compute the replication tree */ + if (handle->mysql51_replication) + root_master = build_mysql51_replication_tree(mon); + else + root_master = get_replication_tree(mon, num_servers); + + } + + /* Update server status from monitor pending status on that server*/ + + ptr = mon->databases; + while (ptr) + { + if (!SERVER_IN_MAINT(ptr->server)) + { + /* If "detect_stale_master" option is On, let's use the previus master */ + if (detect_stale_master && + root_master && + (!strcmp(ptr->server->name, root_master->server->name) && + ptr->server->port == root_master->server->port) && + (ptr->server->status & SERVER_MASTER) && + !(ptr->pending_status & SERVER_MASTER)) + { + /** + * In this case server->status will not be updated from pending_statu + * Set the STALE bit for this server in server struct + */ + server_set_status(ptr->server, SERVER_STALE_STATUS); + + /* log it once */ + if (mon_status_changed(ptr)) + { + LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, + "[mysql_mon]: root server " + "[%s:%i] is no longer Master," + " let's use it again even " + " if it could be a stale master," + " you have been warned!", + ptr->server->name, + ptr->server->port))); + } + } + else + { + ptr->server->status = ptr->pending_status; + } + } + ptr = ptr->next; + } + + ptr = mon->databases; + monitor_event_t evtype; + while (ptr) + { + /** Execute monitor script if a server state has changed */ + if (mon_status_changed(ptr)) + { + evtype = mon_get_event_type(ptr); + if (isMySQLEvent(evtype)) + { + skygw_log_write(LOGFILE_TRACE, "Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name, ptr->server->port, + mon_get_event_name(ptr)); + if (handle->script && handle->events[evtype]) + { + monitor_launch_script(mon, ptr, handle->script); + } + } + } + ptr = ptr->next; + } + + /* log master detection failure of first master becomes available after failure */ + if (root_master && + mon_status_changed(root_master) && + !(root_master->server->status & SERVER_STALE_STATUS)) + { + if (root_master->pending_status & (SERVER_MASTER) && SERVER_IS_RUNNING(root_master->server)) + { + if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) && + !(root_master->server->status & SERVER_MAINT)) + { + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "Info : A Master Server is now available: %s:%i", + root_master->server->name, + root_master->server->port))); + } + } + else + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : No Master can be determined. Last known was %s:%i", + root_master->server->name, + root_master->server->port))); + } + log_no_master = 1; + } + else + { + if (!root_master && log_no_master) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : No Master can be determined"))); + log_no_master = 0; + } + } + + /* Do now the heartbeat replication set/get for MySQL Replication Consistency */ + if (replication_heartbeat && + root_master && + (SERVER_IS_MASTER(root_master->server) || + SERVER_IS_RELAY_SERVER(root_master->server))) + { + set_master_heartbeat(handle, root_master); + ptr = mon->databases; + + while (ptr) + { + if ((!SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server)) + { + if (ptr->server->node_id != root_master->server->node_id && + (SERVER_IS_SLAVE(ptr->server) || + SERVER_IS_RELAY_SERVER(ptr->server))) + { + set_slave_heartbeat(mon, ptr); + } + } + ptr = ptr->next; + } + } + } /*< while (1) */ } - + /** * Set the default id to use in the monitor. * @@ -1028,10 +1033,10 @@ detect_stale_master = handle->detectStaleMaster; */ static void defaultId(void *arg, unsigned long id) - { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->id, &id, sizeof(unsigned long)); - } +{ + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; + memcpy(&handle->id, &id, sizeof(unsigned long)); +} /** * Enable/Disable the MySQL Replication hearbeat, detecting slave lag behind master. @@ -1042,8 +1047,8 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void replicationHeartbeat(void *arg, int enable) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->replicationHeartbeat, &enable, sizeof(int)); + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; + memcpy(&handle->replicationHeartbeat, &enable, sizeof(int)); } /** @@ -1057,8 +1062,8 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; static void detectStaleMaster(void *arg, int enable) { -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; - memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; + memcpy(&handle->detectStaleMaster, &enable, sizeof(int)); } /** @@ -1069,17 +1074,19 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; * @return The server with the required server_id */ static MONITOR_SERVERS * -getServerByNodeId(MONITOR_SERVERS *ptr, long node_id) { - SERVER *current; - while (ptr) +getServerByNodeId(MONITOR_SERVERS *ptr, long node_id) +{ + SERVER *current; + while (ptr) + { + current = ptr->server; + if (current->node_id == node_id) { - current = ptr->server; - if (current->node_id == node_id) { - return ptr; - } - ptr = ptr->next; + return ptr; } - return NULL; + ptr = ptr->next; + } + return NULL; } /** @@ -1090,17 +1097,19 @@ getServerByNodeId(MONITOR_SERVERS *ptr, long node_id) { * @return The slave server of this node_id */ static MONITOR_SERVERS * -getSlaveOfNodeId(MONITOR_SERVERS *ptr, long node_id) { - SERVER *current; - while (ptr) +getSlaveOfNodeId(MONITOR_SERVERS *ptr, long node_id) +{ + SERVER *current; + while (ptr) + { + current = ptr->server; + if (current->master_id == node_id) { - current = ptr->server; - if (current->master_id == node_id) { - return ptr; - } - ptr = ptr->next; + return ptr; } - return NULL; + ptr = ptr->next; + } + return NULL; } /******* @@ -1111,107 +1120,113 @@ getSlaveOfNodeId(MONITOR_SERVERS *ptr, long node_id) { * @param handle The monitor handle * @param database The number database server */ -static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) { - unsigned long id = handle->id; - time_t heartbeat; - time_t purge_time; - char heartbeat_insert_query[512]=""; - char heartbeat_purge_query[512]=""; +static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) +{ + unsigned long id = handle->id; + time_t heartbeat; + time_t purge_time; + char heartbeat_insert_query[512] = ""; + char heartbeat_purge_query[512] = ""; - if (handle->master == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: set_master_heartbeat called without an available Master server"))); - return; - } + if (handle->master == NULL) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: set_master_heartbeat called without an available Master server"))); + return; + } - /* create the maxscale_schema database */ - if (mysql_query(database->con, "CREATE DATABASE IF NOT EXISTS maxscale_schema")) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: Error creating maxscale_schema database in Master server" - ": %s", mysql_error(database->con)))); + /* create the maxscale_schema database */ + if (mysql_query(database->con, "CREATE DATABASE IF NOT EXISTS maxscale_schema")) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: Error creating maxscale_schema database in Master server" + ": %s", mysql_error(database->con)))); - database->server->rlag = -1; - } + database->server->rlag = -1; + } - /* create repl_heartbeat table in maxscale_schema database */ - if (mysql_query(database->con, "CREATE TABLE IF NOT EXISTS " - "maxscale_schema.replication_heartbeat " - "(maxscale_id INT NOT NULL, " - "master_server_id INT NOT NULL, " - "master_timestamp INT UNSIGNED NOT NULL, " - "PRIMARY KEY ( master_server_id, maxscale_id ) ) " - "ENGINE=MYISAM DEFAULT CHARSET=latin1")) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: Error creating maxscale_schema.replication_heartbeat table in Master server" - ": %s", mysql_error(database->con)))); + /* create repl_heartbeat table in maxscale_schema database */ + if (mysql_query(database->con, "CREATE TABLE IF NOT EXISTS " + "maxscale_schema.replication_heartbeat " + "(maxscale_id INT NOT NULL, " + "master_server_id INT NOT NULL, " + "master_timestamp INT UNSIGNED NOT NULL, " + "PRIMARY KEY ( master_server_id, maxscale_id ) ) " + "ENGINE=MYISAM DEFAULT CHARSET=latin1")) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: Error creating maxscale_schema.replication_heartbeat table in Master server" + ": %s", mysql_error(database->con)))); - database->server->rlag = -1; - } + database->server->rlag = -1; + } - /* auto purge old values after 48 hours*/ - purge_time = time(0) - (3600 * 48); + /* auto purge old values after 48 hours*/ + purge_time = time(0) - (3600 * 48); - sprintf(heartbeat_purge_query, "DELETE FROM maxscale_schema.replication_heartbeat WHERE master_timestamp < %lu", purge_time); + sprintf(heartbeat_purge_query, "DELETE FROM maxscale_schema.replication_heartbeat WHERE master_timestamp < %lu", purge_time); - if (mysql_query(database->con, heartbeat_purge_query)) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: Error deleting from maxscale_schema.replication_heartbeat table: [%s], %s", - heartbeat_purge_query, - mysql_error(database->con)))); - } + if (mysql_query(database->con, heartbeat_purge_query)) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: Error deleting from maxscale_schema.replication_heartbeat table: [%s], %s", + heartbeat_purge_query, + mysql_error(database->con)))); + } - heartbeat = time(0); + heartbeat = time(0); - /* set node_ts for master as time(0) */ - database->server->node_ts = heartbeat; + /* set node_ts for master as time(0) */ + database->server->node_ts = heartbeat; - sprintf(heartbeat_insert_query, "UPDATE maxscale_schema.replication_heartbeat SET master_timestamp = %lu WHERE master_server_id = %li AND maxscale_id = %lu", heartbeat, handle->master->server->node_id, id); + sprintf(heartbeat_insert_query, "UPDATE maxscale_schema.replication_heartbeat SET master_timestamp = %lu WHERE master_server_id = %li AND maxscale_id = %lu", heartbeat, handle->master->server->node_id, id); - /* Try to insert MaxScale timestamp into master */ - if (mysql_query(database->con, heartbeat_insert_query)) { + /* Try to insert MaxScale timestamp into master */ + if (mysql_query(database->con, heartbeat_insert_query)) + { - database->server->rlag = -1; + database->server->rlag = -1; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: Error updating maxscale_schema.replication_heartbeat table: [%s], %s", - heartbeat_insert_query, - mysql_error(database->con)))); - } else { - if (mysql_affected_rows(database->con) == 0) { - heartbeat = time(0); - sprintf(heartbeat_insert_query, "REPLACE INTO maxscale_schema.replication_heartbeat (master_server_id, maxscale_id, master_timestamp ) VALUES ( %li, %lu, %lu)", handle->master->server->node_id, id, heartbeat); + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: Error updating maxscale_schema.replication_heartbeat table: [%s], %s", + heartbeat_insert_query, + mysql_error(database->con)))); + } + else + { + if (mysql_affected_rows(database->con) == 0) + { + heartbeat = time(0); + sprintf(heartbeat_insert_query, "REPLACE INTO maxscale_schema.replication_heartbeat (master_server_id, maxscale_id, master_timestamp ) VALUES ( %li, %lu, %lu)", handle->master->server->node_id, id, heartbeat); - if (mysql_query(database->con, heartbeat_insert_query)) { + if (mysql_query(database->con, heartbeat_insert_query)) + { - database->server->rlag = -1; + database->server->rlag = -1; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: Error inserting into maxscale_schema.replication_heartbeat table: [%s], %s", - heartbeat_insert_query, - mysql_error(database->con)))); - } else { - /* Set replication lag to 0 for the master */ - database->server->rlag = 0; + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: Error inserting into maxscale_schema.replication_heartbeat table: [%s], %s", + heartbeat_insert_query, + mysql_error(database->con)))); + } + else + { + /* Set replication lag to 0 for the master */ + database->server->rlag = 0; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "[mysql_mon]: heartbeat table inserted data for %s:%i", database->server->name, database->server->port))); - } - } else { - /* Set replication lag as 0 for the master */ - database->server->rlag = 0; + LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, + "[mysql_mon]: heartbeat table inserted data for %s:%i", database->server->name, database->server->port))); + } + } + else + { + /* Set replication lag as 0 for the master */ + database->server->rlag = 0; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "[mysql_mon]: heartbeat table updated for Master %s:%i", database->server->name, database->server->port))); - } - } + LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, + "[mysql_mon]: heartbeat table updated for Master %s:%i", database->server->name, database->server->port))); + } + } } /******* @@ -1222,97 +1237,108 @@ static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *databas * @param handle The monitor handle * @param database The number database server */ -static void set_slave_heartbeat(MONITOR* mon, MONITOR_SERVERS *database) { - MYSQL_MONITOR *handle = (MYSQL_MONITOR*)mon->handle; - unsigned long id = handle->id; - time_t heartbeat; - char select_heartbeat_query[256] = ""; - MYSQL_ROW row; - MYSQL_RES *result; +static void set_slave_heartbeat(MONITOR* mon, MONITOR_SERVERS *database) +{ + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) mon->handle; + unsigned long id = handle->id; + time_t heartbeat; + char select_heartbeat_query[256] = ""; + MYSQL_ROW row; + MYSQL_RES *result; - if (handle->master == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: set_slave_heartbeat called without an available Master server"))); - return; - } + if (handle->master == NULL) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: set_slave_heartbeat called without an available Master server"))); + return; + } - /* Get the master_timestamp value from maxscale_schema.replication_heartbeat table */ + /* Get the master_timestamp value from maxscale_schema.replication_heartbeat table */ - sprintf(select_heartbeat_query, "SELECT master_timestamp " - "FROM maxscale_schema.replication_heartbeat " - "WHERE maxscale_id = %lu AND master_server_id = %li", - id, handle->master->server->node_id); + sprintf(select_heartbeat_query, "SELECT master_timestamp " + "FROM maxscale_schema.replication_heartbeat " + "WHERE maxscale_id = %lu AND master_server_id = %li", + id, handle->master->server->node_id); - /* if there is a master then send the query to the slave with master_id */ - if (handle->master !=NULL && (mysql_query(database->con, select_heartbeat_query) == 0 - && (result = mysql_store_result(database->con)) != NULL)) { - int rows_found = 0; + /* if there is a master then send the query to the slave with master_id */ + if (handle->master != NULL && (mysql_query(database->con, select_heartbeat_query) == 0 + && (result = mysql_store_result(database->con)) != NULL)) + { + int rows_found = 0; - while ((row = mysql_fetch_row(result))) { - int rlag = -1; - time_t slave_read; + while ((row = mysql_fetch_row(result))) + { + int rlag = -1; + time_t slave_read; - rows_found = 1; + rows_found = 1; - heartbeat = time(0); - slave_read = strtoul(row[0], NULL, 10); + heartbeat = time(0); + slave_read = strtoul(row[0], NULL, 10); - if ((errno == ERANGE && (slave_read == LONG_MAX || slave_read == LONG_MIN)) || (errno != 0 && slave_read == 0)) { - slave_read = 0; - } + if ((errno == ERANGE && (slave_read == LONG_MAX || slave_read == LONG_MIN)) || (errno != 0 && slave_read == 0)) + { + slave_read = 0; + } - if (slave_read) { - /* set the replication lag */ - rlag = heartbeat - slave_read; - } + if (slave_read) + { + /* set the replication lag */ + rlag = heartbeat - slave_read; + } - /* set this node_ts as master_timestamp read from replication_heartbeat table */ - database->server->node_ts = slave_read; + /* set this node_ts as master_timestamp read from replication_heartbeat table */ + database->server->node_ts = slave_read; - if (rlag >= 0) { - /* store rlag only if greater than monitor sampling interval */ - database->server->rlag = (rlag > (mon->interval / 1000)) ? rlag : 0; - } else { - database->server->rlag = -1; - } + if (rlag >= 0) + { + /* store rlag only if greater than monitor sampling interval */ + database->server->rlag = (rlag > (mon->interval / 1000)) ? rlag : 0; + } + else + { + database->server->rlag = -1; + } - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "[mysql_mon]: replication heartbeat: " - "Slave %s:%i has %i seconds lag", - database->server->name, - database->server->port, - database->server->rlag))); - } - if (!rows_found) { - database->server->rlag = -1; - database->server->node_ts = 0; - } + LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, + "[mysql_mon]: replication heartbeat: " + "Slave %s:%i has %i seconds lag", + database->server->name, + database->server->port, + database->server->rlag))); + } + if (!rows_found) + { + database->server->rlag = -1; + database->server->node_ts = 0; + } - mysql_free_result(result); - } else { - database->server->rlag = -1; - database->server->node_ts = 0; + mysql_free_result(result); + } + else + { + database->server->rlag = -1; + database->server->node_ts = 0; - if (handle->master->server->node_id < 0) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: error: replication heartbeat: " - "master_server_id NOT available for %s:%i", - database->server->name, - database->server->port))); - } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "[mysql_mon]: error: replication heartbeat: " - "failed selecting from hearthbeat table of %s:%i : [%s], %s", - database->server->name, - database->server->port, - select_heartbeat_query, - mysql_error(database->con)))); - } - } + if (handle->master->server->node_id < 0) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: error: replication heartbeat: " + "master_server_id NOT available for %s:%i", + database->server->name, + database->server->port))); + } + else + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "[mysql_mon]: error: replication heartbeat: " + "failed selecting from hearthbeat table of %s:%i : [%s], %s", + database->server->name, + database->server->port, + select_heartbeat_query, + mysql_error(database->con)))); + } + } } /******* @@ -1326,107 +1352,133 @@ static void set_slave_heartbeat(MONITOR* mon, MONITOR_SERVERS *database) { * @return The server at root level with SERVER_MASTER bit */ -static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers) { - MYSQL_MONITOR* handle = (MYSQL_MONITOR*)mon->handle; - MONITOR_SERVERS *ptr; - MONITOR_SERVERS *backend; - SERVER *current; - int depth=0; - long node_id; - int root_level; +static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers) +{ + MYSQL_MONITOR* handle = (MYSQL_MONITOR*) mon->handle; + MONITOR_SERVERS *ptr; + MONITOR_SERVERS *backend; + SERVER *current; + int depth = 0; + long node_id; + int root_level; - ptr = mon->databases; - root_level = num_servers; + ptr = mon->databases; + root_level = num_servers; - while (ptr) - { - /* The server could be in SERVER_IN_MAINT - * that means SERVER_IS_RUNNING returns 0 - * Let's check only for SERVER_IS_DOWN: server is not running - */ - if (SERVER_IS_DOWN(ptr->server)) { - ptr = ptr->next; - continue; - } - depth = 0; - current = ptr->server; + while (ptr) + { + /* The server could be in SERVER_IN_MAINT + * that means SERVER_IS_RUNNING returns 0 + * Let's check only for SERVER_IS_DOWN: server is not running + */ + if (SERVER_IS_DOWN(ptr->server)) + { + ptr = ptr->next; + continue; + } + depth = 0; + current = ptr->server; - node_id = current->master_id; - if (node_id < 1) { - MONITOR_SERVERS *find_slave; - find_slave = getSlaveOfNodeId(mon->databases, current->node_id); + node_id = current->master_id; + if (node_id < 1) + { + MONITOR_SERVERS *find_slave; + find_slave = getSlaveOfNodeId(mon->databases, current->node_id); - if (find_slave == NULL) { - current->depth = -1; - ptr = ptr->next; + if (find_slave == NULL) + { + current->depth = -1; + ptr = ptr->next; - continue; - } else { - current->depth = 0; - } - } else { - depth++; - } + continue; + } + else + { + current->depth = 0; + } + } + else + { + depth++; + } - while(depth <= num_servers) { - /* set the root master at lowest depth level */ - if (current->depth > -1 && current->depth < root_level) { - root_level = current->depth; - handle->master = ptr; - } - backend = getServerByNodeId(mon->databases, node_id); + while (depth <= num_servers) + { + /* set the root master at lowest depth level */ + if (current->depth > -1 && current->depth < root_level) + { + root_level = current->depth; + handle->master = ptr; + } + backend = getServerByNodeId(mon->databases, node_id); - if (backend) { - node_id = backend->server->master_id; - } else { - node_id = -1; - } + if (backend) + { + node_id = backend->server->master_id; + } + else + { + node_id = -1; + } - if (node_id > 0) { - current->depth = depth + 1; - depth++; + if (node_id > 0) + { + current->depth = depth + 1; + depth++; - } else { - MONITOR_SERVERS *master; - current->depth = depth; + } + else + { + MONITOR_SERVERS *master; + current->depth = depth; - master = getServerByNodeId(mon->databases, current->master_id); - if (master && master->server && master->server->node_id > 0) { - add_slave_to_master(master->server->slaves, MONITOR_MAX_NUM_SLAVES, current->node_id); - master->server->depth = current->depth -1; - monitor_set_pending_status(master, SERVER_MASTER); - handle->master = master; - } else { - if (current->master_id > 0) { - /* this server is slave of another server not in MaxScale configuration - * we cannot use it as a real slave. - */ - monitor_clear_pending_status(ptr, SERVER_SLAVE); - monitor_set_pending_status(ptr, SERVER_SLAVE_OF_EXTERNAL_MASTER); - } - } - break; - } + master = getServerByNodeId(mon->databases, current->master_id); + if (master && master->server && master->server->node_id > 0) + { + add_slave_to_master(master->server->slaves, MONITOR_MAX_NUM_SLAVES, current->node_id); + master->server->depth = current->depth - 1; + monitor_set_pending_status(master, SERVER_MASTER); + handle->master = master; + } + else + { + if (current->master_id > 0) + { + /* this server is slave of another server not in MaxScale configuration + * we cannot use it as a real slave. + */ + monitor_clear_pending_status(ptr, SERVER_SLAVE); + monitor_set_pending_status(ptr, SERVER_SLAVE_OF_EXTERNAL_MASTER); + } + } + break; + } - } + } - ptr = ptr->next; - } + ptr = ptr->next; + } - /* - * Return the root master - */ + /* + * Return the root master + */ - if (handle->master != NULL) { - /* If the root master is in MAINT, return NULL */ - if (SERVER_IN_MAINT(handle->master->server)) { - return NULL; - } else { - return handle->master; - } - } else { - return NULL; - } + if (handle->master != NULL) + { + /* If the root master is in MAINT, return NULL */ + if (SERVER_IN_MAINT(handle->master->server)) + { + return NULL; + } + else + { + return handle->master; + } + } + else + { + return NULL; + } } /******* @@ -1438,30 +1490,34 @@ static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers) { * @param node_id The node_id of the slave to be inserted * @return 1 for inserted value and 0 otherwise */ -static int add_slave_to_master(long *slaves_list, int list_size, long node_id) { - int i; - for (i = 0; i< list_size; i++) { - if (slaves_list[i] == 0) { - memcpy(&slaves_list[i], &node_id, sizeof(long)); - return 1; - } +static int add_slave_to_master(long *slaves_list, int list_size, long node_id) +{ + for (int i = 0; i < list_size; i++) + { + if (slaves_list[i] == 0) + { + memcpy(&slaves_list[i], &node_id, sizeof(long)); + return 1; } - return 0; + } + return 0; } -static monitor_event_t mysql_events[] = { - MASTER_DOWN_EVENT, - MASTER_UP_EVENT, - SLAVE_DOWN_EVENT, - SLAVE_UP_EVENT, - SERVER_DOWN_EVENT, - SERVER_UP_EVENT, - LOST_MASTER_EVENT, - LOST_SLAVE_EVENT, - NEW_MASTER_EVENT, - NEW_SLAVE_EVENT, - MAX_MONITOR_EVENT +static monitor_event_t mysql_events[] = +{ + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + MAX_MONITOR_EVENT }; + /** * Check if the MySQL monitor is monitoring this event type. * @param event Event to check @@ -1470,10 +1526,10 @@ static monitor_event_t mysql_events[] = { bool isMySQLEvent(monitor_event_t event) { int i; - for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++) + for (i = 0; mysql_events[i] != MAX_MONITOR_EVENT; i++) { - if(event == mysql_events[i]) - return true; + if (event == mysql_events[i]) + return true; } return false; } @@ -1612,7 +1668,6 @@ bool check_replicate_wild_do_table(MONITOR_SERVERS* database) return rval; } - /** * Check if replicate_wild_ignore_table is defined and if it matches * maxscale_schema.replication_heartbeat. diff --git a/server/modules/monitor/mysqlmon.h b/server/modules/monitor/mysqlmon.h index 227b693c9..eaa640a94 100644 --- a/server/modules/monitor/mysqlmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -17,6 +17,7 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ + #include #include #include @@ -33,44 +34,46 @@ #include #include #include + /** - * @file mysqlmon.h - The MySQL monitor functionality within the gateway + * @file mysqlmon.h - The MySQL monitor * * @verbatim * Revision History * - * Date Who Description - * 08/07/13 Mark Riddoch Initial implementation - * 26/05/14 Massimiliano Pinto Default values for MONITOR_INTERVAL - * 28/05/14 Massimiliano Pinto Addition of new fields in MYSQL_MONITOR struct - * 24/06/14 Massimiliano Pinto Addition of master field in MYSQL_MONITOR struct and MONITOR_MAX_NUM_SLAVES - * 28/08/14 Massimiliano Pinto Addition of detectStaleMaster - * 30/10/14 Massimiliano Pinto Addition of disableMasterFailback - * 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write - * 20/04/15 Guillaume Lefranc Addition of availableWhenDonor - * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting - * 07/05/15 Markus Makela Addition of command execution on Master server failure + * Date Who Description + * 08/07/13 Mark Riddoch Initial implementation + * 26/05/14 Massimiliano Pinto Default values for MONITOR_INTERVAL + * 28/05/14 Massimiliano Pinto Addition of new fields in MYSQL_MONITOR struct + * 24/06/14 Massimiliano Pinto Addition of master field in MYSQL_MONITOR struct and MONITOR_MAX_NUM_SLAVES + * 28/08/14 Massimiliano Pinto Addition of detectStaleMaster + * 30/10/14 Massimiliano Pinto Addition of disableMasterFailback + * 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write + * 20/04/15 Guillaume Lefranc Addition of availableWhenDonor + * 22/04/15 Martin Brampton Addition of disableMasterRoleSetting + * 07/05/15 Markus Makela Addition of command execution on Master server failure * @endverbatim */ /** * The handle for an instance of a MySQL Monitor module */ -typedef struct { - SPINLOCK lock; /**< The monitor spinlock */ - pthread_t tid; /**< id of monitor thread */ - int shutdown; /**< Flag to shutdown the monitor thread */ - int status; /**< Monitor status */ - unsigned long id; /**< Monitor ID */ - int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ - int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ - int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ - int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ - int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ - bool mysql51_replication; /**< Use MySQL 5.1 replication */ - MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - char* script; /*< Script to call when state changes occur on servers */ - bool events[MAX_MONITOR_EVENT]; /*< enabled events */ +typedef struct +{ + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ + int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ + int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ + int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ + int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ + bool mysql51_replication; /**< Use MySQL 5.1 replication */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ + char* script; /*< Script to call when state changes occur on servers */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MYSQL_MONITOR; #endif diff --git a/server/modules/monitor/ndbclustermon.c b/server/modules/monitor/ndbclustermon.c index d3cb40db3..5dc810c0b 100644 --- a/server/modules/monitor/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon.c @@ -33,31 +33,28 @@ #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - -static void monitorMain(void *); +static void monitorMain(void *); static char *version_str = "V2.1.0"; -MODULE_INFO info = { - MODULE_API_MONITOR, - MODULE_BETA_RELEASE, - MONITOR_VERSION, - "A MySQL cluster SQL node monitor" +MODULE_INFO info = +{ + MODULE_API_MONITOR, + MODULE_BETA_RELEASE, + MONITOR_VERSION, + "A MySQL cluster SQL node monitor" }; -static void *startMonitor(void *,void*); -static void stopMonitor(void *); -static void diagnostics(DCB *, void *); +static void *startMonitor(void *, void*); +static void stopMonitor(void *); +static void diagnostics(DCB *, void *); bool isNdbEvent(monitor_event_t event); -static MONITOR_OBJECT MyObject = { - startMonitor, - stopMonitor, - diagnostics +static MONITOR_OBJECT MyObject = +{ + startMonitor, + stopMonitor, + diagnostics }; /** @@ -68,7 +65,7 @@ static MONITOR_OBJECT MyObject = { char * version() { - return version_str; + return version_str; } /** @@ -78,10 +75,10 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise the MySQL Cluster Monitor module %s.\n", - version_str))); + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, + "Initialise the MySQL Cluster Monitor module %s.\n", + version_str))); } /** @@ -95,7 +92,7 @@ ModuleInit() MONITOR_OBJECT * GetModuleObject() { - return &MyObject; + return &MyObject; } /** @@ -105,79 +102,72 @@ GetModuleObject() * * @return A handle to use when interacting with the monitor */ -static void * -startMonitor(void *arg,void* opt) +static void * +startMonitor(void *arg, void* opt) { - MONITOR* mon = (MONITOR*)arg; + MONITOR* mon = (MONITOR*) arg; MYSQL_MONITOR *handle = mon->handle; - CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt; - bool have_events = false,script_error = false; + CONFIG_PARAMETER* params = (CONFIG_PARAMETER*) opt; + bool have_events = false, script_error = false; if (handle != NULL) { - handle->shutdown = 0; + handle->shutdown = 0; } else { - if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) - return NULL; - handle->shutdown = 0; - handle->id = MONITOR_DEFAULT_ID; - handle->script = NULL; - handle->master = NULL; - memset(handle->events,false,sizeof(handle->events)); - spinlock_init(&handle->lock); + if ((handle = (MYSQL_MONITOR *) malloc(sizeof(MYSQL_MONITOR))) == NULL) + { + return NULL; + } + handle->shutdown = 0; + handle->id = MONITOR_DEFAULT_ID; + handle->script = NULL; + handle->master = NULL; + memset(handle->events, false, sizeof(handle->events)); + spinlock_init(&handle->lock); } - while(params) + while (params) { - 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,"events")) - { - if(mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value) != 0) - script_error = true; - else - have_events = true; - } - params = params->next; + 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) + { + script_error = true; + } + else + { + have_events = true; + } + } + params = params->next; } - if(script_error) + if (script_error) { - skygw_log_write(LE,"Error: Errors were found in the script configuration parameters " - "for the monitor '%s'. The script will not be used.",mon->name); - free(handle->script); - handle->script = NULL; + skygw_log_write(LE, "Error: Errors were found in the script configuration parameters " + "for the monitor '%s'. The script will not be used.", mon->name); + free(handle->script); + handle->script = NULL; } /** If no specific events are given, enable them all */ - if(!have_events) + if (!have_events) { - memset(handle->events,true,sizeof(handle->events)); + memset(handle->events, true, sizeof(handle->events)); } - handle->tid = (THREAD)thread_start(monitorMain, mon); + handle->tid = (THREAD) thread_start(monitorMain, mon); return handle; } @@ -186,14 +176,14 @@ startMonitor(void *arg,void* opt) * * @param arg Handle on thr running monior */ -static void +static void stopMonitor(void *arg) { - MONITOR* mon = (MONITOR*)arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; + MONITOR* mon = (MONITOR*) arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; - handle->shutdown = 1; - thread_wait((void *)handle->tid); + handle->shutdown = 1; + thread_wait((void *) handle->tid); } /** @@ -206,38 +196,38 @@ static void diagnostics(DCB *dcb, void *arg) { MONITOR* mon = arg; -MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle; -MONITOR_SERVERS *db; -char *sep; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; + MONITOR_SERVERS *db; + char *sep; - switch (handle->status) - { - case MONITOR_RUNNING: - dcb_printf(dcb, "\tMonitor running\n"); - break; - case MONITOR_STOPPING: - dcb_printf(dcb, "\tMonitor stopping\n"); - break; - case MONITOR_STOPPED: - dcb_printf(dcb, "\tMonitor stopped\n"); - break; - } + switch (handle->status) + { + case MONITOR_RUNNING: + dcb_printf(dcb, "\tMonitor running\n"); + break; + case MONITOR_STOPPING: + dcb_printf(dcb, "\tMonitor stopping\n"); + break; + case MONITOR_STOPPED: + dcb_printf(dcb, "\tMonitor stopped\n"); + break; + } - dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval); - dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); - dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); - dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); - dcb_printf(dcb, "\tMonitored servers: "); + dcb_printf(dcb, "\tSampling interval:\t%lu milliseconds\n", mon->interval); + dcb_printf(dcb, "\tConnect Timeout:\t%i seconds\n", mon->connect_timeout); + dcb_printf(dcb, "\tRead Timeout:\t\t%i seconds\n", mon->read_timeout); + dcb_printf(dcb, "\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout); + dcb_printf(dcb, "\tMonitored servers: "); - db = mon->databases; - sep = ""; - while (db) - { - dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port); - sep = ", "; - db = db->next; - } - dcb_printf(dcb, "\n"); + db = mon->databases; + sep = ""; + while (db) + { + dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port); + sep = ", "; + db = db->next; + } + dcb_printf(dcb, "\n"); } /** @@ -246,16 +236,18 @@ char *sep; * @param database The database to probe */ static void -monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MONITOR *mon) +monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MONITOR *mon) { -MYSQL_ROW row; -MYSQL_RES *result; -int isjoined = 0; -char *server_string; + MYSQL_ROW row; + MYSQL_RES *result; + int isjoined = 0; + char *server_string; /* Don't even probe server flagged as in maintenance */ if (SERVER_IN_MAINT(database->server)) + { return; + } connect_result_t rval = mon_connect_to_db(mon, database); if (rval != MONITOR_CONN_OK) @@ -277,70 +269,72 @@ char *server_string; } server_clear_status(database->server, SERVER_AUTH_ERROR); - /* If we get this far then we have a working connection */ - server_set_status(database->server, SERVER_RUNNING); + /* If we get this far then we have a working connection */ + server_set_status(database->server, SERVER_RUNNING); - /* 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); - } + /* get server version string */ + server_string = (char *) mysql_get_server_info(database->con); + 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 - && (result = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } + /* 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 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } - while ((row = mysql_fetch_row(result))) - { - if (atoi(row[1]) > 0) - isjoined = 1; - } - mysql_free_result(result); - } + while ((row = mysql_fetch_row(result))) + { + if (atoi(row[1]) > 0) + isjoined = 1; + } + mysql_free_result(result); + } - /* Check the the SQL node id in the MySQL cluster */ - if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0 - && (result = mysql_store_result(database->con)) != NULL) - { - if(mysql_field_count(database->con) < 2) - { - mysql_free_result(result); - skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". Expected 2 columns." - " MySQL Version: %s",version_str); - return; - } + /* Check the the SQL node id in the MySQL cluster */ + if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0 + && (result = mysql_store_result(database->con)) != NULL) + { + if (mysql_field_count(database->con) < 2) + { + mysql_free_result(result); + skygw_log_write(LE, "Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". Expected 2 columns." + " MySQL Version: %s", version_str); + return; + } - long cluster_node_id = -1; - while ((row = mysql_fetch_row(result))) - { - cluster_node_id = strtol(row[1], NULL, 10); - if ((errno == ERANGE && (cluster_node_id == LONG_MAX - || cluster_node_id == LONG_MIN)) || (errno != 0 && cluster_node_id == 0)) - { - cluster_node_id = -1; - } - database->server->node_id = cluster_node_id; - } - mysql_free_result(result); - } + long cluster_node_id = -1; + while ((row = mysql_fetch_row(result))) + { + cluster_node_id = strtol(row[1], NULL, 10); + if ((errno == ERANGE && (cluster_node_id == LONG_MAX + || cluster_node_id == LONG_MIN)) || (errno != 0 && cluster_node_id == 0)) + { + cluster_node_id = -1; + } + database->server->node_id = cluster_node_id; + } + mysql_free_result(result); + } - if (isjoined) { - server_set_status(database->server, SERVER_NDB); - database->server->depth = 0; - } else { - server_clear_status(database->server, SERVER_NDB); - database->server->depth = -1; - } + if (isjoined) + { + server_set_status(database->server, SERVER_NDB); + database->server->depth = 0; + } + else + { + server_clear_status(database->server, SERVER_NDB); + database->server->depth = -1; + } } /** @@ -352,114 +346,115 @@ static void monitorMain(void *arg) { MONITOR* mon = arg; -MYSQL_MONITOR *handle; -MONITOR_SERVERS *ptr; -size_t nrounds = 0; + MYSQL_MONITOR *handle; + MONITOR_SERVERS *ptr; + size_t nrounds = 0; -spinlock_acquire(&mon->lock); -handle = (MYSQL_MONITOR *)mon->handle; -spinlock_release(&mon->lock); + spinlock_acquire(&mon->lock); + handle = (MYSQL_MONITOR *) mon->handle; + spinlock_release(&mon->lock); - if (mysql_thread_init()) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Fatal : mysql_thread_init failed in monitor " - "module. Exiting.\n"))); - return; - } - handle->status = MONITOR_RUNNING; - - while (1) - { - if (handle->shutdown) - { - handle->status = MONITOR_STOPPING; - mysql_thread_end(); - handle->status = MONITOR_STOPPED; - return; - } + if (mysql_thread_init()) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Fatal : mysql_thread_init failed in monitor " + "module. Exiting.\n"))); + return; + } + handle->status = MONITOR_RUNNING; - /** Wait base interval */ - thread_millisleep(MON_BASE_INTERVAL_MS); - /** - * Calculate how far away the monitor interval is from its full - * cycle and if monitor interval time further than the base - * interval, then skip monitoring checks. Excluding the first - * round. - */ - if (nrounds != 0 && - ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= - MON_BASE_INTERVAL_MS) - { - nrounds += 1; - continue; - } - nrounds += 1; - ptr = mon->databases; + while (1) + { + if (handle->shutdown) + { + handle->status = MONITOR_STOPPING; + mysql_thread_end(); + handle->status = MONITOR_STOPPED; + return; + } - while (ptr) - { - ptr->mon_prev_status = ptr->server->status; - monitorDatabase(ptr, mon->user, mon->password,mon); + /** Wait base interval */ + thread_millisleep(MON_BASE_INTERVAL_MS); + /** + * Calculate how far away the monitor interval is from its full + * cycle and if monitor interval time further than the base + * interval, then skip monitoring checks. Excluding the first + * round. + */ + if (nrounds != 0 && + ((nrounds * MON_BASE_INTERVAL_MS) % mon->interval) >= + MON_BASE_INTERVAL_MS) + { + nrounds += 1; + continue; + } + nrounds += 1; + ptr = mon->databases; - if (ptr->server->status != ptr->mon_prev_status || - SERVER_IS_DOWN(ptr->server)) - { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Backend server %s:%d state : %s", - ptr->server->name, - ptr->server->port, - STRSRVSTATUS(ptr->server)))); - } + while (ptr) + { + ptr->mon_prev_status = ptr->server->status; + monitorDatabase(ptr, mon->user, mon->password, mon); - ptr = ptr->next; - } + if (ptr->server->status != ptr->mon_prev_status || + SERVER_IS_DOWN(ptr->server)) + { + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); + } - ptr = mon->databases; - monitor_event_t evtype; + ptr = ptr->next; + } - while(ptr) - { - /** Execute monitor script if a server state has changed */ - if(mon_status_changed(ptr)) - { - evtype = mon_get_event_type(ptr); - if(isNdbEvent(evtype)) - { - skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s", - ptr->server->unique_name, - ptr->server->name,ptr->server->port, - mon_get_event_name(ptr)); - if(handle->script && handle->events[evtype]) - { - monitor_launch_script(mon,ptr,handle->script); - } - } - } - ptr = ptr->next; - } - } + ptr = mon->databases; + monitor_event_t evtype; + + while (ptr) + { + /** Execute monitor script if a server state has changed */ + if (mon_status_changed(ptr)) + { + evtype = mon_get_event_type(ptr); + if (isNdbEvent(evtype)) + { + skygw_log_write(LOGFILE_TRACE, "Server changed state: %s[%s:%u]: %s", + ptr->server->unique_name, + ptr->server->name, ptr->server->port, + mon_get_event_name(ptr)); + if (handle->script && handle->events[evtype]) + { + monitor_launch_script(mon, ptr, handle->script); + } + } + } + ptr = ptr->next; + } + } } -static monitor_event_t ndb_events[] = { - MASTER_DOWN_EVENT, - MASTER_UP_EVENT, - SLAVE_DOWN_EVENT, - SLAVE_UP_EVENT, - SERVER_DOWN_EVENT, - SERVER_UP_EVENT, - NDB_UP_EVENT, - NDB_DOWN_EVENT, - LOST_MASTER_EVENT, - LOST_SLAVE_EVENT, - LOST_NDB_EVENT, - NEW_MASTER_EVENT, - NEW_SLAVE_EVENT, - NEW_NDB_EVENT, - MAX_MONITOR_EVENT +static monitor_event_t ndb_events[] = +{ + MASTER_DOWN_EVENT, + MASTER_UP_EVENT, + SLAVE_DOWN_EVENT, + SLAVE_UP_EVENT, + SERVER_DOWN_EVENT, + SERVER_UP_EVENT, + NDB_UP_EVENT, + NDB_DOWN_EVENT, + LOST_MASTER_EVENT, + LOST_SLAVE_EVENT, + LOST_NDB_EVENT, + NEW_MASTER_EVENT, + NEW_SLAVE_EVENT, + NEW_NDB_EVENT, + MAX_MONITOR_EVENT }; /** @@ -470,10 +465,12 @@ static monitor_event_t ndb_events[] = { bool isNdbEvent(monitor_event_t event) { int i; - for(i = 0;ndb_events[i] != MAX_MONITOR_EVENT;i++) + for (i = 0; ndb_events[i] != MAX_MONITOR_EVENT; i++) { - if(event == ndb_events[i]) - return true; + if (event == ndb_events[i]) + { + return true; + } } return false; -} \ No newline at end of file +} diff --git a/server/modules/monitor/ndbclustermon.h b/server/modules/monitor/ndbclustermon.h index 56b961c45..90798fb87 100644 --- a/server/modules/monitor/ndbclustermon.h +++ b/server/modules/monitor/ndbclustermon.h @@ -17,6 +17,7 @@ * * Copyright MariaDB Corporation Ab 2013-2014 */ + #include #include #include @@ -42,15 +43,16 @@ /** * The handle for an instance of a NDB Cluster Monitor module */ -typedef struct { - SPINLOCK lock; /**< The monitor spinlock */ - pthread_t tid; /**< id of monitor thread */ - int shutdown; /**< Flag to shutdown the monitor thread */ - int status; /**< Monitor status */ - unsigned long id; /**< Monitor ID */ - MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ - char* script; /*< Script to call when state changes occur on servers */ - bool events[MAX_MONITOR_EVENT]; /*< enabled events */ +typedef struct +{ + SPINLOCK lock; /**< The monitor spinlock */ + pthread_t tid; /**< id of monitor thread */ + int shutdown; /**< Flag to shutdown the monitor thread */ + int status; /**< Monitor status */ + unsigned long id; /**< Monitor ID */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ + char* script; /*< Script to call when state changes occur on servers */ + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ } MYSQL_MONITOR; #endif diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index a9c9c3ded..5d4aaeb7f 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -50,11 +50,6 @@ MODULE_INFO info = { "An experimental HTTPD implementation for use in admnistration" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - #define ISspace(x) isspace((int)(x)) #define HTTP_SERVER_STRING "MaxScale(c) v.1.0.0" static char *version_str = "V1.0.1"; @@ -350,9 +345,6 @@ int n_connect = 0; client->remote = strdup(inet_ntoa(addr.sin_addr)); memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL)); - /* we don't need the session */ - client->session = NULL; - /* create the session data for HTTPD */ client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session)); client->data = client_data; @@ -360,9 +352,10 @@ int n_connect = 0; client->session = session_alloc(dcb->session->service, client); - if (poll_add_dcb(client) == -1) + if (NULL == client->session || poll_add_dcb(client) == -1) { close(so); + dcb_close(client); return n_connect; } n_connect++; @@ -418,9 +411,8 @@ int syseno = 0; if(syseno != 0){ char errbuf[STRERROR_BUFLEN]; - skygw_log_write_flush(LOGFILE_ERROR, - "Error: Failed to set socket options. Error %d: %s", - errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } /* set NONBLOCKING mode */ @@ -435,7 +427,7 @@ int syseno = 0; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening httpd connections at %s", config))); + MXS_NOTICE("Listening httpd connections at %s", config); } else { int eno = errno; errno = 0; diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index 874aa8bbd..0c5fa6614 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -45,11 +45,6 @@ MODULE_INFO info = { "A maxscale protocol for the administration interface" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * @file maxscaled.c - MaxScale administration protocol * @@ -109,9 +104,7 @@ version() void ModuleInit() { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Initialise MaxScaled Protocol module.\n"))); + MXS_INFO("Initialise MaxScaled Protocol module.");; } /** @@ -156,7 +149,7 @@ char *password; maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); maxscaled->state = MAXSCALED_STATE_PASSWD; dcb_printf(dcb, "PASSWORD"); - gwbuf_consume(head, GWBUF_LENGTH(head)); + while ((head = gwbuf_consume(head, GWBUF_LENGTH(head))) != NULL); break; case MAXSCALED_STATE_PASSWD: password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); @@ -170,7 +163,7 @@ char *password; dcb_printf(dcb, "FAILED"); maxscaled->state = MAXSCALED_STATE_LOGIN; } - gwbuf_consume(head, GWBUF_LENGTH(head)); + while ((head = gwbuf_consume(head, GWBUF_LENGTH(head))) != NULL); free(password); break; case MAXSCALED_STATE_DATA: @@ -182,7 +175,7 @@ char *password; else { // Force the free of the buffer header - gwbuf_consume(head, 0); + while ((head = gwbuf_consume(head, GWBUF_LENGTH(head))) != NULL); } } } @@ -291,7 +284,7 @@ int n_connect = 0; client_dcb->session = session_alloc(dcb->session->service, client_dcb); - if (poll_add_dcb(client_dcb)) + if (NULL == client_dcb->session || poll_add_dcb(client_dcb)) { dcb_close(dcb); return n_connect; @@ -357,10 +350,7 @@ int rc; // socket options if (setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Unable to set SO_REUSEADDR on maxscale listener." - ))); + MXS_ERROR("Unable to set SO_REUSEADDR on maxscale listener."); } // set NONBLOCKING mode setnonblocking(listener->fd); @@ -373,20 +363,14 @@ int rc; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Listening maxscale connections at %s\n", - config))); + MXS_NOTICE("Listening maxscale connections at %s", config); } else { int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Failed to start listening for maxscale admin connections " - "due error %d, %s\n\n", - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to start listening for maxscale admin connections " + "due error %d, %s", + eno, strerror_r(eno, errbuf, sizeof(errbuf))); return 0; } diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 391ec0cc7..279a87981 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -44,6 +44,8 @@ * 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support * 10/11/2014 Massimiliano Pinto Client charset is passed to backend * 19/06/2015 Martin Brampton Persistent connection handling + * 07/10/2015 Martin Brampton Remove calls to dcb_close - should be done by routers + * 27/10/2015 Martin Brampton Test for RCAP_TYPE_NO_RSESSION before calling clientReply * */ #include @@ -55,11 +57,6 @@ MODULE_INFO info = { "The MySQL to backend server protocol" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static char *version_str = "V2.0.0"; static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *in_session); static int gw_read_backend_event(DCB* dcb); @@ -140,15 +137,13 @@ static MYSQL_session* gw_get_shared_session_auth_info( spinlock_acquire(&dcb->session->ses_lock); - if (dcb->session->state != SESSION_STATE_ALLOC) { + if (dcb->session->state != SESSION_STATE_ALLOC && dcb->session->state != SESSION_STATE_DUMMY) { auth_info = dcb->session->data; } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_get_shared_session_auth_info] Couldn't get " - "session authentication info. Session in a wrong state %d.", - pthread_self(), - dcb->session->state))); + MXS_ERROR("%lu [gw_get_shared_session_auth_info] Couldn't get " + "session authentication info. Session in a wrong state %d.", + pthread_self(), + dcb->session->state); } spinlock_release(&dcb->session->ses_lock); @@ -167,7 +162,7 @@ static int gw_read_backend_event(DCB *dcb) { int rc = 0; CHK_DCB(dcb); - if (!dcb->session && dcb->persistentstart) + if (dcb->persistentstart) { dcb->dcb_errhandle_called = true; goto return_rc; @@ -187,15 +182,13 @@ static int gw_read_backend_event(DCB *dcb) { backend_protocol = (MySQLProtocol *) dcb->protocol; CHK_PROTOCOL(backend_protocol); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] Read dcb %p fd %d protocol " - "state %d, %s.", - pthread_self(), - dcb, - dcb->fd, - backend_protocol->protocol_auth_state, - STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)))); + MXS_DEBUG("%lu [gw_read_backend_event] Read dcb %p fd %d protocol " + "state %d, %s.", + pthread_self(), + dcb, + dcb->fd, + backend_protocol->protocol_auth_state, + STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)); /* backend is connected: @@ -223,13 +216,11 @@ static int gw_read_backend_event(DCB *dcb) { { backend_protocol->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] after " - "gw_read_backend_handshake, fd %d, " - "state = MYSQL_HANDSHAKE_FAILED.", - pthread_self(), - backend_protocol->owner_dcb->fd))); + MXS_DEBUG("%lu [gw_read_backend_event] after " + "gw_read_backend_handshake, fd %d, " + "state = MYSQL_HANDSHAKE_FAILED.", + pthread_self(), + backend_protocol->owner_dcb->fd); } else { @@ -244,13 +235,11 @@ static int gw_read_backend_event(DCB *dcb) { backend_protocol) != 0) { backend_protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] after " - "gw_send_authentication_to_backend " - "fd %d, state = MYSQL_AUTH_FAILED.", - pthread_self(), - backend_protocol->owner_dcb->fd))); + MXS_DEBUG("%lu [gw_read_backend_event] after " + "gw_send_authentication_to_backend " + "fd %d, state = MYSQL_AUTH_FAILED.", + pthread_self(), + backend_protocol->owner_dcb->fd); } else { @@ -285,7 +274,7 @@ static int gw_read_backend_event(DCB *dcb) { SESSION *session = dcb->session; int receive_rc = 0; - if (session == NULL) + if (SESSION_STATE_DUMMY == session->state) { rc = 0; goto return_with_lock; @@ -306,46 +295,38 @@ static int gw_read_backend_event(DCB *dcb) { switch (receive_rc) { case -1: backend_protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] after " - "gw_receive_backend_authentication " - "fd %d, state = MYSQL_AUTH_FAILED.", - pthread_self(), - backend_protocol->owner_dcb->fd))); + MXS_DEBUG("%lu [gw_read_backend_event] after " + "gw_receive_backend_authentication " + "fd %d, state = MYSQL_AUTH_FAILED.", + pthread_self(), + backend_protocol->owner_dcb->fd); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Backend server didn't " - "accept authentication for user " - "%s.", - current_session->user))); + MXS_ERROR("Backend server didn't " + "accept authentication for user " + "%s.", + current_session->user); break; case 1: backend_protocol->protocol_auth_state = MYSQL_IDLE; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] " - "gw_receive_backend_auth succeed. " - "dcb %p fd %d, user %s.", - pthread_self(), - dcb, - dcb->fd, - current_session->user))); + MXS_DEBUG("%lu [gw_read_backend_event] " + "gw_receive_backend_auth succeed. " + "dcb %p fd %d, user %s.", + pthread_self(), + dcb, + dcb->fd, + current_session->user); break; default: ss_dassert(receive_rc == 0); - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] " - "gw_receive_backend_auth read " - "successfully " - "nothing. dcb %p fd %d, user %s.", - pthread_self(), - dcb, - dcb->fd, - current_session->user))); + MXS_DEBUG("%lu [gw_read_backend_event] " + "gw_receive_backend_auth read " + "successfully " + "nothing. dcb %p fd %d, user %s.", + pthread_self(), + dcb, + dcb->fd, + current_session->user); rc = 0; goto return_with_lock; break; @@ -379,14 +360,12 @@ static int gw_read_backend_event(DCB *dcb) { service_refresh_users(dcb->session->service); } #if defined(SS_DEBUG) - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] " - "calling handleError. Backend " - "DCB %p, session %p", - pthread_self(), - dcb, - dcb->session))); + MXS_DEBUG("%lu [gw_read_backend_event] " + "calling handleError. Backend " + "DCB %p, session %p", + pthread_self(), + dcb, + dcb->session); #endif errbuf = mysql_create_custom_error( @@ -395,41 +374,46 @@ static int gw_read_backend_event(DCB *dcb) { "Authentication with backend failed. " "Session will be closed."); - router->handleError(router_instance, + if (rsession) + { + router->handleError(router_instance, rsession, errbuf, dcb, ERRACT_REPLY_CLIENT, &succp); + } + else + { + gwbuf_free(errbuf); + dcb->dcb_errhandle_called = true; + /* + * I'm pretty certain this is best removed and + * causes trouble if present, but have left it + * here just for now as a comment. Martin + */ + /* dcb_close(dcb); */ + rc = 1; + goto return_rc; + } gwbuf_free(errbuf); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] " - "after calling handleError. Backend " - "DCB %p, session %p", - pthread_self(), - dcb, - dcb->session))); spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; spinlock_release(&session->ses_lock); ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); rc = 1; goto return_rc; } else { ss_dassert(backend_protocol->protocol_auth_state == MYSQL_IDLE); - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_read_backend_event] " - "gw_receive_backend_auth succeed. Fd %d, " - "user %s.", - pthread_self(), - dcb->fd, - current_session->user))); + MXS_DEBUG("%lu [gw_read_backend_event] " + "gw_receive_backend_auth succeed. Fd %d, " + "user %s.", + pthread_self(), + dcb->fd, + current_session->user); /* check the delay queue and flush the data */ if (dcb->delayq) @@ -464,9 +448,7 @@ static int gw_read_backend_event(DCB *dcb) { GWBUF* errbuf; bool succp; #if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend read error handling #2."))); + MXS_ERROR("Backend read error handling #2."); #endif errbuf = mysql_create_custom_error( 1, @@ -488,8 +470,6 @@ static int gw_read_backend_event(DCB *dcb) { session->state = SESSION_STATE_STOPPING; spinlock_release(&session->ses_lock); } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); rc = 0; goto return_rc; } @@ -553,13 +533,11 @@ static int gw_read_backend_event(DCB *dcb) { } if (!read_buffer) { - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "%lu [gw_read_backend_event] " - "Read buffer unexpectedly null, even though response " - "not marked as complete. User: %s", - pthread_self(), - current_session->user))); + MXS_NOTICE("%lu [gw_read_backend_event] " + "Read buffer unexpectedly null, even though response " + "not marked as complete. User: %s", + pthread_self(), + current_session->user); rc = 0; goto return_rc; } @@ -570,7 +548,8 @@ static int gw_read_backend_event(DCB *dcb) { */ if (dcb->session->state == SESSION_STATE_ROUTER_READY && dcb->session->client != NULL && - dcb->session->client->state == DCB_STATE_POLLING) + dcb->session->client->state == DCB_STATE_POLLING && + (session->router_session || router->getCapabilities() & RCAP_TYPE_NO_RSESSION)) { client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol); @@ -647,34 +626,28 @@ static int gw_write_backend_event(DCB *dcb) { 0, "Writing to backend failed due invalid Maxscale " "state."); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_write_backend_event] Write to backend " - "dcb %p fd %d " - "failed due invalid state %s.", - pthread_self(), - dcb, - dcb->fd, - STRDCBSTATE(dcb->state)))); + MXS_DEBUG("%lu [gw_write_backend_event] Write to backend " + "dcb %p fd %d " + "failed due invalid state %s.", + pthread_self(), + dcb, + dcb->fd, + STRDCBSTATE(dcb->state)); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Attempt to write buffered data to backend " - "failed " - "due internal inconsistent state."))); + MXS_ERROR("Attempt to write buffered data to backend " + "failed " + "due internal inconsistent state."); rc = 0; } } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_write_backend_event] Dcb %p in state %s " - "but there's nothing to write either.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); + MXS_DEBUG("%lu [gw_write_backend_event] Dcb %p in state %s " + "but there's nothing to write either.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); rc = 1; } goto return_rc; @@ -688,14 +661,12 @@ static int gw_write_backend_event(DCB *dcb) { dcb_drain_writeq(dcb); rc = 1; return_rc: - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_write_backend_event] " - "wrote to dcb %p fd %d, return %d", - pthread_self(), - dcb, - dcb->fd, - rc))); + MXS_DEBUG("%lu [gw_write_backend_event] " + "wrote to dcb %p fd %d, return %d", + pthread_self(), + dcb, + dcb->fd, + rc); return rc; } @@ -723,12 +694,12 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) switch (backend_protocol->protocol_auth_state) { case MYSQL_HANDSHAKE_FAILED: case MYSQL_AUTH_FAILED: - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error : Unable to write to backend '%s' due to " - "%s failure. Server in state %s.", - dcb->server->unique_name, - backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED ? "handshake" : "authentication", - STRSRVSTATUS(dcb->server)))); + MXS_ERROR("Unable to write to backend '%s' due to " + "%s failure. Server in state %s.", + dcb->server->unique_name, + backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED ? + "handshake" : "authentication", + STRSRVSTATUS(dcb->server)); /** Consume query buffer */ while ((queue = gwbuf_consume( queue, @@ -743,14 +714,12 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) uint8_t* ptr = GWBUF_DATA(queue); int cmd = MYSQL_GET_COMMAND(ptr); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_MySQLWrite_backend] write to dcb %p " - "fd %d protocol state %s.", - pthread_self(), - dcb, - dcb->fd, - STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)))); + MXS_DEBUG("%lu [gw_MySQLWrite_backend] write to dcb %p " + "fd %d protocol state %s.", + pthread_self(), + dcb, + dcb->fd, + STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)); spinlock_release(&dcb->authlock); /** @@ -774,14 +743,12 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) default: { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_MySQLWrite_backend] delayed write to " - "dcb %p fd %d protocol state %s.", - pthread_self(), - dcb, - dcb->fd, - STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)))); + MXS_DEBUG("%lu [gw_MySQLWrite_backend] delayed write to " + "dcb %p fd %d protocol state %s.", + pthread_self(), + dcb, + dcb->fd, + STRPROTOCOLSTATE(backend_protocol->protocol_auth_state)); /** * In case of session commands, store command to DCB's * protocol struct. @@ -830,6 +797,11 @@ static int gw_error_backend_event(DCB *dcb) CHK_DCB(dcb); session = dcb->session; CHK_SESSION(session); + if (SESSION_STATE_DUMMY == session->state) + { + dcb_close(dcb); + return 1; + } rsession = session->router_session; router = session->service->router; router_instance = session->service->router_instance; @@ -851,11 +823,9 @@ static int gw_error_backend_event(DCB *dcb) if (error != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "DCB in state %s got error '%s'.", - STRDCBSTATE(dcb->state), - strerror_r(error, errbuf, sizeof(errbuf))))); + MXS_ERROR("DCB in state %s got error '%s'.", + STRDCBSTATE(dcb->state), + strerror_r(error, errbuf, sizeof(errbuf))); } } return 1; @@ -892,10 +862,8 @@ static int gw_error_backend_event(DCB *dcb) if (error != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error '%s' in session that is not ready for routing.", - strerror_r(error, errbuf, sizeof(errbuf))))); + MXS_ERROR("Error '%s' in session that is not ready for routing.", + strerror_r(error, errbuf, sizeof(errbuf))); } } gwbuf_free(errbuf); @@ -903,9 +871,7 @@ static int gw_error_backend_event(DCB *dcb) } #if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend error event handling."))); + MXS_INFO("Backend error event handling."); #endif router->handleError(router_instance, rsession, @@ -925,8 +891,6 @@ static int gw_error_backend_event(DCB *dcb) session->state = SESSION_STATE_STOPPING; spinlock_release(&session->ses_lock); } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); retblock: return 1; @@ -959,16 +923,11 @@ static int gw_create_backend_connection( ss_dassert(protocol != NULL); if (protocol == NULL) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Failed to create " - "protocol object for backend connection.", - pthread_self()))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to create " - "protocol object for backend connection."))); - goto return_fd; + MXS_DEBUG("%lu [gw_create_backend_connection] Failed to create " + "protocol object for backend connection.", + pthread_self()); + MXS_ERROR("Failed to create protocol object for backend connection."); + goto return_fd; } /** Copy client flags to backend protocol */ @@ -998,45 +957,39 @@ static int gw_create_backend_connection( ss_dassert(fd > 0); protocol->fd = fd; protocol->protocol_auth_state = MYSQL_CONNECTED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Established " - "connection to %s:%i, protocol fd %d client " - "fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); + MXS_DEBUG("%lu [gw_create_backend_connection] Established " + "connection to %s:%i, protocol fd %d client " + "fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd); break; case 1: ss_dassert(fd > 0); protocol->protocol_auth_state = MYSQL_PENDING_CONNECT; protocol->fd = fd; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Connection " - "pending to %s:%i, protocol fd %d client fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); + MXS_DEBUG("%lu [gw_create_backend_connection] Connection " + "pending to %s:%i, protocol fd %d client fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd); break; default: ss_dassert(fd == -1); ss_dassert(protocol->protocol_auth_state == MYSQL_ALLOC); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_create_backend_connection] Connection " - "failed to %s:%i, protocol fd %d client fd %d.", - pthread_self(), - server->name, - server->port, - protocol->fd, - session->client->fd))); + MXS_DEBUG("%lu [gw_create_backend_connection] Connection " + "failed to %s:%i, protocol fd %d client fd %d.", + pthread_self(), + server->name, + server->port, + protocol->fd, + session->client->fd); break; } /*< switch */ @@ -1067,7 +1020,7 @@ gw_backend_hangup(DCB *dcb) session_state_t ses_state; CHK_DCB(dcb); - if (!dcb->session && dcb->persistentstart) + if (dcb->persistentstart) { dcb->dcb_errhandle_called = true; goto retblock; @@ -1117,22 +1070,24 @@ gw_backend_hangup(DCB *dcb) if (error != 0 && ses_state != SESSION_STATE_STOPPING) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Hangup in session that is not ready for routing, " - "Error reported is '%s'.", - strerror_r(error, errbuf, sizeof(errbuf))))); + MXS_ERROR("Hangup in session that is not ready for routing, " + "Error reported is '%s'.", + strerror_r(error, errbuf, sizeof(errbuf))); } } gwbuf_free(errbuf); + /* + * I'm pretty certain this is best removed and + * causes trouble if present, but have left it + * here just for now as a comment. Martin + */ + /* dcb_close(dcb); */ goto retblock; } #if defined(SS_DEBUG) if(ses_state != SESSION_STATE_STOPPING) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend hangup error handling."))); + MXS_ERROR("Backend hangup error handling."); } #endif @@ -1148,16 +1103,12 @@ gw_backend_hangup(DCB *dcb) if (!succp) { #if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend hangup -> closing session."))); + MXS_ERROR("Backend hangup -> closing session."); #endif spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; spinlock_release(&session->ses_lock); } - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); retblock: return 1; @@ -1178,9 +1129,8 @@ gw_backend_close(DCB *dcb) CHK_DCB(dcb); session = dcb->session; - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [gw_backend_close]", - pthread_self()))); + MXS_DEBUG("%lu [gw_backend_close]", + pthread_self()); quitbuf = mysql_create_com_quit(NULL, 0); gwbuf_set_type(quitbuf, GWBUF_TYPE_MYSQL); @@ -1209,10 +1159,13 @@ gw_backend_close(DCB *dcb) { if (session->client->state == DCB_STATE_POLLING) { + DCB *temp; spinlock_release(&session->ses_lock); /** Close client DCB */ - dcb_close(session->client); + temp = session->client; + session->client = NULL; + dcb_close(temp); } else { @@ -1313,9 +1266,7 @@ static int backend_write_delayqueue(DCB *dcb) router_instance = session->service->router_instance; rsession = session->router_session; #if defined(SS_DEBUG) - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Backend write delayqueue error handling."))); + MXS_INFO("Backend write delayqueue error handling."); #endif errbuf = mysql_create_custom_error( 1, @@ -1337,8 +1288,6 @@ static int backend_write_delayqueue(DCB *dcb) spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; spinlock_release(&session->ses_lock); - ss_dassert(dcb->dcb_errhandle_called); - dcb_close(dcb); } } } @@ -1477,11 +1426,9 @@ static int gw_change_user( auth_ret); if (message == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Creating error message failed."))); - rv = 0; - goto retblock; + MXS_ERROR("Creating error message failed."); + rv = 0; + goto retblock; } /** * Add command to backend's protocol, create artificial reply @@ -1550,13 +1497,11 @@ static GWBUF* process_response_data ( srvcmd = protocol_get_srv_command(p, false); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [process_response_data] Read command %s for DCB %p fd %d.", - pthread_self(), - STRPACKETTYPE(srvcmd), - dcb, - dcb->fd))); + MXS_DEBUG("%lu [process_response_data] Read command %s for DCB %p fd %d.", + pthread_self(), + STRPACKETTYPE(srvcmd), + dcb, + dcb->fd); /** * Read values from protocol structure, fails if values are * uninitialized. @@ -1656,9 +1601,11 @@ static GWBUF* process_response_data ( wait for more data from the backend server.*/ if(readbuf == NULL || GWBUF_LENGTH(readbuf) < 3) { - skygw_log_write(LD," %lu [%s] Read %d packets. Waiting for %d more packets for a total of %d packets.", - pthread_self(),__FUNCTION__,initial_packets - npackets_left, - npackets_left,initial_packets); + MXS_DEBUG("%lu [%s] Read %d packets. Waiting for %d more " + "packets for a total of %d packets.", + pthread_self(),__FUNCTION__, + initial_packets - npackets_left, + npackets_left,initial_packets); /** Store the already read data into the readqueue of the DCB * and restore the response status to the initial number of packets */ diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index db580e69a..8993fc802 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -39,6 +39,8 @@ * 10/11/2014 Massimiliano Pinto Added: client charset added to protocol struct * 29/05/2015 Markus Makela Added SSL support * 11/06/2015 Martin Brampton COM_QUIT suppressed for persistent connections + * 04/09/2015 Martin Brampton Introduce DUMMY session to fulfill guarantee DCB always has session + * 09/09/2015 Martin Brampton Modify error handler calls */ #include #include @@ -56,11 +58,6 @@ MODULE_INFO info = { "The client to MaxScale MySQL protocol implementation" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static char *version_str = "V1.0.0"; static int gw_MySQLAccept(DCB *listener); @@ -483,16 +480,16 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { /** Client didn't requested SSL when SSL mode was required*/ if(!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED) { - LOGIF(LT,(skygw_log_write(LT,"User %s@%s connected to service '%s' without SSL when SSL was required.", - protocol->owner_dcb->user, - protocol->owner_dcb->remote, - protocol->owner_dcb->service->name))); + MXS_INFO("User %s@%s connected to service '%s' without SSL when SSL was required.", + protocol->owner_dcb->user, + protocol->owner_dcb->remote, + protocol->owner_dcb->service->name); return MYSQL_FAILED_AUTH_SSL; } if(LOG_IS_ENABLED(LT) && ssl) { - skygw_log_write(LT,"User %s@%s connected to service '%s' with SSL.", + MXS_INFO("User %s@%s connected to service '%s' with SSL.", protocol->owner_dcb->user, protocol->owner_dcb->remote, protocol->owner_dcb->service->name); @@ -524,7 +521,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { client_auth_packet = GWBUF_DATA(queue); client_auth_packet_size = gwbuf_length(queue); *buf = queue; - LOGIF(LD,(skygw_log_write(LD,"%lu Read %d bytes from fd %d",pthread_self(),bytes,dcb->fd))); + MXS_DEBUG("%lu Read %d bytes from fd %d",pthread_self(),bytes,dcb->fd); } } @@ -565,7 +562,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { * Decode the token and check the password * Note: if auth_token_len == 0 && auth_token == NULL, user is without password */ - skygw_log_write(LOGFILE_DEBUG,"Receiving connection from '%s' to database '%s'.",username,database); + MXS_DEBUG("Receiving connection from '%s' to database '%s'.",username,database); auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, @@ -599,18 +596,18 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf) { /* on succesful auth set user into dcb field */ if (auth_ret == 0) { dcb->user = strdup(client_data->user); - } - else + } + else if (dcb->service->log_auth_warnings) { - skygw_log_write(LM, "%s: login attempt for user '%s', authentication failed.", - dcb->service->name, username); + MXS_NOTICE("%s: login attempt for user '%s', authentication failed.", + dcb->service->name, username); if (dcb->ipv4.sin_addr.s_addr == 0x0100007F && !dcb->service->localhost_match_wildcard_host) { - skygw_log_write_flush(LM, "If you have a wildcard grant that covers" - " this address, try adding " - "'localhost_match_wildcard_host=true' for " - "service '%s'. ", dcb->service->name); + MXS_NOTICE("If you have a wildcard grant that covers" + " this address, try adding " + "'localhost_match_wildcard_host=true' for " + "service '%s'. ", dcb->service->name); } } @@ -679,8 +676,8 @@ int gw_read_client_event( CHK_PROTOCOL(protocol); #ifdef SS_DEBUG - skygw_log_write(LD,"[gw_read_client_event] Protocol state: %s", - gw_mysql_protocol_state2string(protocol->protocol_auth_state)); + MXS_DEBUG("[gw_read_client_event] Protocol state: %s", + gw_mysql_protocol_state2string(protocol->protocol_auth_state)); #endif @@ -701,8 +698,7 @@ int gw_read_client_event( ioctl(dcb->fd,FIONREAD,&b); if(b == 0) { - skygw_log_write(LD, - "[gw_read_client_event] No data in socket after SSL auth"); + MXS_DEBUG("[gw_read_client_event] No data in socket after SSL auth"); return 0; } break; @@ -750,76 +746,38 @@ int gw_read_client_event( session = dcb->session; - if (protocol->protocol_auth_state == MYSQL_IDLE && session != NULL) + if (protocol->protocol_auth_state == MYSQL_IDLE && session != NULL && SESSION_STATE_DUMMY != session->state) { CHK_SESSION(session); router = session->service->router; router_instance = session->service->router_instance; rsession = session->router_session; - ss_dassert(rsession != NULL); - if (router_instance != NULL && rsession != NULL) { + if (NULL == router_instance || NULL == rsession) + { + /** Send ERR 1045 to client */ + mysql_send_auth_error( + dcb, + 2, + 0, + "failed to create new session"); + while (read_buffer) + { + read_buffer = gwbuf_consume(read_buffer, GWBUF_LENGTH(read_buffer)); + } + return 0; + } - /** Ask what type of input the router expects */ - cap = router->getCapabilities(router_instance, rsession); - - if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT)) - { - stmt_input = false; - } - else if (cap == RCAP_TYPE_STMT_INPUT) - { - stmt_input = true; - /** Mark buffer to as MySQL type */ - gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); - } - - /** - * If router doesn't implement getCapabilities correctly we end - * up here. - */ - else - { - GWBUF* errbuf; - bool succp; - - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] Reading router " - "capabilities failed.", - pthread_self()))); - - errbuf = mysql_create_custom_error( - 1, - 0, - "Read invalid router capabilities. Routing failed. " - "Session will be closed."); - - router->handleError( - router_instance, - rsession, - errbuf, - dcb, - ERRACT_REPLY_CLIENT, - &succp); - gwbuf_free(errbuf); - /** - * If there are not enough backends close - * session - */ - if (!succp) - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Routing the query failed. " - "Session will be closed."))); - dcb_close(dcb); - } - rc = 1; - goto return_rc; - } - } - } + /** Ask what type of input the router expects */ + cap = router->getCapabilities(router_instance, rsession); + + if (cap & RCAP_TYPE_STMT_INPUT) + { + stmt_input = true; + /** Mark buffer to as MySQL type */ + gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL); + } + } if (stmt_input) { @@ -912,7 +870,7 @@ int gw_read_client_event( if (session != NULL) { CHK_SESSION(session); - ss_dassert(session->state != SESSION_STATE_ALLOC); + ss_dassert(session->state != SESSION_STATE_ALLOC && session->state != SESSION_STATE_DUMMY); protocol->protocol_auth_state = MYSQL_IDLE; /** @@ -924,13 +882,11 @@ int gw_read_client_event( else { protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] session " - "creation failed. fd %d, " - "state = MYSQL_AUTH_FAILED.", - pthread_self(), - protocol->owner_dcb->fd))); + MXS_DEBUG("%lu [gw_read_client_event] session " + "creation failed. fd %d, " + "state = MYSQL_AUTH_FAILED.", + pthread_self(), + protocol->owner_dcb->fd); /** Send ERR 1045 to client */ mysql_send_auth_error( @@ -968,13 +924,11 @@ int gw_read_client_event( if (fail_str) free(fail_str); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] after " - "gw_mysql_do_authentication, fd %d, " - "state = MYSQL_AUTH_FAILED.", - protocol->owner_dcb->fd, - pthread_self()))); + MXS_DEBUG("%lu [gw_read_client_event] after " + "gw_mysql_do_authentication, fd %d, " + "state = MYSQL_AUTH_FAILED.", + pthread_self(), + protocol->owner_dcb->fd); /** * Release MYSQL_session since it is not used anymore. */ @@ -1012,7 +966,7 @@ int gw_read_client_event( if (session != NULL) { CHK_SESSION(session); - ss_dassert(session->state != SESSION_STATE_ALLOC); + ss_dassert(session->state != SESSION_STATE_ALLOC && session->state != SESSION_STATE_DUMMY); protocol->protocol_auth_state = MYSQL_IDLE; /** @@ -1024,13 +978,11 @@ int gw_read_client_event( else { protocol->protocol_auth_state = MYSQL_AUTH_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] session " - "creation failed. fd %d, " - "state = MYSQL_AUTH_FAILED.", - pthread_self(), - protocol->owner_dcb->fd))); + MXS_DEBUG("%lu [gw_read_client_event] session " + "creation failed. fd %d, " + "state = MYSQL_AUTH_FAILED.", + pthread_self(), + protocol->owner_dcb->fd); /** Send ERR 1045 to client */ mysql_send_auth_error( @@ -1068,13 +1020,11 @@ int gw_read_client_event( if (fail_str) free(fail_str); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] after " - "gw_mysql_do_authentication, fd %d, " - "state = MYSQL_AUTH_FAILED.", - protocol->owner_dcb->fd, - pthread_self()))); + MXS_DEBUG("%lu [gw_read_client_event] after " + "gw_mysql_do_authentication, fd %d, " + "state = MYSQL_AUTH_FAILED.", + pthread_self(), + protocol->owner_dcb->fd); /** * Release MYSQL_session since it is not used anymore. */ @@ -1096,7 +1046,7 @@ int gw_read_client_event( session_state_t ses_state; session = dcb->session; - ss_dassert(session!= NULL); + ss_dassert(session!= NULL && SESSION_STATE_DUMMY != session->state); if (session != NULL) { @@ -1122,6 +1072,7 @@ int gw_read_client_event( /* Temporarily suppressed: SESSION_ROUTE_QUERY(session, read_buffer); */ /* Replaced with freeing the read buffer. */ gwbuf_free(read_buffer); + read_buffer = NULL; /** * Close router session which causes closing of backends. */ @@ -1130,7 +1081,7 @@ int gw_read_client_event( else { /** Reset error handler when routing of the new query begins */ - router->handleError(NULL, NULL, NULL, dcb, ERRACT_RESET, NULL); + dcb->dcb_errhandle_called = false; if (stmt_input) { @@ -1144,12 +1095,18 @@ int gw_read_client_event( { /** add incomplete mysql packet to read queue */ dcb->dcb_readqueue = gwbuf_append(dcb->dcb_readqueue, read_buffer); + read_buffer = NULL; } } - else + else if (NULL != session->router_session || cap & RCAP_TYPE_NO_RSESSION) { /** Feed whole packet to router */ rc = SESSION_ROUTE_QUERY(session, read_buffer); + read_buffer = NULL; + } + else + { + rc = 0; } /** Routing succeed */ @@ -1187,20 +1144,21 @@ int gw_read_client_event( */ if (!succp) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Routing the query failed. " - "Session will be closed."))); + MXS_ERROR("Routing the query failed. " + "Session will be closed."); - dcb_close(dcb); } + while (read_buffer) + { + read_buffer = gwbuf_consume(read_buffer, GWBUF_LENGTH(read_buffer)); + } } } } else { - skygw_log_write_flush(LT,"Session received a query in state %s", - STRSESSIONSTATE(ses_state)); + MXS_INFO("Session received a query in state %s", + STRSESSIONSTATE(ses_state)); while((read_buffer = GWBUF_CONSUME_ALL(read_buffer)) != NULL); goto return_rc; } @@ -1359,10 +1317,9 @@ int gw_MySQLListener(DCB *listen_dcb, if ((l_so = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Error: Can't create UNIX socket: %i, %s", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Can't create UNIX socket: %i, %s", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } memset(&local_addr, 0, sizeof(local_addr)); @@ -1379,7 +1336,7 @@ int gw_MySQLListener(DCB *listen_dcb, */ if (!parse_bindconfig(config_bind, 4406, &serv_addr)) { - skygw_log_write(LE, "Error in parse_bindconfig for [%s]", config_bind); + MXS_ERROR("Error in parse_bindconfig for [%s]", config_bind); return 0; } @@ -1387,10 +1344,9 @@ int gw_MySQLListener(DCB *listen_dcb, if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Error: Can't create socket: %i, %s", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Can't create socket: %i, %s", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } @@ -1404,10 +1360,9 @@ int gw_MySQLListener(DCB *listen_dcb, if (setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error: Failed to set socket options. Error %d: %s", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } if (is_tcp) @@ -1415,16 +1370,15 @@ int gw_MySQLListener(DCB *listen_dcb, char errbuf[STRERROR_BUFLEN]; if (setsockopt(l_so, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != 0) { - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error: Failed to set socket options. Error %d: %s", - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } } // set NONBLOCKING mode if (setnonblocking(l_so) != 0) { - skygw_log_write(LE, "Error: Failed to set socket to non-blocking mode."); + MXS_ERROR("Failed to set socket to non-blocking mode."); close(l_so); return 0; } @@ -1437,18 +1391,17 @@ int gw_MySQLListener(DCB *listen_dcb, if ((rc == -1) && (errno != ENOENT)) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, "Error: Failed to unlink Unix Socket %s: %d %s", - config_bind, errno, strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to unlink Unix Socket %s: %d %s", + config_bind, errno, strerror_r(errno, errbuf, sizeof(errbuf))); } if (bind(l_so, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Error: Failed to bind to UNIX Domain socket '%s': %i, %s", - config_bind, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to bind to UNIX Domain socket '%s': %i, %s", + config_bind, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); close(l_so); return 0; } @@ -1457,31 +1410,28 @@ int gw_MySQLListener(DCB *listen_dcb, if (chmod(config_bind, 0777) < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Error: Failed to change permissions on UNIX Domain socket '%s': %i, %s", - config_bind, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to change permissions on UNIX Domain socket '%s': %i, %s", + config_bind, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } - break; case AF_INET: if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Error: Failed to bind on '%s': %i, %s", - config_bind, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to bind on '%s': %i, %s", + config_bind, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); close(l_so); return 0; } break; default: - skygw_log_write(LE, "Error: Socket Family %i not supported\n", current_addr->sa_family); + MXS_ERROR("Socket Family %i not supported\n", current_addr->sa_family); close(l_so); return 0; } @@ -1489,16 +1439,15 @@ int gw_MySQLListener(DCB *listen_dcb, if (listen(l_so, 10 * SOMAXCONN) != 0) { char errbuf[STRERROR_BUFLEN]; - skygw_log_write(LE, - "Failed to start listening on '%s': %d, %s", - config_bind, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to start listening on '%s': %d, %s", + config_bind, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); close(l_so); return 0; } - LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, "Listening MySQL connections at %s", config_bind))); + MXS_NOTICE("Listening MySQL connections at %s", config_bind); // assign l_so to dcb listen_dcb->fd = l_so; @@ -1506,9 +1455,8 @@ int gw_MySQLListener(DCB *listen_dcb, // add listening socket to poll structure if (poll_add_dcb(listen_dcb) != 0) { - skygw_log_write(LE, - "MaxScale encountered system limit while " - "attempting to register on an epoll instance."); + MXS_ERROR("MaxScale encountered system limit while " + "attempting to register on an epoll instance."); return 0; } #if defined(FAKE_CODE) @@ -1591,23 +1539,19 @@ int gw_MySQLAccept(DCB *listener) * (EMFILE) max. number of files limit. */ char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_MySQLAccept] Error %d, %s. ", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [gw_MySQLAccept] Error %d, %s. ", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); if (i == 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error %d, %s. " - "Failed to accept new client " - "connection.", - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Error %d, %s. " + "Failed to accept new client " + "connection.", + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); } i++; ts1.tv_nsec = 100*i*i*1000000; @@ -1625,18 +1569,14 @@ int gw_MySQLAccept(DCB *listener) * Other error. */ char errbuf[STRERROR_BUFLEN]; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_MySQLAccept] Error %d, %s.", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to accept new client " - "connection due to %d, %s.", - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_DEBUG("%lu [gw_MySQLAccept] Error %d, %s.", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); + MXS_ERROR("Failed to accept new client " + "connection due to %d, %s.", + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); rc = 1; goto return_rc; } /* if (eno == ..) */ @@ -1646,11 +1586,9 @@ int gw_MySQLAccept(DCB *listener) listener->stats.n_accepts++; #if defined(SS_DEBUG) - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_MySQLAccept] Accepted fd %d.", - pthread_self(), - c_sock))); + MXS_DEBUG("%lu [gw_MySQLAccept] Accepted fd %d.", + pthread_self(), + c_sock); #endif /* SS_DEBUG */ #if defined(FAKE_CODE) conn_open[c_sock] = true; @@ -1660,29 +1598,29 @@ int gw_MySQLAccept(DCB *listener) char errbuf[STRERROR_BUFLEN]; if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen)) != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); } sendbuf = GW_CLIENT_SO_RCVBUF; if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen)) != 0){ - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); } setnonblocking(c_sock); client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); if (client_dcb == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Failed to create " - "DCB object for client connection."))); - close(c_sock); - rc = 1; - goto return_rc; + MXS_ERROR("Failed to create DCB object for client connection."); + close(c_sock); + rc = 1; + goto return_rc; } client_dcb->service = listener->session->service; + client_dcb->session = session_set_dummy(client_dcb); client_dcb->fd = c_sock; // get client address @@ -1716,11 +1654,9 @@ int gw_MySQLAccept(DCB *listener) if (protocol == NULL) { /** delete client_dcb */ dcb_close(client_dcb); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Failed to create " - "protocol object for client connection.", - pthread_self()))); + MXS_ERROR("%lu [gw_MySQLAccept] Failed to create " + "protocol object for client connection.", + pthread_self()); rc = 1; goto return_rc; } @@ -1752,25 +1688,21 @@ int gw_MySQLAccept(DCB *listener) dcb_close(client_dcb); /** Previous state is recovered in poll_add_dcb. */ - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Failed to add dcb %p for " - "fd %d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd))); + MXS_ERROR("%lu [gw_MySQLAccept] Failed to add dcb %p for " + "fd %d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd); rc = 1; goto return_rc; } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_MySQLAccept] Added dcb %p for fd " - "%d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd))); + MXS_DEBUG("%lu [gw_MySQLAccept] Added dcb %p for fd " + "%d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd); } } /**< while 1 */ #if defined(SS_DEBUG) @@ -1793,14 +1725,12 @@ static int gw_error_client_event( session = dcb->session; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_error_client_event] Error event handling for DCB %p " - "in state %s, session %p.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state), - (session != NULL ? session : NULL)))); + MXS_DEBUG("%lu [gw_error_client_event] Error event handling for DCB %p " + "in state %s, session %p.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state), + (session != NULL ? session : NULL)); if (session != NULL && session->state == SESSION_STATE_STOPPING) { @@ -1808,9 +1738,7 @@ static int gw_error_client_event( } #if defined(SS_DEBUG) - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Client error event handling."))); + MXS_DEBUG("Client error event handling."); #endif dcb_close(dcb); @@ -1833,16 +1761,14 @@ gw_client_close(DCB *dcb) if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol); } #endif - LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "%lu [gw_client_close]", - pthread_self()))); + MXS_DEBUG("%lu [gw_client_close]", pthread_self()); mysql_protocol_done(dcb); session = dcb->session; /** * session may be NULL if session_alloc failed. * In that case, router session wasn't created. */ - if (session != NULL) + if (session != NULL && SESSION_STATE_DUMMY != session->state) { CHK_SESSION(session); spinlock_acquire(&session->ses_lock); @@ -1896,11 +1822,7 @@ gw_client_hangup_event(DCB *dcb) { goto retblock; } -#if defined(SS_DEBUG) - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "Client hangup error handling."))); -#endif + dcb_close(dcb); retblock: @@ -2009,9 +1931,9 @@ int do_ssl_accept(MySQLProtocol* protocol) queue and wait for more.*/ rval = 0; - skygw_log_write_flush(LT,"SSL_accept ongoing for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); + MXS_INFO("SSL_accept ongoing for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); return 0; break; case 1: @@ -2027,9 +1949,9 @@ int do_ssl_accept(MySQLProtocol* protocol) rval = 1; - skygw_log_write_flush(LT,"SSL_accept done for %s@%s", - protocol->owner_dcb->user, - protocol->owner_dcb->remote); + MXS_INFO("SSL_accept done for %s@%s", + protocol->owner_dcb->user, + protocol->owner_dcb->remote); break; case -1: @@ -2038,20 +1960,17 @@ int do_ssl_accept(MySQLProtocol* protocol) protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_FAILED; spinlock_release(&protocol->protocol_lock); rval = -1; - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept for %s", - protocol->owner_dcb->remote); + MXS_ERROR("Fatal error in SSL_accept for %s", + protocol->owner_dcb->remote); break; default: - skygw_log_write_flush(LE, - "Error: Fatal error in SSL_accept, returned value was %d.", - rval); + MXS_ERROR("Fatal error in SSL_accept, returned value was %d.", rval); break; } #ifdef SS_DEBUG - skygw_log_write(LD,"[do_ssl_accept] Protocol state: %s", - gw_mysql_protocol_state2string(protocol->protocol_auth_state)); + MXS_DEBUG("[do_ssl_accept] Protocol state: %s", + gw_mysql_protocol_state2string(protocol->protocol_auth_state)); #endif return rval; diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index c2bec47da..5ed0d5b24 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -50,11 +50,6 @@ /* The following can be compared using memcmp to detect a null password */ uint8_t null_client_sha1[MYSQL_SCRAMBLE_LEN]=""; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - extern int gw_read_backend_event(DCB* dcb); extern int gw_write_backend_event(DCB *dcb); extern int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue); @@ -91,13 +86,11 @@ MySQLProtocol* mysql_protocol_init( int eno = errno; errno = 0; char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [mysql_init_protocol] MySQL protocol init failed : " - "memory allocation due error %d, %s.", - pthread_self(), - eno, - strerror_r(eno, errbuf, sizeof(errbuf))))); + MXS_ERROR("%lu [mysql_init_protocol] MySQL protocol init failed : " + "memory allocation due error %d, %s.", + pthread_self(), + eno, + strerror_r(eno, errbuf, sizeof(errbuf))); goto return_p; } p->protocol_state = MYSQL_PROTOCOL_ALLOC; @@ -192,13 +185,11 @@ int gw_read_backend_handshake( if (h_len <= 4) { /* log error this exit point */ conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_handshake] after " - "dcb_read, fd %d, " - "state = MYSQL_HANDSHAKE_FAILED.", - dcb->fd, - pthread_self()))); + MXS_DEBUG("%lu [gw_read_backend_handshake] after " + "dcb_read, fd %d, " + "state = MYSQL_HANDSHAKE_FAILED.", + pthread_self(), + dcb->fd); return 1; } @@ -211,24 +202,20 @@ int gw_read_backend_handshake( conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_receive_backend_auth] Invalid " - "authentication message from backend dcb %p " - "fd %d, ptr[4] = %p, error code %d, msg %s.", - pthread_self(), - dcb, - dcb->fd, - payload[4], - errcode, - bufstr))); + MXS_DEBUG("%lu [gw_receive_backend_auth] Invalid " + "authentication message from backend dcb %p " + "fd %d, ptr[4] = %d, error code %d, msg %s.", + pthread_self(), + dcb, + dcb->fd, + payload[4], + errcode, + bufstr); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Invalid authentication message " - "from backend. Error code: %d, Msg : %s", - errcode, - bufstr))); + MXS_ERROR("Invalid authentication message " + "from backend. Error code: %d, Msg : %s", + errcode, + bufstr); /** * If ER_HOST_IS_BLOCKED is found @@ -237,12 +224,14 @@ int gw_read_backend_handshake( */ if (errcode == 1129) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Server %s has been put into maintenance mode due to the server blocking connections from MaxScale. Run 'mysqladmin -h %s -P %d flush-hosts' on this server before taking this server out of maintenance mode.", - dcb->server->unique_name, - dcb->server->name, - dcb->server->port))); + MXS_ERROR("Server %s has been put into maintenance mode due " + "to the server blocking connections from MaxScale. " + "Run 'mysqladmin -h %s -P %d flush-hosts' on this " + "server before taking this server out of maintenance " + "mode.", + dcb->server->unique_name, + dcb->server->name, + dcb->server->port); server_set_status(dcb->server, SERVER_MAINT); } @@ -260,14 +249,11 @@ int gw_read_backend_handshake( conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_handshake] after " - "gw_mysql_get_byte3, fd %d, " - "state = MYSQL_HANDSHAKE_FAILED.", - pthread_self(), - dcb->fd, - pthread_self()))); + MXS_DEBUG("%lu [gw_read_backend_handshake] after " + "gw_mysql_get_byte3, fd %d, " + "state = MYSQL_HANDSHAKE_FAILED.", + pthread_self(), + dcb->fd); return 1; } @@ -285,14 +271,11 @@ int gw_read_backend_handshake( */ conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_read_backend_handshake] after " - "gw_decode_mysql_server_handshake, fd %d, " - "state = MYSQL_HANDSHAKE_FAILED.", - pthread_self(), - conn->owner_dcb->fd, - pthread_self()))); + MXS_DEBUG("%lu [gw_read_backend_handshake] after " + "gw_decode_mysql_server_handshake, fd %d, " + "state = MYSQL_HANDSHAKE_FAILED.", + pthread_self(), + conn->owner_dcb->fd); while((head = gwbuf_consume(head, GWBUF_LENGTH(head)))); return 1; } @@ -452,24 +435,20 @@ int gw_receive_backend_auth( char* err = strndup(&((char *)ptr)[8], 5); char* bufstr = strndup(&((char *)ptr)[13], len-4-5); - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_receive_backend_auth] Invalid " - "authentication message from backend dcb %p " - "fd %d, ptr[4] = %p, error %s, msg %s.", - pthread_self(), - dcb, - dcb->fd, - ptr[4], - err, - bufstr))); + MXS_DEBUG("%lu [gw_receive_backend_auth] Invalid " + "authentication message from backend dcb %p " + "fd %d, ptr[4] = %d, error %s, msg %s.", + pthread_self(), + dcb, + dcb->fd, + ptr[4], + err, + bufstr); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Invalid authentication message " - "from backend. Error : %s, Msg : %s", - err, - bufstr))); + MXS_ERROR("Invalid authentication message " + "from backend. Error : %s, Msg : %s", + err, + bufstr); free(bufstr); free(err); @@ -477,21 +456,17 @@ int gw_receive_backend_auth( } else { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_receive_backend_auth] Invalid " - "authentication message from backend dcb %p " - "fd %d, ptr[4] = %p", - pthread_self(), - dcb, - dcb->fd, - ptr[4]))); + MXS_DEBUG("%lu [gw_receive_backend_auth] Invalid " + "authentication message from backend dcb %p " + "fd %d, ptr[4] = %d", + pthread_self(), + dcb, + dcb->fd, + ptr[4]); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Invalid authentication message " - "from backend. Packet type : %p", - ptr[4]))); + MXS_ERROR("Invalid authentication message " + "from backend. Packet type : %d", + ptr[4]); } /*< * Remove data from buffer. @@ -505,33 +480,29 @@ int gw_receive_backend_auth( * although no bytes was read. */ rc = 0; - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [gw_receive_backend_auth] Read zero bytes from " - "backend dcb %p fd %d in state %s. n %d, head %p, len %d", - pthread_self(), - dcb, - dcb->fd, - STRDCBSTATE(dcb->state), - n, - head, - (head == NULL) ? 0 : GWBUF_LENGTH(head)))); + MXS_DEBUG("%lu [gw_receive_backend_auth] Read zero bytes from " + "backend dcb %p fd %d in state %s. n %d, head %p, len %ld", + pthread_self(), + dcb, + dcb->fd, + STRDCBSTATE(dcb->state), + n, + head, + (head == NULL) ? 0 : GWBUF_LENGTH(head)); } else { ss_dassert(n < 0 && head == NULL); rc = -1; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_receive_backend_auth] Reading from backend dcb %p " - "fd %d in state %s failed. n %d, head %p, len %d", - pthread_self(), - dcb, - dcb->fd, - STRDCBSTATE(dcb->state), - n, - head, - (head == NULL) ? 0 : GWBUF_LENGTH(head)))); + MXS_DEBUG("%lu [gw_receive_backend_auth] Reading from backend dcb %p " + "fd %d in state %s failed. n %d, head %p, len %ld", + pthread_self(), + dcb, + dcb->fd, + STRDCBSTATE(dcb->state), + n, + head, + (head == NULL) ? 0 : GWBUF_LENGTH(head)); } return rc; @@ -769,15 +740,13 @@ int gw_do_connect_to_backend( if (so < 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Establishing connection to backend server " - "%s:%d failed.\n\t\t Socket creation failed " - "due %d, %s.", - host, - port, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Establishing connection to backend server " + "%s:%d failed.\n\t\t Socket creation failed " + "due %d, %s.", + host, + port, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); rv = -1; goto return_rv; } @@ -789,15 +758,13 @@ int gw_do_connect_to_backend( if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to set socket options " - "%s:%d failed.\n\t\t Socket configuration failed " - "due %d, %s.", - host, - port, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); rv = -1; /** Close socket */ goto close_so; @@ -807,15 +774,13 @@ int gw_do_connect_to_backend( if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to set socket options " - "%s:%d failed.\n\t\t Socket configuration failed " - "due %d, %s.", - host, - port, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); rv = -1; /** Close socket */ goto close_so; @@ -825,15 +790,13 @@ int gw_do_connect_to_backend( if(setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to set socket options " - "%s:%d failed.\n\t\t Socket configuration failed " - "due %d, %s.", - host, - port, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options " + "%s:%d failed.\n\t\t Socket configuration failed " + "due %d, %s.", + host, + port, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); rv = -1; /** Close socket */ goto close_so; @@ -852,27 +815,20 @@ int gw_do_connect_to_backend( else { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to connect backend server %s:%d, " - "due %d, %s.", - host, - port, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to connect backend server %s:%d, " + "due %d, %s.", + host, + port, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); /** Close socket */ goto close_so; } } *fd = so; - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_do_connect_to_backend] Connected to backend server " - "%s:%d, fd %d.", - pthread_self(), - host, - port, - so))); + MXS_DEBUG("%lu [gw_do_connect_to_backend] Connected to backend server " + "%s:%d, fd %d.", + pthread_self(), host, port, so); #if defined(FAKE_CODE) conn_open[so] = true; #endif /* FAKE_CODE */ @@ -885,13 +841,10 @@ close_so: if (close(so) != 0) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error: Failed to " - "close socket %d due %d, %s.", - so, - errno, - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to close socket %d due %d, %s.", + so, + errno, + strerror_r(errno, errbuf, sizeof(errbuf))); } goto return_rv; } @@ -1463,16 +1416,17 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, memcpy(&key.ipv4, client, sizeof(struct sockaddr_in)); key.netmask = 32; key.resource = client_data->db; + if(strlen(dcb->remote) < MYSQL_HOST_MAXLEN) + { + strcpy(key.hostname, dcb->remote); + } - LOGIF(LD, - (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [MySQL Client Auth], checking user [%s@%s]%s%s", - pthread_self(), - key.user, - dcb->remote, - key.resource != NULL ?" db: " :"", - key.resource != NULL ?key.resource :""))); + MXS_DEBUG("%lu [MySQL Client Auth], checking user [%s@%s]%s%s", + pthread_self(), + key.user, + dcb->remote, + key.resource != NULL ?" db: " :"", + key.resource != NULL ?key.resource :""); /* look for user@current_ipv4 now */ user_password = mysql_users_fetch(service->users, &key); @@ -1533,13 +1487,11 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, memset(&key.ipv4, 0, sizeof(struct sockaddr_in)); key.netmask = 0; - LOGIF(LD, - (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [MySQL Client Auth], checking user [%s@%s] with wildcard host [%%]", - pthread_self(), - key.user, - dcb->remote))); + MXS_DEBUG("%lu [MySQL Client Auth], checking user [%s@%s] with " + "wildcard host [%%]", + pthread_self(), + key.user, + dcb->remote); user_password = mysql_users_fetch(service->users, &key); @@ -1553,19 +1505,14 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, * user@% not found. */ - LOGIF(LD, - (skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [MySQL Client Auth], user [%s@%s] not existent", - pthread_self(), - key.user, - dcb->remote))); + MXS_DEBUG("%lu [MySQL Client Auth], user [%s@%s] not existent", + pthread_self(), + key.user, + dcb->remote); - LOGIF(LT,skygw_log_write_flush( - LOGFILE_ERROR, - "Authentication Failed: user [%s@%s] not found.", - key.user, - dcb->remote)); + MXS_INFO("Authentication Failed: user [%s@%s] not found.", + key.user, + dcb->remote); break; } @@ -1625,14 +1572,12 @@ mysql_send_auth_error ( if (dcb->state != DCB_STATE_POLLING) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [mysql_send_auth_error] dcb %p is in a state %s, " - "and it is not in epoll set anymore. Skip error sending.", - pthread_self(), - dcb, - STRDCBSTATE(dcb->state)))); - return 0; + MXS_DEBUG("%lu [mysql_send_auth_error] dcb %p is in a state %s, " + "and it is not in epoll set anymore. Skip error sending.", + pthread_self(), + dcb, + STRDCBSTATE(dcb->state)); + return 0; } mysql_errno = 1045; mysql_error_msg = "Access denied!"; @@ -1833,11 +1778,9 @@ void protocol_archive_srv_command( s1 = &p->protocol_command; #if defined(EXTRA_SS_DEBUG) - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Move command %s from fd %d to command history.", - STRPACKETTYPE(s1->scom_cmd), - p->owner_dcb->fd))); + MXS_INFO("Move command %s from fd %d to command history.", + STRPACKETTYPE(s1->scom_cmd), + p->owner_dcb->fd); #endif /** Copy to history list */ if ((h1 = p->protocol_cmd_history) == NULL) @@ -1910,23 +1853,19 @@ void protocol_add_srv_command( p->protocol_command.scom_next = server_command_init(NULL, cmd); } #if defined(EXTRA_SS_DEBUG) - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Added command %s to fd %d.", - STRPACKETTYPE(cmd), - p->owner_dcb->fd))); + MXS_INFO("Added command %s to fd %d.", + STRPACKETTYPE(cmd), + p->owner_dcb->fd); c = &p->protocol_command; while (c != NULL && c->scom_cmd != MYSQL_COM_UNDEFINED) { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "fd %d : %d %s", - p->owner_dcb->fd, - c->scom_cmd, - STRPACKETTYPE(c->scom_cmd)))); - c = c->scom_next; + MXS_INFO("fd %d : %d %s", + p->owner_dcb->fd, + c->scom_cmd, + STRPACKETTYPE(c->scom_cmd)); + c = c->scom_next; } #endif retblock: @@ -1947,11 +1886,9 @@ void protocol_remove_srv_command( spinlock_acquire(&p->protocol_lock); s = &p->protocol_command; #if defined(EXTRA_SS_DEBUG) - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Removed command %s from fd %d.", - STRPACKETTYPE(s->scom_cmd), - p->owner_dcb->fd))); + MXS_INFO("Removed command %s from fd %d.", + STRPACKETTYPE(s->scom_cmd), + p->owner_dcb->fd); #endif if (s->scom_next == NULL) { @@ -1978,12 +1915,10 @@ mysql_server_cmd_t protocol_get_srv_command( { protocol_remove_srv_command(p); } - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, - "%lu [protocol_get_srv_command] Read command %s for fd %d.", - pthread_self(), - STRPACKETTYPE(cmd), - p->owner_dcb->fd))); + MXS_DEBUG("%lu [protocol_get_srv_command] Read command %s for fd %d.", + pthread_self(), + STRPACKETTYPE(cmd), + p->owner_dcb->fd); return cmd; } @@ -2238,10 +2173,8 @@ char *create_auth_fail_str( if (errstr == NULL) { char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Memory allocation failed due to %s.", - strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Memory allocation failed due to %s.", + strerror_r(errno, errbuf, sizeof(errbuf))); goto retblock; } diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index f70f8c038..324c66524 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -45,11 +45,6 @@ MODULE_INFO info = { "A telnet deamon protocol for simple administration interface" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * @file telnetd.c - telnet daemon protocol module * @@ -121,9 +116,7 @@ version() void ModuleInit() { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Initialise Telnetd Protocol module.\n"))); + MXS_INFO("Initialise Telnetd Protocol module."); } /** @@ -311,6 +304,11 @@ int n_connect = 0; memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); client_dcb->session = session_alloc(dcb->session->service, client_dcb); + if (NULL == client_dcb->session) + { + dcb_close(client_dcb); + return n_connect; + } telnetd_pr = (TELNETD *)malloc(sizeof(TELNETD)); client_dcb->protocol = (void *)telnetd_pr; @@ -382,7 +380,8 @@ int syseno = 0; if(syseno != 0){ char errbuf[STRERROR_BUFLEN]; - LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s", errno, strerror_r(errno, errbuf, sizeof(errbuf))))); + MXS_ERROR("Failed to set socket options. Error %d: %s", + errno, strerror_r(errno, errbuf, sizeof(errbuf))); return 0; } // set NONBLOCKING mode @@ -396,7 +395,7 @@ int syseno = 0; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening telnet connections at %s", config))); + MXS_NOTICE("Listening telnet connections at %s", config); } else { int eno = errno; errno = 0; diff --git a/server/modules/routing/GaleraHACRoute.c b/server/modules/routing/GaleraHACRoute.c index 27394c5c8..a14979a31 100644 --- a/server/modules/routing/GaleraHACRoute.c +++ b/server/modules/routing/GaleraHACRoute.c @@ -47,8 +47,6 @@ #include -extern int lm_enabled_logfiles_bitmask; - static char *version_str = "V1.0.0"; /* The router entry points */ diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index e89664cd8..dda5889d3 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -16,8 +16,10 @@ add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master ${CMAKE_SOURCE_DIR}/server/core/resultset.c ${CMAKE_SOURCE_DIR}/server/core/load_utils.c ${CMAKE_SOURCE_DIR}/server/core/monitor.c ${CMAKE_SOURCE_DIR}/server/core/gw_utils.c ${CMAKE_SOURCE_DIR}/server/core/thread.c ${CMAKE_SOURCE_DIR}/server/core/secrets.c + ${CMAKE_SOURCE_DIR}/server/core/random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc) + target_link_libraries(maxbinlogcheck utils ssl pthread ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} aio rt crypt dl crypto inih z m stdc++ ${CURL_LIBRARIES}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index 866019d5d..8277ab2ed 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -46,8 +46,10 @@ * If set those values are sent to slaves instead of * saved master responses * 23/08/2015 Massimiliano Pinto Added strerror_r + * 09/09/2015 Martin Brampton Modify error handler * 30/09/2015 Massimiliano Pinto Addition of send_slave_heartbeat option * 23/10/2015 Markus Makela Added current_safe_event + * 27/10/2015 Martin Brampton Amend getCapabilities to return RCAP_TYPE_NO_RSESSION * * @endverbatim */ @@ -75,10 +77,6 @@ #include #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static char *version_str = "V2.0.0"; /* The router entry points */ @@ -101,7 +99,7 @@ static void errorReply( error_action_t action, bool *succp); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); static int blr_handler_config(void *userdata, const char *section, const char *name, const char *value); static int blr_handle_config_item(const char *name, const char *value, ROUTER_INSTANCE *inst); static int blr_set_service_mysql_user(SERVICE *service); @@ -115,7 +113,6 @@ extern int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debu void blr_master_close(ROUTER_INSTANCE *); char * blr_last_event_description(ROUTER_INSTANCE *router); extern int MaxScaleUptime(); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); char *blr_get_event_description(ROUTER_INSTANCE *router, uint8_t event); /** The module object definition */ @@ -472,8 +469,8 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Warning : invalid heartbeat period %s." - " Setting it to default value %d.", - value, inst->heartbeat ))); + " Setting it to default value %ld.", + value, inst->heartbeat))); } else { inst->heartbeat = h_val; } @@ -532,8 +529,9 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; if (inst->serverid <= 0) { skygw_log_write_flush(LOGFILE_ERROR, - "Error : Service %s, server-id is not configured. Please configure it with a unique positive integer value (1..2^32-1)", - service->name, inst->serverid); + "Error : Service %s, server-id is not configured. " + "Please configure it with a unique positive integer value (1..2^32-1)", + service->name); free(inst); return NULL; } @@ -583,6 +581,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = ""; inst->service->name))); if (service->users) { users_free(service->users); + service->users = NULL; } free(inst); @@ -661,6 +660,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) { @@ -889,7 +889,7 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "%s: Master %s disconnected after %ld seconds. " - "%d events read,", + "%lu events read,", router->service->name, router->service->dbref->server->name, time(0) - router->connect_time, router->stats.n_binlogs_ses))); LOGIF(LE, (skygw_log_write_flush( @@ -1393,8 +1393,8 @@ int len; * @param router_session The router session * @param message The error message to reply * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION - * @param succp Result of action + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true iff router can continue * */ static void @@ -1407,12 +1407,6 @@ char msg[STRERROR_BUFLEN + 1 + 5] = ""; char *errmsg; unsigned long mysql_errno; - if (action == ERRACT_RESET) - { - backend_dcb->dcb_errhandle_called = false; - return; - } - /** Don't handle same error twice on same DCB */ if (backend_dcb->dcb_errhandle_called) { @@ -1466,10 +1460,11 @@ unsigned long mysql_errno; if (errmsg) free(errmsg); *succp = true; + dcb_close(backend_dcb); LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, "%s: Master %s disconnected after %ld seconds. " - "%d events read.", + "%lu events read.", router->service->name, router->service->dbref->server->name, time(0) - router->connect_time, router->stats.n_binlogs_ses))); blr_master_reconnect(router); @@ -1524,9 +1519,9 @@ static void rses_end_locked_router_action(ROUTER_SLAVE * rses) } -static uint8_t getCapabilities(ROUTER *inst, void *router_session) +static int getCapabilities() { - return 0; + return RCAP_TYPE_NO_RSESSION; } /** diff --git a/server/modules/routing/binlog/blr_cache.c b/server/modules/routing/binlog/blr_cache.c index 400114b9b..63b1e8f9e 100644 --- a/server/modules/routing/binlog/blr_cache.c +++ b/server/modules/routing/binlog/blr_cache.c @@ -53,11 +53,6 @@ #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - - /** * Initialise the cache for this instanceof the binlog router. As a side * effect also determine the binlog file to read and the position to read diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index 3310c92e7..8da5d97b7 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -63,10 +63,6 @@ #include #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static int blr_file_create(ROUTER_INSTANCE *router, char *file); static void blr_file_append(ROUTER_INSTANCE *router, char *file); static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr); @@ -286,7 +282,7 @@ int fd; /* If for any reason the file's length is between 1 and 3 bytes * then report an error. */ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, - "%s: binlog file %s has an invalid length %d.", + "%s: binlog file %s has an invalid length %lu.", router->service->name, path, router->current_pos))); close(fd); spinlock_release(&router->binlog_lock); @@ -484,7 +480,7 @@ struct stat statb; { case 0: LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "Reached end of binlog file '%s' at %d.", + "Reached end of binlog file '%s' at %lu.", file->binlogname, pos))); /* set ok indicator */ @@ -552,7 +548,7 @@ struct stat statb; { case 0: LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "Reached end of binlog file at %d.", + "Reached end of binlog file at %lu.", pos))); /* set ok indicator */ @@ -929,8 +925,8 @@ int fde_seen = 0; LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE, "Transaction Summary for binlog '%s'\n" "\t\t\tDescription %17s%17s%17s\n\t\t\t" - "No. of Transactions %16llu\n\t\t\t" - "No. of Events %16llu %16.1f %16llu\n\t\t\t" + "No. of Transactions %16lu\n\t\t\t" + "No. of Events %16lu %16.1f %16lu\n\t\t\t" "No. of Bytes %16.1f%s%16.1f%s%16.1f%s", router->binlog_name, "Total", "Average", "Max", n_transactions, total_events, @@ -1036,7 +1032,7 @@ int fde_seen = 0; if (hdr.event_type > MAX_EVENT_TYPE_MARIADB10) { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Invalid MariaDB 10 event type 0x%x. " - "Binlog file is %s, position %d", + "Binlog file is %s, position %llu", hdr.event_type, router->binlog_name, pos))); @@ -1046,7 +1042,7 @@ int fde_seen = 0; if (hdr.event_type > MAX_EVENT_TYPE) { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Invalid event type 0x%x. " - "Binlog file is %s, position %d", + "Binlog file is %s, position %llu", hdr.event_type, router->binlog_name, pos))); @@ -1308,7 +1304,7 @@ int fde_seen = 0; if(debug) LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, - "- Rotate event @ %llu, next file is [%s] @ %llu", + "- Rotate event @ %llu, next file is [%s] @ %lu", pos, file, new_pos))); } @@ -1331,7 +1327,7 @@ int fde_seen = 0; if (pending_transaction > 0) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "ERROR: Transaction cannot be @ pos %llu: " - "Another MariaDB 10 transaction (GTID %lu-%lu-%llu)" + "Another MariaDB 10 transaction (GTID %u-%u-%lu)" " was opened at %llu", pos, domainid, hdr.serverid, n_sequence, last_known_commit))); @@ -1346,7 +1342,7 @@ int fde_seen = 0; if (debug) LOGIF(LD, (skygw_log_write_flush(LOGFILE_DEBUG, - "> MariaDB 10 Transaction (GTID %lu-%lu-%llu)" + "> MariaDB 10 Transaction (GTID %u-%u-%lu)" " starts @ pos %llu", domainid, hdr.serverid, n_sequence, pos))); } @@ -1440,7 +1436,7 @@ int fde_seen = 0; /* pos and next_pos sanity checks */ if (hdr.next_pos > 0 && hdr.next_pos < pos) { LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE, - "Binlog %s: next pos %llu < pos %llu, truncating to %llu", + "Binlog %s: next pos %u < pos %llu, truncating to %llu", router->binlog_name, hdr.next_pos, pos, @@ -1469,7 +1465,7 @@ int fde_seen = 0; if (hdr.next_pos > 0 && hdr.next_pos != (pos + hdr.event_size)) { LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE, - "Binlog %s: next pos %llu != (pos %llu + event_size %llu), truncating to %llu", + "Binlog %s: next pos %u != (pos %llu + event_size %u), truncating to %llu", router->binlog_name, hdr.next_pos, pos, @@ -1513,7 +1509,7 @@ int fde_seen = 0; } else { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Current event type %lu @ %llu has nex pos = %llu : exiting", hdr.event_type, pos, hdr.next_pos))); + "Current event type %d @ %llu has nex pos = %u : exiting", hdr.event_type, pos, hdr.next_pos))); break; } @@ -1663,7 +1659,7 @@ char err_msg[STRERROR_BUFLEN]; snprintf(filename,(PATH_MAX - 4), "%s/master.ini", path); - snprintf(tmp_file, (PATH_MAX -4), filename); + snprintf(tmp_file, (PATH_MAX - 4), "%s", filename); strcat(tmp_file, ".tmp"); diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index 43fa9b9ea..6251837cd 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -78,10 +78,6 @@ #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static GWBUF *blr_make_query(char *statement); static GWBUF *blr_make_registration(ROUTER_INSTANCE *router); static GWBUF *blr_make_binlog_dump(ROUTER_INSTANCE *router); @@ -401,7 +397,7 @@ char task_name[BLRM_TASK_NAME_LEN + 1] = ""; LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, - "%s: Received error: %u, '%s' from master during '%s' phase " + "%s: Received error: %lu, '%s' from master during '%s' phase " "of the master state machine.", router->service->name, mysql_errno, msg_err, @@ -872,11 +868,11 @@ int n_bufs = -1, pn_bufs = -1; if ((msg = malloc(len)) == NULL) { LOGIF(LE,(skygw_log_write( - LOGFILE_ERROR, - "Insufficient memory to buffer event " - "of %d bytes. Binlog %s @ %d.", - len, router->binlog_name, - router->current_pos))); + LOGFILE_ERROR, + "Insufficient memory to buffer event " + "of %d bytes. Binlog %s @ %lu.", + len, router->binlog_name, + router->current_pos))); break; } @@ -896,10 +892,10 @@ int n_bufs = -1, pn_bufs = -1; if (remainder) { LOGIF(LE,(skygw_log_write( - LOGFILE_ERROR, + LOGFILE_ERROR, "Expected entire message in buffer " "chain, but failed to create complete " - "message as expected. %s @ %d", + "message as expected. %s @ %lu", router->binlog_name, router->current_pos))); free(msg); @@ -920,7 +916,7 @@ int n_bufs = -1, pn_bufs = -1; router->stats.n_residuals++; LOGIF(LD,(skygw_log_write( LOGFILE_DEBUG, - "Residual data left after %d records. %s @ %d", + "Residual data left after %lu records. %s @ %lu", router->stats.n_binlogs, router->binlog_name, router->current_pos))); break; @@ -971,7 +967,7 @@ int n_bufs = -1, pn_bufs = -1; LOGIF(LE,(skygw_log_write( LOGFILE_ERROR, "Packet length is %d, but event size is %d, " - "binlog file %s position %d " + "binlog file %s position %lu " "reslen is %d and preslen is %d, " "length of previous event %d. %s", len, hdr.event_size, @@ -1037,7 +1033,7 @@ int n_bufs = -1, pn_bufs = -1; LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, "%s: Checksum error in event " "from master, " - "binlog %s @ %d. " + "binlog %s @ %lu. " "Closing master connection.", router->service->name, router->binlog_name, @@ -1094,7 +1090,7 @@ int n_bufs = -1, pn_bufs = -1; LOGIF(LE,(skygw_log_write_flush(LOGFILE_ERROR, "Error: a MariaDB 10 transaction " "is already open " - "@ %lu (GTID %lu-%lu-%llu) and " + "@ %lu (GTID %u-%u-%lu) and " "a new one starts @ %lu", router->binlog_position, domainid, hdr.serverid, n_sequence, @@ -1173,7 +1169,7 @@ int n_bufs = -1, pn_bufs = -1; // Fake format description message LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, "Replication fake event. " - "Binlog %s @ %d.", + "Binlog %s @ %lu.", router->binlog_name, router->current_pos))); router->stats.n_fakeevents++; @@ -1219,7 +1215,7 @@ int n_bufs = -1, pn_bufs = -1; LOGIF(LD,(skygw_log_write( LOGFILE_DEBUG, "Replication heartbeat. " - "Binlog %s @ %d.", + "Binlog %s @ %lu.", router->binlog_name, router->current_pos))); @@ -1356,7 +1352,7 @@ int n_bufs = -1, pn_bufs = -1; /* Some events have been sent */ LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, "Some events were not distributed to slaves for a pending transaction " - "in %s at %lu. Last distributed even at %lu, last event from master at %lu", + "in %s at %lu. Last distributed even at %llu, last event from master at %lu", router->binlog_name, router->binlog_position, pos, @@ -1389,7 +1385,7 @@ int n_bufs = -1, pn_bufs = -1; "Artificial event not written " "to disk or distributed. " "Type 0x%x, Length %d, Binlog " - "%s @ %d.", + "%s @ %lu.", hdr.event_type, hdr.event_size, router->binlog_name, @@ -1444,7 +1440,7 @@ int n_bufs = -1, pn_bufs = -1; spinlock_release(&router->lock); LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, - "Error packet in binlog stream.%s @ %d.", + "Error packet in binlog stream.%s @ %lu.", router->binlog_name, router->current_pos))); @@ -1691,7 +1687,7 @@ int action; * try to resolve the issue. */ LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Slave %d is ahead of expected position %s@%d. " + "Slave %d is ahead of expected position %s@%lu. " "Expected position %d", slave->serverid, slave->binlogfile, (unsigned long)slave->binlog_pos, @@ -1927,7 +1923,7 @@ int event_limit; if (pos > end_pos) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "Error: Reading saved events, the specified pos %lu " + "Error: Reading saved events, the specified pos %llu " "is ahead of current pos %lu for file %s", pos, router->current_pos, router->binlog_name))); return NULL; @@ -1940,14 +1936,14 @@ int event_limit; { case 0: LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, - "Reading saved events: reached end of binlog file at %d.", pos))); + "Reading saved events: reached end of binlog file at %llu.", pos))); break; case -1: { char err_msg[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: failed to read binlog " - "file %s at position %d" + "file %s at position %llu" " (%s).", router->binlog_name, pos, strerror_r(errno, err_msg, sizeof(err_msg))))); @@ -1962,7 +1958,7 @@ int event_limit; LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: short read when reading the header. " "Expected 19 bytes but got %d bytes. " - "Binlog file is %s, position %d", + "Binlog file is %s, position %llu", n, router->binlog_name, pos))); break; } @@ -1983,7 +1979,7 @@ int event_limit; { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: invalid event type 0x%x. " - "Binlog file is %s, position %d", + "Binlog file is %s, position %llu", hdr->event_type, router->binlog_name, pos))); return NULL; @@ -1993,7 +1989,7 @@ int event_limit; { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: failed to allocate memory for binlog entry, " - "size %d at %d.", + "size %d at %llu.", hdr->event_size, pos))); return NULL; } @@ -2009,14 +2005,14 @@ int event_limit; { char err_msg[STRERROR_BUFLEN]; LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, - "Error: Reading saved events: the event at %ld in %s. " + "Error: Reading saved events: the event at %llu in %s. " "%s, expected %d bytes.", pos, router->binlog_name, strerror_r(errno, err_msg, sizeof(err_msg)), hdr->event_size - 19))); } else { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: short read when reading " - "the event at %ld in %s. " + "the event at %llu in %s. " "Expected %d bytes got %d bytes.", pos, router->binlog_name, hdr->event_size - 19, n))); @@ -2025,7 +2021,7 @@ int event_limit; LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Error: Reading saved events: binlog event " "is close to the end of the binlog file, " - "current file size is %u.", end_pos))); + "current file size is %llu.", end_pos))); } } @@ -2153,7 +2149,7 @@ char *event_desc = NULL; if (router->master_state == BLRM_BINLOGDUMP && router->lastEventReceived > 0) { if ((t_now - router->stats.lastReply) > (router->heartbeat + BLR_NET_LATENCY_WAIT_TIME)) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, - "ERROR: No event received from master %s:%d in heartbeat period (%d seconds), last event (%s %d) received %lu seconds ago. Assuming connection is dead and reconnecting.", + "ERROR: No event received from master %s:%d in heartbeat period (%lu seconds), last event (%s %d) received %lu seconds ago. Assuming connection is dead and reconnecting.", router->service->dbref->server->name, router->service->dbref->server->port, router->heartbeat, diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 649e4e832..0bb6abd86 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -58,6 +58,7 @@ * 25/09/2015 Massimiliano Pinto Addition of slave heartbeat: * the period set during registration is checked * and heartbeat event might be sent to the affected slave. + * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB * 23/10/15 Markus Makela Added current_safe_event * * @endverbatim @@ -154,10 +155,6 @@ static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave void poll_fake_write_event(DCB *dcb); -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * Process a request packet from the slave server. * @@ -2003,7 +2000,7 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; blr_close_binlog(router, slave->file); if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, "blr_close_binlog took %d maxscale beats", + LOGFILE_ERROR, "blr_close_binlog took %lu maxscale beats", hkheartbeat - beat1))); blr_slave_rotate(router, slave, GWBUF_DATA(record)); beat1 = hkheartbeat; @@ -2040,7 +2037,7 @@ char read_errmsg[BINLOG_ERROR_MSG_LEN+1]; } if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, "blr_open_binlog took %d beats", + LOGFILE_ERROR, "blr_open_binlog took %lu beats", hkheartbeat - beat1))); } slave->stats.n_bytes += gwbuf_length(head); @@ -2245,6 +2242,15 @@ blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data) ROUTER_SLAVE *slave = (ROUTER_SLAVE *)data; ROUTER_INSTANCE *router = slave->router; + if (NULL == dcb->session->router_session) + { + /* + * The following processing will fail if there is no router session, + * because the "data" parameter will not contain meaningful data, + * so we have no choice but to stop here. + */ + return 0; + } if (reason == DCB_REASON_DRAINED) { if (slave->state == BLRS_DUMPING) @@ -2981,7 +2987,7 @@ blr_start_slave(ROUTER_INSTANCE* router, ROUTER_SLAVE* slave) LOGFILE_ERROR, "Warning: a transaction is still opened at pos %lu" " File %s will be truncated. " - "Next binlog file is %s at pos %lu, " + "Next binlog file is %s at pos %d, " "START SLAVE is required again.", router->last_safe_pos, router->prevbinlog, @@ -3368,7 +3374,7 @@ int blr_handle_change_master(ROUTER_INSTANCE* router, char *command, char *error router->binlog_name))); } - LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s: New MASTER_LOG_POS is [%u]", + LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s: New MASTER_LOG_POS is [%lu]", router->service->name, router->current_pos))); } diff --git a/server/modules/routing/binlog/maxbinlogcheck.c b/server/modules/routing/binlog/maxbinlogcheck.c index ece9058ff..6706c0b65 100644 --- a/server/modules/routing/binlog/maxbinlogcheck.c +++ b/server/modules/routing/binlog/maxbinlogcheck.c @@ -63,9 +63,6 @@ #include #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; extern int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug); extern uint32_t extract_field(uint8_t *src, int bits); static void printVersion(const char *progname); @@ -89,8 +86,6 @@ return 1; } int main(int argc, char **argv) { - char** arg_vector; - int arg_count = 4; ROUTER_INSTANCE *inst; int fd; int ret; @@ -129,36 +124,16 @@ int main(int argc, char **argv) { num_args = optind; - 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] = "-j"; - arg_vector[2] = "/tmp/maxbinlogcheck"; - arg_vector[3] = "-o"; - arg_vector[4] = NULL; - skygw_logmanager_init(arg_count,arg_vector); - - skygw_log_set_augmentation(0); - - free(arg_vector); - - if (!debug_out) - skygw_log_disable(LOGFILE_DEBUG); - else - skygw_log_enable(LOGFILE_DEBUG); + mxs_log_init(NULL, NULL, LOG_TARGET_DEFAULT); + mxs_log_set_augmentation(0); + mxs_log_set_priority_enabled(LOG_DEBUG, debug_out); if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) { 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; } @@ -181,8 +156,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); @@ -214,13 +189,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))); + "Check retcode: %i, Binlog Pos = %lu", 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 3741130be..257994351 100644 --- a/server/modules/routing/binlog/test/testbinlog.c +++ b/server/modules/routing/binlog/test/testbinlog.c @@ -51,9 +51,6 @@ #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; static void printVersion(const char *progname); static void printUsage(const char *progname); extern int blr_test_parse_change_master_command(char *input, char *error_string, CHANGE_MASTER_OPTIONS *config); @@ -76,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 = 4; char error_string[BINLOG_ERROR_MSG_LEN + 1] = ""; CHANGE_MASTER_OPTIONS change_master; char query[255+1]=""; @@ -94,26 +89,12 @@ 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)); + 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] = "-j"; - arg_vector[2] = "/tmp/maxbinlogcheck"; - arg_vector[3] = "-o"; - arg_vector[4] = NULL; - skygw_logmanager_init(arg_count,arg_vector); - free(arg_vector); - - skygw_log_disable(LOGFILE_DEBUG); - skygw_log_disable(LOGFILE_TRACE); - skygw_log_disable(LOGFILE_ERROR); - skygw_log_disable(LOGFILE_MESSAGE); + mxs_log_set_priority_enabled(LOG_DEBUG, false); + mxs_log_set_priority_enabled(LOG_INFO, false); + mxs_log_set_priority_enabled(LOG_NOTICE, false); + mxs_log_set_priority_enabled(LOG_ERR, false); service = service_alloc("test_service", "binlogrouter"); service->credentials.name = strdup("foo"); @@ -146,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; } @@ -597,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/cli.c b/server/modules/routing/cli.c index 5f927cd3c..18a54ab3a 100644 --- a/server/modules/routing/cli.c +++ b/server/modules/routing/cli.c @@ -53,11 +53,6 @@ MODULE_INFO info = { "The admin user interface" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static char *version_str = "V1.0.0"; /* The router entry points */ @@ -67,7 +62,7 @@ static void closeSession(ROUTER *instance, void *router_session); static void freeSession(ROUTER *instance, void *router_session); static int execute(ROUTER *instance, void *router_session, GWBUF *queue); static void diagnostics(ROUTER *instance, DCB *dcb); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); /** The module object definition */ static ROUTER_OBJECT MyObject = { @@ -105,10 +100,7 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise CLI router module %s.\n", - version_str))); + MXS_NOTICE("Initialise CLI router module %s.", version_str); spinlock_init(&instlock); instances = NULL; } @@ -154,12 +146,7 @@ int i; { for (i = 0; options[i]; i++) { - { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Unknown option for CLI '%s'\n", - options[i]))); - } + MXS_ERROR("Unknown option for CLI '%s'", options[i]); } } @@ -292,9 +279,7 @@ diagnostics(ROUTER *instance, DCB *dcb) return; /* Nothing to do currently */ } -static uint8_t getCapabilities( - ROUTER* inst, - void* router_session) +static int getCapabilities() { return 0; } diff --git a/server/modules/routing/debugcli.c b/server/modules/routing/debugcli.c index 635847b42..3dcbe0a31 100644 --- a/server/modules/routing/debugcli.c +++ b/server/modules/routing/debugcli.c @@ -52,11 +52,6 @@ MODULE_INFO info = { "The debug user interface" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static char *version_str = "V1.1.1"; /* The router entry points */ @@ -66,7 +61,7 @@ static void closeSession(ROUTER *instance, void *router_session); static void freeSession(ROUTER *instance, void *router_session); static int execute(ROUTER *instance, void *router_session, GWBUF *queue); static void diagnostics(ROUTER *instance, DCB *dcb); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); /** The module object definition */ static ROUTER_OBJECT MyObject = { @@ -104,12 +99,9 @@ version() void ModuleInit() { - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, - "Initialise debug CLI router module %s.\n", - version_str))); - spinlock_init(&instlock); - instances = NULL; + MXS_NOTICE("Initialise debug CLI router module %s.", version_str); + spinlock_init(&instlock); + instances = NULL; } /** @@ -163,10 +155,7 @@ int i; } else { - LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, - "Unknown option for CLI '%s'\n", - options[i]))); + MXS_ERROR("Unknown option for CLI '%s'", options[i]); } } } @@ -315,9 +304,7 @@ diagnostics(ROUTER *instance, DCB *dcb) return; /* Nothing to do currently */ } -static uint8_t getCapabilities( - ROUTER* inst, - void* router_session) +static int getCapabilities() { return 0; } diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index b88a67a19..45e275215 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -33,17 +33,18 @@ * @verbatim * Revision History * - * Date Who Description - * 20/06/13 Mark Riddoch Initial implementation - * 17/07/13 Mark Riddoch Additional commands - * 09/08/13 Massimiliano Pinto Added enable/disable commands (now only for log) - * 20/05/14 Mark Riddoch Added ability to give server and service names rather - * than simply addresses - * 23/05/14 Mark Riddoch Added support for developer and user modes - * 29/05/14 Mark Riddoch Add Filter support - * 16/10/14 Mark Riddoch Add show eventq - * 05/03/15 Massimiliano Pinto Added enable/disable feedback - * 27/05/15 Martin Brampton Add show persistent [server] + * Date Who Description + * 20/06/13 Mark Riddoch Initial implementation + * 17/07/13 Mark Riddoch Additional commands + * 09/08/13 Massimiliano Pinto Added enable/disable commands (now only for log) + * 20/05/14 Mark Riddoch Added ability to give server and service names rather + * than simply addresses + * 23/05/14 Mark Riddoch Added support for developer and user modes + * 29/05/14 Mark Riddoch Add Filter support + * 16/10/14 Mark Riddoch Add show eventq + * 05/03/15 Massimiliano Pinto Added enable/disable feedback + * 27/05/15 Martin Brampton Add show persistent [server] + * 06/11/15 Martin Brampton Add show buffers (conditional compilation) * * @endverbatim */ @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -74,19 +76,20 @@ #include #include +#include -#define MAXARGS 5 +#define MAXARGS 5 -#define ARG_TYPE_ADDRESS 1 -#define ARG_TYPE_STRING 2 -#define ARG_TYPE_SERVICE 3 -#define ARG_TYPE_SERVER 4 -#define ARG_TYPE_DBUSERS 5 -#define ARG_TYPE_SESSION 6 -#define ARG_TYPE_DCB 7 -#define ARG_TYPE_MONITOR 8 -#define ARG_TYPE_FILTER 9 -#define ARG_TYPE_NUMERIC 10 +#define ARG_TYPE_ADDRESS 1 +#define ARG_TYPE_STRING 2 +#define ARG_TYPE_SERVICE 3 +#define ARG_TYPE_SERVER 4 +#define ARG_TYPE_DBUSERS 5 +#define ARG_TYPE_SESSION 6 +#define ARG_TYPE_DCB 7 +#define ARG_TYPE_MONITOR 8 +#define ARG_TYPE_FILTER 9 +#define ARG_TYPE_NUMERIC 10 /** * The subcommand structure @@ -94,161 +97,171 @@ * These are the options that may be passed to a command */ struct subcommand { - char *arg1; - int n_args; - void (*fn)(); - char *help; - char *devhelp; - int arg_types[3]; + char *arg1; + int n_args; + void (*fn)(); + char *help; + char *devhelp; + int arg_types[3]; }; -static void telnetdShowUsers(DCB *); +static void telnetdShowUsers(DCB *); /** * The subcommands of the show command */ struct subcommand showoptions[] = { - { "dcbs", 0, dprintAllDCBs, - "Show all descriptor control blocks (network connections)", - "Show all descriptor control blocks (network connections)", - {0, 0, 0} }, - { "dcb", 1, dprintDCB, - "Show a single descriptor control block e.g. show dcb 0x493340", - "Show a single descriptor control block e.g. show dcb 0x493340", - {ARG_TYPE_DCB, 0, 0} }, - { "dbusers", 1, dcb_usersPrint, - "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers ", - "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers |", - {ARG_TYPE_DBUSERS, 0, 0} }, - { "epoll", 0, dprintPollStats, - "Show the poll statistics", - "Show the poll statistics", - {0, 0, 0} }, - { "eventq", 0, dShowEventQ, - "Show the queue of events waiting to be processed", - "Show the queue of events waiting to be processed", - {0, 0, 0} }, - { "eventstats", 0, dShowEventStats, - "Show the event statistics", - "Show the event statistics", - {0, 0, 0} }, - { "feedbackreport", 0, moduleShowFeedbackReport, - "Show the report of MaxScale loaded modules, suitable for Notification Service", - "Show the report of MaxScale loaded modules, suitable for Notification Service", - {0, 0, 0} }, - { "filter", 1, dprintFilter, - "Show details of a filter, called with a filter name", - "Show details of a filter, called with the address of a filter", - {ARG_TYPE_FILTER, 0, 0} }, - { "filters", 0, dprintAllFilters, - "Show all filters", - "Show all filters", - {0, 0, 0} }, - { "modules", 0, dprintAllModules, - "Show all currently loaded modules", - "Show all currently loaded modules", - {0, 0, 0} }, - { "monitor", 1, monitorShow, - "Show the monitor details", - "Show the monitor details", - {ARG_TYPE_MONITOR, 0, 0} }, - { "monitors", 0, monitorShowAll, - "Show the monitors that are configured", - "Show the monitors that are configured", - {0, 0, 0} }, - { "persistent", 1, dprintPersistentDCBs, - "Show persistent pool for a named server, e.g. show persistent dbnode1", - "Show persistent pool for a server, e.g. show persistent 0x485390. The address may also be replaced with the server name from the configuration file", - {ARG_TYPE_SERVER, 0, 0} }, - { "server", 1, dprintServer, - "Show details for a named server, e.g. show server dbnode1", - "Show details for a server, e.g. show server 0x485390. The address may also be repalced with the server name from the configuration file", - {ARG_TYPE_SERVER, 0, 0} }, - { "servers", 0, dprintAllServers, - "Show all configured servers", - "Show all configured servers", - {0, 0, 0} }, - { "serversjson", 0, dprintAllServersJson, - "Show all configured servers in JSON format", - "Show all configured servers in JSON format", - {0, 0, 0} }, - { "services", 0, dprintAllServices, - "Show all configured services in MaxScale", - "Show all configured services in MaxScale", - {0, 0, 0} }, - { "service", 1, dprintService, - "Show a single service in MaxScale, may be passed a service name", - "Show a single service in MaxScale, may be passed a service name or address of a service object", - {ARG_TYPE_SERVICE, 0, 0} }, - { "session", 1, dprintSession, - "Show a single session in MaxScale, e.g. show session 0x284830", - "Show a single session in MaxScale, e.g. show session 0x284830", - {ARG_TYPE_SESSION, 0, 0} }, - { "sessions", 0, dprintAllSessions, - "Show all active sessions in MaxScale", - "Show all active sessions in MaxScale", - {0, 0, 0} }, - { "tasks", 0, hkshow_tasks, - "Show all active housekeeper tasks in MaxScale", - "Show all active housekeeper tasks in MaxScale", - {0, 0, 0} }, - { "threads", 0, dShowThreads, - "Show the status of the polling threads in MaxScale", - "Show the status of the polling threads in MaxScale", - {0, 0, 0} }, - { "users", 0, telnetdShowUsers, - "Show statistics and user names for the debug interface", - "Show statistics and user names for the debug interface", - {0, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } +#if defined(BUFFER_TRACE) + { "buffers", 0, dprintAllBuffers, + "Show all buffers with backtrace", + "Show all buffers with backtrace", + {0, 0, 0} }, +#endif + { "dcbs", 0, dprintAllDCBs, + "Show all descriptor control blocks (network connections)", + "Show all descriptor control blocks (network connections)", + {0, 0, 0} }, + { "dcb", 1, dprintDCB, + "Show a single descriptor control block e.g. show dcb 0x493340", + "Show a single descriptor control block e.g. show dcb 0x493340", + {ARG_TYPE_DCB, 0, 0} }, + { "dbusers", 1, dcb_usersPrint, + "Show statistics and user names for a service's user table.\n" + "\t\tExample : show dbusers ", + "Show statistics and user names for a service's user table.\n" + "\t\tExample : show dbusers |", + {ARG_TYPE_DBUSERS, 0, 0} }, + { "epoll", 0, dprintPollStats, + "Show the poll statistics", + "Show the poll statistics", + {0, 0, 0} }, + { "eventq", 0, dShowEventQ, + "Show the queue of events waiting to be processed", + "Show the queue of events waiting to be processed", + {0, 0, 0} }, + { "eventstats", 0, dShowEventStats, + "Show the event statistics", + "Show the event statistics", + {0, 0, 0} }, + { "feedbackreport", 0, moduleShowFeedbackReport, + "Show the report of MaxScale loaded modules, suitable for Notification Service", + "Show the report of MaxScale loaded modules, suitable for Notification Service", + {0, 0, 0} }, + { "filter", 1, dprintFilter, + "Show details of a filter, called with a filter name", + "Show details of a filter, called with the address of a filter", + {ARG_TYPE_FILTER, 0, 0} }, + { "filters", 0, dprintAllFilters, + "Show all filters", + "Show all filters", + {0, 0, 0} }, + { "modules", 0, dprintAllModules, + "Show all currently loaded modules", + "Show all currently loaded modules", + {0, 0, 0} }, + { "monitor", 1, monitorShow, + "Show the monitor details", + "Show the monitor details", + {ARG_TYPE_MONITOR, 0, 0} }, + { "monitors", 0, monitorShowAll, + "Show the monitors that are configured", + "Show the monitors that are configured", + {0, 0, 0} }, + { "persistent", 1, dprintPersistentDCBs, + "Show persistent pool for a named server, e.g. show persistent dbnode1", + "Show persistent pool for a server, e.g. show persistent 0x485390. " + "The address may also be replaced with the server name from the configuration file", + {ARG_TYPE_SERVER, 0, 0} }, + { "server", 1, dprintServer, + "Show details for a named server, e.g. show server dbnode1", + "Show details for a server, e.g. show server 0x485390. The address may also be " + "repalced with the server name from the configuration file", + {ARG_TYPE_SERVER, 0, 0} }, + { "servers", 0, dprintAllServers, + "Show all configured servers", + "Show all configured servers", + {0, 0, 0} }, + { "serversjson", 0, dprintAllServersJson, + "Show all configured servers in JSON format", + "Show all configured servers in JSON format", + {0, 0, 0} }, + { "services", 0, dprintAllServices, + "Show all configured services in MaxScale", + "Show all configured services in MaxScale", + {0, 0, 0} }, + { "service", 1, dprintService, + "Show a single service in MaxScale, may be passed a service name", + "Show a single service in MaxScale, may be passed a service name or address of a service object", + {ARG_TYPE_SERVICE, 0, 0} }, + { "session", 1, dprintSession, + "Show a single session in MaxScale, e.g. show session 0x284830", + "Show a single session in MaxScale, e.g. show session 0x284830", + {ARG_TYPE_SESSION, 0, 0} }, + { "sessions", 0, dprintAllSessions, + "Show all active sessions in MaxScale", + "Show all active sessions in MaxScale", + {0, 0, 0} }, + { "tasks", 0, hkshow_tasks, + "Show all active housekeeper tasks in MaxScale", + "Show all active housekeeper tasks in MaxScale", + {0, 0, 0} }, + { "threads", 0, dShowThreads, + "Show the status of the polling threads in MaxScale", + "Show the status of the polling threads in MaxScale", + {0, 0, 0} }, + { "users", 0, telnetdShowUsers, + "Show statistics and user names for the debug interface", + "Show statistics and user names for the debug interface", + {0, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; /** * The subcommands of the list command */ struct subcommand listoptions[] = { - { "clients", 0, dListClients, - "List all the client connections to MaxScale", - "List all the client connections to MaxScale", - {0, 0, 0} }, - { "dcbs", 0, dListDCBs, - "List all the DCBs active within MaxScale", - "List all the DCBs active within MaxScale", - {0, 0, 0} }, - { "filters", 0, dListFilters, - "List all the filters defined within MaxScale", - "List all the filters defined within MaxScale", - {0, 0, 0} }, - { "listeners", 0, dListListeners, - "List all the listeners defined within MaxScale", - "List all the listeners defined within MaxScale", - {0, 0, 0} }, - { "modules", 0, dprintAllModules, - "List all currently loaded modules", - "List all currently loaded modules", - {0, 0, 0} }, - { "monitors", 0, monitorList, - "List all monitors", - "List all monitors", - {0, 0, 0} }, - { "services", 0, dListServices, - "List all the services defined within MaxScale", - "List all the services defined within MaxScale", - {0, 0, 0} }, - { "servers", 0, dListServers, - "List all the servers defined within MaxScale", - "List all the servers defined within MaxScale", - {0, 0, 0} }, - { "sessions", 0, dListSessions, - "List all the active sessions within MaxScale", - "List all the active sessions within MaxScale", - {0, 0, 0} }, - { "threads", 0, dShowThreads, - "List the status of the polling threads in MaxScale", - "List the status of the polling threads in MaxScale", - {0, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { "clients", 0, dListClients, + "List all the client connections to MaxScale", + "List all the client connections to MaxScale", + {0, 0, 0} }, + { "dcbs", 0, dListDCBs, + "List all the DCBs active within MaxScale", + "List all the DCBs active within MaxScale", + {0, 0, 0} }, + { "filters", 0, dListFilters, + "List all the filters defined within MaxScale", + "List all the filters defined within MaxScale", + {0, 0, 0} }, + { "listeners", 0, dListListeners, + "List all the listeners defined within MaxScale", + "List all the listeners defined within MaxScale", + {0, 0, 0} }, + { "modules", 0, dprintAllModules, + "List all currently loaded modules", + "List all currently loaded modules", + {0, 0, 0} }, + { "monitors", 0, monitorList, + "List all monitors", + "List all monitors", + {0, 0, 0} }, + { "services", 0, dListServices, + "List all the services defined within MaxScale", + "List all the services defined within MaxScale", + {0, 0, 0} }, + { "servers", 0, dListServers, + "List all the servers defined within MaxScale", + "List all the servers defined within MaxScale", + {0, 0, 0} }, + { "sessions", 0, dListSessions, + "List all the active sessions within MaxScale", + "List all the active sessions within MaxScale", + {0, 0, 0} }, + { "threads", 0, dShowThreads, + "List the status of the polling threads in MaxScale", + "List the status of the polling threads in MaxScale", + {0, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; extern void shutdown_server(); @@ -259,39 +272,72 @@ static void shutdown_monitor(DCB *dcb, MONITOR *monitor); * The subcommands of the shutdown command */ struct subcommand shutdownoptions[] = { - { "maxscale", - 0, - shutdown_server, - "Shutdown MaxScale", - "Shutdown MaxScale", - {0, 0, 0} - }, - { - "monitor", - 1, - shutdown_monitor, - "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", - "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", - {ARG_TYPE_MONITOR, 0, 0} - }, - { - "service", - 1, - shutdown_service, - "Shutdown a service, e.g. shutdown service \"Sales Database\"", - "Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"", - {ARG_TYPE_SERVICE, 0, 0} - }, - { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} - } + { + "maxscale", + 0, + shutdown_server, + "Shutdown MaxScale", + "Shutdown MaxScale", + {0, 0, 0} + }, + { + "monitor", + 1, + shutdown_monitor, + "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", + "Shutdown a monitor, e.g. shutdown monitor 0x48381e0", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "service", + 1, + shutdown_service, + "Shutdown a service, e.g. shutdown service \"Sales Database\"", + "Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"", + {ARG_TYPE_SERVICE, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + NULL, + {0, 0, 0} + } }; +static void sync_logs(DCB *dcb) +{ + if (mxs_log_flush_sync() == 0) + { + dcb_printf(dcb, "Logs flushed to disk\n"); + } + else + { + dcb_printf(dcb, "Failed to flush logs to disk. Read the error log for " + "more details.\n"); + } +} + +struct subcommand syncoptions[] = +{ + { + "logs", + 0, + sync_logs, + "Flush log files to disk", + "Flush log files to disk", + {0, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + NULL, + {0, 0, 0} + } +}; static void restart_service(DCB *dcb, SERVICE *service); static void restart_monitor(DCB *dcb, MONITOR *monitor); @@ -299,16 +345,16 @@ static void restart_monitor(DCB *dcb, MONITOR *monitor); * The subcommands of the restart command */ struct subcommand restartoptions[] = { - { "monitor", 1, restart_monitor, - "Restart a monitor, e.g. restart monitor 0x48181e0", - "Restart a monitor, e.g. restart monitor 0x48181e0", - {ARG_TYPE_MONITOR, 0, 0} }, - { "service", 1, restart_service, - "Restart a service, e.g. restart service \"Test Service\"", - "Restart a service, e.g. restart service 0x4838320", - {ARG_TYPE_SERVICE, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { "monitor", 1, restart_monitor, + "Restart a monitor, e.g. restart monitor 0x48181e0", + "Restart a monitor, e.g. restart monitor 0x48181e0", + {ARG_TYPE_MONITOR, 0, 0} }, + { "service", 1, restart_service, + "Restart a service, e.g. restart service \"Test Service\"", + "Restart a service, e.g. restart service 0x4838320", + {ARG_TYPE_SERVICE, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; static void set_server(DCB *dcb, SERVER *server, char *bit); @@ -318,21 +364,21 @@ static void set_nbpoll(DCB *dcb, int); * The subcommands of the set command */ struct subcommand setoptions[] = { - { "server", 2, set_server, - "Set the status of a server. E.g. set server dbnode4 master", - "Set the status of a server. E.g. set server 0x4838320 master", - {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, - { "pollsleep", 1, set_pollsleep, - "Set the maximum poll sleep period in milliseconds", - "Set the maximum poll sleep period in milliseconds", - {ARG_TYPE_NUMERIC, 0, 0} }, - { "nbpolls", 1, set_nbpoll, - "Set the number of non-blocking polls", - "Set the number of non-blocking polls", - {ARG_TYPE_NUMERIC, 0, 0} }, + { "server", 2, set_server, + "Set the status of a server. E.g. set server dbnode4 master", + "Set the status of a server. E.g. set server 0x4838320 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, + { "pollsleep", 1, set_pollsleep, + "Set the maximum poll sleep period in milliseconds", + "Set the maximum poll sleep period in milliseconds", + {ARG_TYPE_NUMERIC, 0, 0} }, + { "nbpolls", 1, set_nbpoll, + "Set the number of non-blocking polls", + "Set the number of non-blocking polls", + {ARG_TYPE_NUMERIC, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; static void clear_server(DCB *dcb, SERVER *server, char *bit); @@ -340,12 +386,12 @@ static void clear_server(DCB *dcb, SERVER *server, char *bit); * The subcommands of the clear command */ struct subcommand clearoptions[] = { - { "server", 2, clear_server, - "Clear the status of a server. E.g. clear server dbnode2 master", - "Clear the status of a server. E.g. clear server 0x4838320 master", - {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { "server", 2, clear_server, + "Clear the status of a server. E.g. clear server dbnode2 master", + "Clear the status of a server. E.g. clear server 0x4838320 master", + {ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; static void reload_dbusers(DCB *dcb, SERVICE *service); @@ -355,20 +401,22 @@ static void reload_config(DCB *dcb); * The subcommands of the reload command */ struct subcommand reloadoptions[] = { - { "config", 0, reload_config, - "Reload the configuration data for MaxScale.", - "Reload the configuration data for MaxScale.", - {0, 0, 0} }, - { "dbusers", 1, reload_dbusers, - "Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"", - "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", - {ARG_TYPE_SERVICE, 0, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { "config", 0, reload_config, + "Reload the configuration data for MaxScale.", + "Reload the configuration data for MaxScale.", + {0, 0, 0} }, + { "dbusers", 1, reload_dbusers, + "Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"", + "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", + {ARG_TYPE_SERVICE, 0, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; static void enable_log_action(DCB *, char *); static void disable_log_action(DCB *, char *); +static void enable_log_priority(DCB *, char *); +static void disable_log_priority(DCB *, char *); static void enable_sess_log_action(DCB *dcb, char *arg1, char *arg2); static void disable_sess_log_action(DCB *dcb, char *arg1, char *arg2); static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor); @@ -382,58 +430,68 @@ static void disable_feedback_action(); * * The subcommands of the enable command * */ struct subcommand enableoptions[] = { - { - "heartbeat", - 1, - enable_monitor_replication_heartbeat, - "Enable the monitor replication heartbeat, pass a monitor name as argument", - "Enable the monitor replication heartbeat, pass a monitor name as argument", - {ARG_TYPE_MONITOR, 0, 0} - }, - { - "log", - 1, - enable_log_action, - "Enable Log options for MaxScale, options trace | error | " - "message E.g. enable log message.", - "Enable Log options for MaxScale, options trace | error | " - "message E.g. enable log message.", - {ARG_TYPE_STRING, 0, 0} - }, - { - "sessionlog", - 2, - enable_sess_log_action, - "Enable Log options for a single session. Usage: enable sessionlog [trace | error | " - "message | debug] \t E.g. enable sessionlog message 123.", - "Enable Log options for a single session. Usage: enable sessionlog [trace | error | " - "message | debug] \t E.g. enable sessionlog message 123.", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} - }, - { - "root", - 1, - enable_service_root, - "Enable root access to a service, pass a service name to enable root access", - "Enable root access to a service, pass a service name to enable root access", - {ARG_TYPE_SERVICE, 0, 0} - }, - { - "feedback", - 0, - enable_feedback_action, - "Enable MaxScale modules list sending via http to notification service", - "Enable MaxScale modules list sending via http to notification service", - {0, 0, 0} - }, - { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} - } + { + "heartbeat", + 1, + enable_monitor_replication_heartbeat, + "Enable the monitor replication heartbeat, pass a monitor name as argument", + "Enable the monitor replication heartbeat, pass a monitor name as argument", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "log", + 1, + enable_log_action, + "[deprecated] Enable Log options for MaxScale, options 'trace' | 'error' | 'message'." + "E.g. 'enable log message'.", + "[deprecated] Enable Log options for MaxScale, options 'trace' | 'error' | 'message'." + "E.g. 'enable log message'.", + {ARG_TYPE_STRING, 0, 0} + }, + { + "log-priority", + 1, + enable_log_priority, + "Enable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "E.g.: 'enable log-priority info'.", + "Enable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "E.g.: 'enable log-priority info'.", + {ARG_TYPE_STRING, 0, 0} + }, + { + "sessionlog", + 2, + enable_sess_log_action, + "Enable Log options for a single session. Usage: enable sessionlog [trace | error | " + "message | debug] \t E.g. enable sessionlog message 123.", + "Enable Log options for a single session. Usage: enable sessionlog [trace | error | " + "message | debug] \t E.g. enable sessionlog message 123.", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} + }, + { + "root", + 1, + enable_service_root, + "Enable root access to a service, pass a service name to enable root access", + "Enable root access to a service, pass a service name to enable root access", + {ARG_TYPE_SERVICE, 0, 0} + }, + { + "feedback", + 0, + enable_feedback_action, + "Enable MaxScale modules list sending via http to notification service", + "Enable MaxScale modules list sending via http to notification service", + {0, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + NULL, + {0, 0, 0} + } }; @@ -442,58 +500,68 @@ struct subcommand enableoptions[] = { * * The subcommands of the disable command * */ struct subcommand disableoptions[] = { - { - "heartbeat", - 1, - disable_monitor_replication_heartbeat, - "Disable the monitor replication heartbeat", - "Disable the monitor replication heartbeat", - {ARG_TYPE_MONITOR, 0, 0} - }, - { - "log", - 1, - disable_log_action, - "Disable Log for MaxScale, Options: debug | trace | error | message " - "E.g. disable log debug", - "Disable Log for MaxScale, Options: debug | trace | error | message " - "E.g. disable log debug", - {ARG_TYPE_STRING, 0, 0} - }, - { - "sessionlog", - 2, - disable_sess_log_action, - "Disable Log options for a single session. Usage: disable sessionlog [trace | error | " - "message | debug] \t E.g. disable sessionlog message 123.", - "Disable Log options for a single session. Usage: disable sessionlog [trace | error | " - "message | debug] \t E.g. disable sessionlog message 123.", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} - }, - { - "root", - 1, - disable_service_root, - "Disable root access to a service", - "Disable root access to a service", - {ARG_TYPE_SERVICE, 0, 0} - }, - { - "feedback", - 0, - disable_feedback_action, - "Disable MaxScale modules list sending via http to notification service", - "Disable MaxScale modules list sending via http to notification service", - {0, 0, 0} - }, - { - NULL, - 0, - NULL, - NULL, - NULL, - {0, 0, 0} - } + { + "heartbeat", + 1, + disable_monitor_replication_heartbeat, + "Disable the monitor replication heartbeat", + "Disable the monitor replication heartbeat", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "log", + 1, + disable_log_action, + "[deprecated] Disable Log for MaxScale, Options: 'debug' | 'trace' | 'error' | 'message'." + "E.g. 'disable log debug'.", + "[deprecated] Disable Log for MaxScale, Options: 'debug' | 'trace' | 'error' | 'message'." + "E.g. 'disable log debug'.", + {ARG_TYPE_STRING, 0, 0} + }, + { + "log-priority", + 1, + disable_log_priority, + "Disable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "E.g.: 'disable log-priority info'.", + "Disable a logging priority; options 'err' | 'warning' | 'notice' | 'info' | 'debug'. " + "E.g.: 'disable log-priority info'.", + {ARG_TYPE_STRING, 0, 0} + }, + { + "sessionlog", + 2, + disable_sess_log_action, + "Disable Log options for a single session. Usage: disable sessionlog [trace | error | " + "message | debug] \t E.g. disable sessionlog message 123.", + "Disable Log options for a single session. Usage: disable sessionlog [trace | error | " + "message | debug] \t E.g. disable sessionlog message 123.", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} + }, + { + "root", + 1, + disable_service_root, + "Disable root access to a service", + "Disable root access to a service", + {ARG_TYPE_SERVICE, 0, 0} + }, + { + "feedback", + 0, + disable_feedback_action, + "Disable MaxScale modules list sending via http to notification service", + "Disable MaxScale modules list sending via http to notification service", + {0, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + NULL, + {0, 0, 0} + } }; #if defined(FAKE_CODE) @@ -545,12 +613,12 @@ static void telnetdAddUser(DCB *, char *, char *); * The subcommands of the add command */ struct subcommand addoptions[] = { - { "user", 2, telnetdAddUser, - "Add a new user for the debug interface. E.g. add user john today", - "Add a new user for the debug interface. E.g. add user john today", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, - { NULL, 0, NULL, NULL, NULL, - {0, 0, 0} } + { "user", 2, telnetdAddUser, + "Add a new user for the debug interface. E.g. add user john today", + "Add a new user for the debug interface. E.g. add user john today", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, + { NULL, 0, NULL, NULL, NULL, + {0, 0, 0} } }; @@ -559,66 +627,82 @@ static void telnetdRemoveUser(DCB *, char *, char *); * The subcommands of the remove command */ struct subcommand removeoptions[] = { - { - "user", - 2, - telnetdRemoveUser, - "Remove existing maxscale user. Example : remove user john johnpwd", - "Remove existing maxscale user. Example : remove user john johnpwd", - {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} - }, - { - NULL, 0, NULL, NULL, NULL, {0, 0, 0} - } + { + "user", + 2, + telnetdRemoveUser, + "Remove existing maxscale user. Example : remove user john johnpwd", + "Remove existing maxscale user. Example : remove user john johnpwd", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} + }, + { + NULL, 0, NULL, NULL, NULL, {0, 0, 0} + } }; /** * User command to flush a single logfile * - * @param pdcb The stream to write output to - * @param logname The name of the log + * @param pdcb The stream to write output to + * @param logname The name of the log */ static void flushlog(DCB *pdcb, char *logname) { - if (logname == NULL) - { - } - else if (!strcasecmp(logname, "error")) - { - skygw_log_rotate(LOGFILE_ERROR); - } - else if (!strcasecmp(logname, "message")) - { - skygw_log_rotate(LOGFILE_MESSAGE); - } - else if (!strcasecmp(logname, "trace")) - { - skygw_log_rotate(LOGFILE_TRACE); - } - else if (!strcasecmp(logname, "debug")) - { - skygw_log_rotate(LOGFILE_DEBUG); - } - else - { - dcb_printf(pdcb, "Unexpected logfile name, expected " - "error, message, trace or debug.\n"); - } + bool unrecognized = false; + bool deprecated = false; + + if (!strcasecmp(logname, "error")) + { + deprecated = true; + } + else if (!strcasecmp(logname, "message")) + { + deprecated = true; + } + else if (!strcasecmp(logname, "trace")) + { + deprecated = true; + } + else if (!strcasecmp(logname, "debug")) + { + deprecated = true; + } + else if (!strcasecmp(logname, "maxscale")) + { + ; // nop + } + else + { + 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); + } + } } /** * User command to flush all logfiles * - * @param pdcb The stream to write output to + * @param pdcb The stream to write output to */ 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(); } @@ -626,25 +710,25 @@ flushlogs(DCB *pdcb) * The subcommands of the flush command */ struct subcommand flushoptions[] = { - { - "log", - 1, - flushlog, - "Flush the content of a log file, close that log, rename it and open a new log file", - "Flush the content of a log file, close that log, rename it and open a new log file", - {ARG_TYPE_STRING, 0, 0} - }, - { - "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", - {0, 0, 0} - }, - { - NULL, 0, NULL, NULL, NULL, {0, 0, 0} - } + { + "log", + 1, + flushlog, + "Flush the content of a log file, close that log, rename it and open a new log file", + "Flush the content of a log file, close that log, rename it and open a new log file", + {ARG_TYPE_STRING, 0, 0} + }, + { + "logs", + 0, + flushlogs, + "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} + }, + { + NULL, 0, NULL, NULL, NULL, {0, 0, 0} + } }; @@ -652,25 +736,26 @@ struct subcommand flushoptions[] = { * The debug command table */ static struct { - char *cmd; - struct subcommand *options; + char *cmd; + struct subcommand *options; } cmds[] = { - { "add", addoptions }, - { "clear", clearoptions }, - { "disable", disableoptions }, - { "enable", enableoptions }, + { "add", addoptions }, + { "clear", clearoptions }, + { "disable", disableoptions }, + { "enable", enableoptions }, #if defined(FAKE_CODE) - { "fail", failoptions }, + { "fail", failoptions }, #endif /* FAKE_CODE */ - { "flush", flushoptions }, - { "list", listoptions }, - { "reload", reloadoptions }, - { "remove", removeoptions }, - { "restart", restartoptions }, - { "set", setoptions }, - { "show", showoptions }, - { "shutdown", shutdownoptions }, - { NULL, NULL } + { "flush", flushoptions }, + { "list", listoptions }, + { "reload", reloadoptions }, + { "remove", removeoptions }, + { "restart", restartoptions }, + { "set", setoptions }, + { "show", showoptions }, + { "shutdown", shutdownoptions }, + { "sync", syncoptions }, + { NULL, NULL } }; @@ -678,76 +763,89 @@ static struct { * Convert a string argument to a numeric, observing prefixes * for number bases, e.g. 0x for hex, 0 for octal * - * @param mode The CLI mode - * @param arg The string representation of the argument - * @param arg_type The target type for the argument + * @param mode The CLI mode + * @param arg The string representation of the argument + * @param arg_type The target type for the argument * @return The argument as a long integer */ static unsigned long convert_arg(int mode, char *arg, int arg_type) { -unsigned long rval; -SERVICE *service; + unsigned long rval; + SERVICE *service; - switch (arg_type) - { - case ARG_TYPE_ADDRESS: - return (unsigned long)strtol(arg, NULL, 0); - case ARG_TYPE_STRING: - return (unsigned long)arg; - case ARG_TYPE_SERVICE: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - rval = (unsigned long)service_find(arg); - - return rval; - case ARG_TYPE_SERVER: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - rval = (unsigned long)server_find_by_unique_name(arg); - - return rval; - case ARG_TYPE_DBUSERS: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - { - service = service_find(arg); - if (service) - return (unsigned long)(service->users); - else - return 0; - } - return rval; - case ARG_TYPE_DCB: - rval = (unsigned long)strtol(arg, NULL, 0); - if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0) - rval = 0; - return rval; - case ARG_TYPE_SESSION: - rval = (unsigned long)strtol(arg, NULL, 0); - if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0) - rval = 0; - - return rval; - case ARG_TYPE_MONITOR: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - rval = (unsigned long)monitor_find(arg); - - return rval; - case ARG_TYPE_FILTER: - if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) - rval = (unsigned long)filter_find(arg); - - return rval; - case ARG_TYPE_NUMERIC: - { - int i; - for (i = 0; arg[i]; i++) - { - if (arg[i] < '0' || arg[i] > '9') - return 0; - } - return atoi(arg); - } - } - return 0; + switch (arg_type) + { + case ARG_TYPE_ADDRESS: + return (unsigned long)strtol(arg, NULL, 0); + case ARG_TYPE_STRING: + return (unsigned long)arg; + case ARG_TYPE_SERVICE: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)service_find(arg); + } + return rval; + case ARG_TYPE_SERVER: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)server_find_by_unique_name(arg); + } + return rval; + case ARG_TYPE_DBUSERS: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + service = service_find(arg); + if (service) + { + return (unsigned long)(service->users); + } + else + { + return 0; + } + } + return rval; + case ARG_TYPE_DCB: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0) + { + rval = 0; + } + return rval; + case ARG_TYPE_SESSION: + rval = (unsigned long)strtol(arg, NULL, 0); + if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0) + { + rval = 0; + } + return rval; + case ARG_TYPE_MONITOR: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)monitor_find(arg); + } + return rval; + case ARG_TYPE_FILTER: + if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0) + { + rval = (unsigned long)filter_find(arg); + } + return rval; + case ARG_TYPE_NUMERIC: + { + int i; + for (i = 0; arg[i]; i++) + { + if (arg[i] < '0' || arg[i] > '9') + { + return 0; + } + } + return atoi(arg); + } + } + return 0; } /** @@ -761,408 +859,419 @@ SERVICE *service; * assumed to the numeric values and will be converted before being passed * to the handler function for the command. * - * @param cli The CLI_SESSION - * @return Returns 0 if the interpreter should exit + * @param cli The CLI_SESSION + * @return Returns 0 if the interpreter should exit */ int execute_cmd(CLI_SESSION *cli) { -DCB *dcb = cli->session->client; -int argc, i, j, found = 0; -char *args[MAXARGS + 1]; -unsigned long arg1, arg2, arg3; -int in_quotes = 0, escape_next = 0; -char *ptr, *lptr; -bool in_space = false; -int nskip = 0; + DCB *dcb = cli->session->client; + int argc, i, j, found = 0; + char *args[MAXARGS + 1]; + unsigned long arg1, arg2, arg3; + int in_quotes = 0, escape_next = 0; + char *ptr, *lptr; + bool in_space = false; + int nskip = 0; - args[0] = cli->cmdbuf; - ptr = args[0]; - lptr = ptr; - i = 0; - /* - * Break the command line into a number of words. Whitespace is used - * to delimit words and may be escaped by use of the \ character or - * the use of double quotes. - * The array args contains the broken down words, one per index. - */ + args[0] = cli->cmdbuf; + ptr = args[0]; + lptr = ptr; + i = 0; + /* + * Break the command line into a number of words. Whitespace is used + * to delimit words and may be escaped by use of the \ character or + * the use of double quotes. + * The array args contains the broken down words, one per index. + */ - while (*ptr) - { - if (escape_next) - { - *lptr++ = *ptr++; - escape_next = 0; - } - else if (*ptr == '\\') - { - escape_next = 1; - ptr++; - } - else if (in_quotes == 0 && ((in_space = *ptr == ' ') || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')) - { + while (*ptr) + { + if (escape_next) + { + *lptr++ = *ptr++; + escape_next = 0; + } + else if (*ptr == '\\') + { + escape_next = 1; + ptr++; + } + else if (in_quotes == 0 && ((in_space = *ptr == ' ') || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')) + { - *lptr = 0; - lptr += nskip; - nskip = 0; + *lptr = 0; + lptr += nskip; + nskip = 0; - if(!in_space){ - break; - } + if (!in_space) + { + break; + } - if (args[i] == ptr) - args[i] = ptr + 1; - else - { - i++; - if (i >= MAXARGS-1) - break; - args[i] = ptr + 1; - } - ptr++; - lptr++; - } - else if (*ptr == '\"' && in_quotes == 0) - { - in_quotes = 1; - ptr++; - nskip++; - } - else if (*ptr == '\"' && in_quotes == 1) - { - in_quotes = 0; - ptr++; - nskip++; - } - else - { - *lptr++ = *ptr++; - } - } - *lptr = 0; - args[MIN(MAXARGS-1,i+1)] = NULL; + if (args[i] == ptr) + { + args[i] = ptr + 1; + } + else + { + i++; + if (i >= MAXARGS - 1) + { + break; + } + args[i] = ptr + 1; + } + ptr++; + lptr++; + } + else if (*ptr == '\"' && in_quotes == 0) + { + in_quotes = 1; + ptr++; + nskip++; + } + else if (*ptr == '\"' && in_quotes == 1) + { + in_quotes = 0; + ptr++; + nskip++; + } + else + { + *lptr++ = *ptr++; + } + } + *lptr = 0; + args[MIN(MAXARGS - 1, i + 1)] = NULL; - if (args[0] == NULL || *args[0] == 0) - return 1; - for (i = 0; args[i] && *args[i]; i++) - ; - argc = i - 2; /* The number of extra arguments to commands */ - + if (args[0] == NULL || *args[0] == 0) + { + return 1; + } + for (i = 0; args[i] && *args[i]; i++) + ; + argc = i - 2; /* The number of extra arguments to commands */ - if (!strcasecmp(args[0], "help")) - { - if (args[1] == NULL || *args[1] == 0) - { - found = 1; - dcb_printf(dcb, "Available commands:\n"); - for (i = 0; cmds[i].cmd; i++) - { - if (cmds[i].options[1].arg1 == NULL) - dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[0].arg1); - else - { - dcb_printf(dcb, " %s [", cmds[i].cmd); - for (j = 0; cmds[i].options[j].arg1; j++) - { - dcb_printf(dcb, "%s%s", cmds[i].options[j].arg1, - cmds[i].options[j+1].arg1 ? "|" : ""); - } - dcb_printf(dcb, "]\n"); - } - } - dcb_printf(dcb, "\nType help command to see details of each command.\n"); - dcb_printf(dcb, "Where commands require names as arguments and these names contain\n"); - dcb_printf(dcb, "whitespace either the \\ character may be used to escape the whitespace\n"); - dcb_printf(dcb, "or the name may be enclosed in double quotes \".\n\n"); - } - else - { - for (i = 0; cmds[i].cmd; i++) - { - if (!strcasecmp(args[1], cmds[i].cmd)) - { - found = 1; - dcb_printf(dcb, "Available options to the %s command:\n", args[1]); - for (j = 0; cmds[i].options[j].arg1; j++) - { - dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1, - cmds[i].options[j].help); - } - } - } - if (found == 0) - { - dcb_printf(dcb, "No command %s to offer help with\n", args[1]); - } - } - found = 1; - } - else if (!strcasecmp(args[0], "quit")) - { - return 0; - } - else if (argc >= 0) - { - for (i = 0; cmds[i].cmd; i++) - { - if (strcasecmp(args[0], cmds[i].cmd) == 0) - { - for (j = 0; cmds[i].options[j].arg1; j++) - { - if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0) - { - found = 1; /**< command and sub-command match */ - if (argc != cmds[i].options[j].n_args) - { - dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n", - cmds[i].cmd, cmds[i].options[j].arg1, - cmds[i].options[j].n_args); - - } - else - { - switch (cmds[i].options[j].n_args) - { - case 0: - cmds[i].options[j].fn(dcb); - break; - case 1: - arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); + if (!strcasecmp(args[0], "help")) + { + if (args[1] == NULL || *args[1] == 0) + { + found = 1; + dcb_printf(dcb, "Available commands:\n"); + for (i = 0; cmds[i].cmd; i++) + { + if (cmds[i].options[1].arg1 == NULL) + { + dcb_printf(dcb, " %s %s\n", cmds[i].cmd, cmds[i].options[0].arg1); + } + else + { + dcb_printf(dcb, " %s [", cmds[i].cmd); + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, "%s%s", cmds[i].options[j].arg1, + cmds[i].options[j + 1].arg1 ? "|" : ""); + } + dcb_printf(dcb, "]\n"); + } + } + dcb_printf(dcb, "\nType help command to see details of each command.\n"); + dcb_printf(dcb, "Where commands require names as arguments and these names contain\n"); + dcb_printf(dcb, "whitespace either the \\ character may be used to escape the whitespace\n"); + dcb_printf(dcb, "or the name may be enclosed in double quotes \".\n\n"); + } + else + { + for (i = 0; cmds[i].cmd; i++) + { + if (!strcasecmp(args[1], cmds[i].cmd)) + { + found = 1; + dcb_printf(dcb, "Available options to the %s command:\n", args[1]); + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, " %-12s %s\n", cmds[i].options[j].arg1, + cmds[i].options[j].help); + } + } + } + if (found == 0) + { + dcb_printf(dcb, "No command %s to offer help with\n", args[1]); + } + } + found = 1; + } + else if (!strcasecmp(args[0], "quit")) + { + return 0; + } + else if (argc >= 0) + { + for (i = 0; cmds[i].cmd; i++) + { + if (strcasecmp(args[0], cmds[i].cmd) == 0) + { + for (j = 0; cmds[i].options[j].arg1; j++) + { + if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0) + { + found = 1; /**< command and sub-command match */ + if (argc != cmds[i].options[j].n_args) + { + dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n", + cmds[i].cmd, cmds[i].options[j].arg1, + cmds[i].options[j].n_args); + } + else + { + switch (cmds[i].options[j].n_args) + { + case 0: + cmds[i].options[j].fn(dcb); + break; + case 1: + arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); - if (arg1) - cmds[i].options[j].fn(dcb, arg1); - else - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - break; - case 2: - arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]); - if (arg1 && arg2) - cmds[i].options[j].fn(dcb, arg1, arg2); - else if (arg1 == 0) - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - else - dcb_printf(dcb, "Invalid argument: %s\n", - args[3]); - break; - case 3: - arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]); - arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]); - arg3 = convert_arg(cli->mode, args[4],cmds[i].options[j].arg_types[2]); - if (arg1 && arg2 && arg3) - cmds[i].options[j].fn(dcb, arg1, arg2, arg3); - else if (arg1 == 0) - dcb_printf(dcb, "Invalid argument: %s\n", - args[2]); - else if (arg2 == 0) - dcb_printf(dcb, "Invalid argument: %s\n", - args[3]); - else if (arg3 == 0) - dcb_printf(dcb, "Invalid argument: %s\n", - args[4]); - } - } - } - } - if (!found) - { - dcb_printf(dcb, - "Unknown or missing option for the %s command. Valid sub-commands are:\n", - cmds[i].cmd); - for (j = 0; cmds[i].options[j].arg1; j++) - { - dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1, - cmds[i].options[j].help); - } - found = 1; - } - } - } - } - else if (argc == -1) - { - dcb_printf(dcb, - "Commands must consist of at least two words. Type help for a list of commands\n"); - found = 1; - } - if (!found) - dcb_printf(dcb, - "Command '%s' not known, type help for a list of available commands\n", args[0]); - memset(cli->cmdbuf, 0, cmdbuflen); + if (arg1) + { + cmds[i].options[j].fn(dcb, arg1); + } + else + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[2]); + } + break; + case 2: + arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); + arg2 = convert_arg(cli->mode, args[3], cmds[i].options[j].arg_types[1]); + if (arg1 && arg2) + { + cmds[i].options[j].fn(dcb, arg1, arg2); + } + else if (arg1 == 0) + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[2]); + } + else + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[3]); + } + break; + case 3: + arg1 = convert_arg(cli->mode, args[2], cmds[i].options[j].arg_types[0]); + arg2 = convert_arg(cli->mode, args[3], cmds[i].options[j].arg_types[1]); + arg3 = convert_arg(cli->mode, args[4], cmds[i].options[j].arg_types[2]); + if (arg1 && arg2 && arg3) + { + cmds[i].options[j].fn(dcb, arg1, arg2, arg3); + } + else if (arg1 == 0) + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[2]); + } + else if (arg2 == 0) + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[3]); + } + else if (arg3 == 0) + { + dcb_printf(dcb, "Invalid argument: %s\n", + args[4]); + } + } + } + } + } + if (!found) + { + dcb_printf(dcb, + "Unknown or missing option for the %s command. Valid sub-commands are:\n", + cmds[i].cmd); + for (j = 0; cmds[i].options[j].arg1; j++) + { + dcb_printf(dcb, " %-10s %s\n", cmds[i].options[j].arg1, + cmds[i].options[j].help); + } + found = 1; + } + } + } + } + else if (argc == -1) + { + dcb_printf(dcb, + "Commands must consist of at least two words. Type help for a list of commands\n"); + found = 1; + } + if (!found) + { + dcb_printf(dcb, + "Command '%s' not known, type help for a list of available commands\n", args[0]); + } - return 1; + memset(cli->cmdbuf, 0, cmdbuflen); + + return 1; } /** * Debug command to stop a service * - * @param dcb The DCB to print any output to - * @param service The service to shutdown + * @param dcb The DCB to print any output to + * @param service The service to shutdown */ static void shutdown_service(DCB *dcb, SERVICE *service) { - serviceStop(service); + serviceStop(service); } /** * Debug command to restart a stopped service * - * @param dcb The DCB to print any output to - * @param service The service to restart + * @param dcb The DCB to print any output to + * @param service The service to restart */ static void 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; + serviceRestart(service); } /** * Set the status bit of a server * - * @param dcb DCB to send output to - * @param server The server to set the status of - * @param bit String representation of the status bit + * @param dcb DCB to send output to + * @param server The server to set the status of + * @param bit String representation of the status bit */ static void set_server(DCB *dcb, SERVER *server, char *bit) { -unsigned int bitvalue; + unsigned int bitvalue; - if ((bitvalue = server_map_status(bit)) != 0) - server_set_status(server, bitvalue); - else - dcb_printf(dcb, "Unknown status bit %s\n", bit); + if ((bitvalue = server_map_status(bit)) != 0) + { + server_set_status(server, bitvalue); + } + else + { + dcb_printf(dcb, "Unknown status bit %s\n", bit); + } } /** * Clear the status bit of a server * - * @param dcb DCB to send output to - * @param server The server to set the status of - * @param bit String representation of the status bit + * @param dcb DCB to send output to + * @param server The server to set the status of + * @param bit String representation of the status bit */ static void clear_server(DCB *dcb, SERVER *server, char *bit) { -unsigned int bitvalue; + unsigned int bitvalue; - if ((bitvalue = server_map_status(bit)) != 0) - server_clear_status(server, bitvalue); - else - dcb_printf(dcb, "Unknown status bit %s\n", bit); + if ((bitvalue = server_map_status(bit)) != 0) + { + server_clear_status(server, bitvalue); + } + else + { + dcb_printf(dcb, "Unknown status bit %s\n", bit); + } } /** * Reload the authenticaton data from the backend database of a service. * - * @param dcb DCB to send output - * @param service The service to update + * @param dcb DCB to send output + * @param service The service to update */ static void reload_dbusers(DCB *dcb, SERVICE *service) { - dcb_printf(dcb, "Loaded %d database users for service %s.\n", - reload_mysql_users(service), service->name); + dcb_printf(dcb, "Loaded %d database users for service %s.\n", + reload_mysql_users(service), service->name); } /** * Relaod the configuration data from the config file * - * @param dcb DCB to use to send output + * @param dcb DCB to use to send output */ static void reload_config(DCB *dcb) { - dcb_printf(dcb, "Reloading configuration from file.\n"); - config_reload(); + dcb_printf(dcb, "Reloading configuration from file.\n"); + config_reload(); } /** * Add a new maxscale admin user * - * @param dcb The DCB for messages - * @param user The user name - * @param passwd The Password of the user + * @param dcb The DCB for messages + * @param user The user name + * @param passwd The Password of the user */ static void telnetdAddUser(DCB *dcb, char *user, char *passwd) { -char *err; + char *err; - if (admin_search_user(user)) - { - dcb_printf(dcb, "User %s already exists.\n", user); - return; - } - if ((err = admin_add_user(user, passwd)) == NULL) - dcb_printf(dcb, "User %s has been successfully added.\n", user); - else - dcb_printf(dcb, "Failed to add new user. %s\n", err); + if (admin_search_user(user)) + { + dcb_printf(dcb, "User %s already exists.\n", user); + return; + } + + if ((err = admin_add_user(user, passwd)) == NULL) + { + dcb_printf(dcb, "User %s has been successfully added.\n", user); + } + else + { + dcb_printf(dcb, "Failed to add new user. %s\n", err); + } } /** * Remove a maxscale admin user * - * @param dcb The DCB for messages - * @param user The user name - * @param passwd The Password of the user + * @param dcb The DCB for messages + * @param user The user name + * @param passwd The Password of the user */ static void telnetdRemoveUser( - DCB* dcb, - char* user, - char* passwd) + DCB* dcb, + char* user, + char* passwd) { - char* err; + char* err; - if (!admin_search_user(user)) - { - dcb_printf(dcb, "User %s doesn't exist.\n", user); - return; - } - - if ((err = admin_remove_user(user, passwd)) == NULL) - { - dcb_printf(dcb, "User %s has been successfully removed.\n", user); - } - else - { - dcb_printf(dcb, "Failed to remove user %s. %s\n", user, err); - } + if (!admin_search_user(user)) + { + dcb_printf(dcb, "User %s doesn't exist.\n", user); + return; + } + + if ((err = admin_remove_user(user, passwd)) == NULL) + { + dcb_printf(dcb, "User %s has been successfully removed.\n", user); + } + else + { + dcb_printf(dcb, "Failed to remove user %s. %s\n", user, err); + } } @@ -1170,44 +1279,44 @@ static void telnetdRemoveUser( /** * Print the adminsitration users * - * @param dcb The DCB to print the user data to + * @param dcb The DCB to print the user data to */ static void telnetdShowUsers(DCB *dcb) { - dcb_printf(dcb, "Administration interface users:\n"); - dcb_PrintAdminUsers(dcb); + dcb_printf(dcb, "Administration interface users:\n"); + dcb_PrintAdminUsers(dcb); } /** * Command to shutdown a running monitor * - * @param dcb The DCB to use to print messages - * @param monitor The monitor to shutdown + * @param dcb The DCB to use to print messages + * @param monitor The monitor to shutdown */ static void shutdown_monitor(DCB *dcb, MONITOR *monitor) { - monitorStop(monitor); + monitorStop(monitor); } /** * Command to restart a stopped monitor * - * @param dcb The DCB to use to print messages - * @param monitor The monitor to restart + * @param dcb The DCB to use to print messages + * @param monitor The monitor to restart */ static void restart_monitor(DCB *dcb, MONITOR *monitor) { - monitorStart(monitor, NULL); + monitorStart(monitor, NULL); } /** * Enable replication heartbeat for a monitor * - * @param dcb Connection to user interface - * @param monitor The monitor + * @param dcb Connection to user interface + * @param monitor The monitor */ static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) @@ -1219,14 +1328,14 @@ enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) param.value = (char*)value; param.next = NULL; monitorStop(monitor); - monitorStart(monitor,¶m); + monitorStart(monitor, ¶m); } /** * Disable replication heartbeat for a monitor * - * @param dcb Connection to user interface - * @param monitor The monitor + * @param dcb Connection to user interface + * @param monitor The monitor */ static void disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) @@ -1238,31 +1347,31 @@ disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) param.value = (char*)value; param.next = NULL; monitorStop(monitor); - monitorStart(monitor,¶m); + monitorStart(monitor, ¶m); } /** * Enable root access to a service * - * @param dcb Connection to user interface - * @param service The service + * @param dcb Connection to user interface + * @param service The service */ static void enable_service_root(DCB *dcb, SERVICE *service) { - serviceEnableRootUser(service, 1); + serviceEnableRootUser(service, 1); } /** * Disable root access to a service * - * @param dcb Connection to user interface - * @param service The service + * @param dcb Connection to user interface + * @param service The service */ static void disable_service_root(DCB *dcb, SERVICE *service) { - serviceEnableRootUser(service, 0); + serviceEnableRootUser(service, 0); } /** @@ -1273,39 +1382,48 @@ disable_service_root(DCB *dcb, SERVICE *service) */ static void enable_sess_log_action(DCB *dcb, char *arg1, char *arg2) { - logfile_id_t type; - size_t id = 0; - int max_len = strlen("message"); - SESSION* session = get_all_sessions(); + logfile_id_t type; + size_t id = 0; + int max_len = strlen("message"); + SESSION* session = get_all_sessions(); - ss_dassert(arg1 != NULL && arg2 != NULL && session != NULL); + ss_dassert(arg1 != NULL && arg2 != NULL && session != NULL); - if (strncmp(arg1, "debug", max_len) == 0) { - type = LOGFILE_DEBUG; - } else if (strncmp(arg1, "trace", max_len) == 0) { - type = LOGFILE_TRACE; - } else if (strncmp(arg1, "error", max_len) == 0) { - type = LOGFILE_ERROR; - } else if (strncmp(arg1, "message", max_len) == 0) { - type = LOGFILE_MESSAGE; - } else { - dcb_printf(dcb, "%s is not supported for enable log\n", arg1); - return ; - } - - id = (size_t)strtol(arg2,0,0); + if (strncmp(arg1, "debug", max_len) == 0) + { + type = LOGFILE_DEBUG; + } + else if (strncmp(arg1, "trace", max_len) == 0) + { + type = LOGFILE_TRACE; + } + else if (strncmp(arg1, "error", max_len) == 0) + { + type = LOGFILE_ERROR; + } + else if (strncmp(arg1, "message", max_len) == 0) + { + type = LOGFILE_MESSAGE; + } + else + { + dcb_printf(dcb, "%s is not supported for enable log\n", arg1); + return ; + } - while(session) - { - if(session->ses_id == id) - { - session_enable_log(session,type); - return; - } - session = session->next; - } + id = (size_t)strtol(arg2, 0, 0); - dcb_printf(dcb, "Session not found: %s\n", arg2); + while(session) + { + if (session->ses_id == id) + { + session_enable_log(session, type); + return; + } + session = session->next; + } + + dcb_printf(dcb, "Session not found: %s\n", arg2); } /** @@ -1316,111 +1434,223 @@ static void enable_sess_log_action(DCB *dcb, char *arg1, char *arg2) */ static void disable_sess_log_action(DCB *dcb, char *arg1, char *arg2) { - logfile_id_t type; - int id = 0; - int max_len = strlen("message"); - SESSION* session = get_all_sessions(); + logfile_id_t type; + int id = 0; + int max_len = strlen("message"); + SESSION* session = get_all_sessions(); - ss_dassert(arg1 != NULL && arg2 != NULL && session != NULL); + ss_dassert(arg1 != NULL && arg2 != NULL && session != NULL); - if (strncmp(arg1, "debug", max_len) == 0) { - type = LOGFILE_DEBUG; - } else if (strncmp(arg1, "trace", max_len) == 0) { - type = LOGFILE_TRACE; - } else if (strncmp(arg1, "error", max_len) == 0) { - type = LOGFILE_ERROR; - } else if (strncmp(arg1, "message", max_len) == 0) { - type = LOGFILE_MESSAGE; - } else { - dcb_printf(dcb, "%s is not supported for disable log\n", arg1); - return ; + if (strncmp(arg1, "debug", max_len) == 0) + { + type = LOGFILE_DEBUG; + } + else if (strncmp(arg1, "trace", max_len) == 0) + { + type = LOGFILE_TRACE; + } + else if (strncmp(arg1, "error", max_len) == 0) + { + type = LOGFILE_ERROR; + } + else if (strncmp(arg1, "message", max_len) == 0) + { + type = LOGFILE_MESSAGE; + } + else + { + dcb_printf(dcb, "%s is not supported for disable log\n", arg1); + return ; + } + + id = (size_t)strtol(arg2, 0, 0); + + while(session) + { + if (session->ses_id == id) + { + session_disable_log(session, type); + return; } - - id = (size_t)strtol(arg2,0,0); + session = session->next; + } - while(session) - { - if(session->ses_id == id) - { - session_disable_log(session,type); - return; - } - session = session->next; - } + dcb_printf(dcb, "Session not found: %s\n", arg2); +} - dcb_printf(dcb, "Session not found: %s\n", arg2); +struct log_action_entry +{ + const char* name; + int priority; + const char* replacement; +}; + +static bool get_log_action(const char* name, struct log_action_entry* entryp) +{ + static const struct log_action_entry entries[] = + { + { "debug", LOG_DEBUG, "debug" }, + { "trace", LOG_INFO, "info" }, + { "message", LOG_NOTICE, "notice" }, + { "error", LOG_ERR, "err" } + }; + const int n_entries = sizeof(entries) / sizeof(entries[0]); + + bool found = false; + int i = 0; + + while (!found && (i < n_entries)) + { + if (strcmp(name, entries[i].name) == 0) + { + *entryp = entries[i]; + found = true; + } + + ++i; + } + + return found; } /** * The log enable action */ +static void enable_log_action(DCB *dcb, char *arg1) +{ + struct log_action_entry entry; -static void enable_log_action(DCB *dcb, char *arg1) { - logfile_id_t type; - int max_len = strlen("message"); + if (get_log_action(arg1, &entry)) + { + mxs_log_set_priority_enabled(entry.priority, true); - if (strncmp(arg1, "debug", max_len) == 0) { - type = LOGFILE_DEBUG; - } else if (strncmp(arg1, "trace", max_len) == 0) { - type = LOGFILE_TRACE; - } else if (strncmp(arg1, "error", max_len) == 0) { - type = LOGFILE_ERROR; - } else if (strncmp(arg1, "message", max_len) == 0) { - type = LOGFILE_MESSAGE; - } else { - dcb_printf(dcb, "%s is not supported for enable log\n", arg1); - return ; - } - - skygw_log_enable(type); + dcb_printf(dcb, + "'enable log %s' is accepted but deprecated, use 'enable log-priority %s' instead.\n", + arg1, entry.replacement); + } + else + { + dcb_printf(dcb, "'%s' is not supported for enable log\n", arg1); + } } /** * The log disable action */ +static void disable_log_action(DCB *dcb, char *arg1) +{ + struct log_action_entry entry; -static void disable_log_action(DCB *dcb, char *arg1) { - logfile_id_t type; - int max_len = strlen("message"); + if (get_log_action(arg1, &entry)) + { + mxs_log_set_priority_enabled(entry.priority, false); - if (strncmp(arg1, "debug", max_len) == 0) { - type = LOGFILE_DEBUG; - } else if (strncmp(arg1, "trace", max_len) == 0) { - type = LOGFILE_TRACE; - } else if (strncmp(arg1, "error", max_len) == 0) { - type = LOGFILE_ERROR; - } else if (strncmp(arg1, "message", max_len) == 0) { - type = LOGFILE_MESSAGE; - } else { - dcb_printf(dcb, "%s is not supported for disable log\n", arg1); - return ; - } + dcb_printf(dcb, + "'disable log %s' is accepted but deprecated, use 'enable log-priority %s' instead.\n", + arg1, entry.replacement); + } + else + { + dcb_printf(dcb, "'%s' is not supported for 'disable log'\n", arg1); + } +} - skygw_log_disable(type); +struct log_priority_entry +{ + const char* name; + int priority; +}; + +static int compare_log_priority_entries(const void* l, const void* r) +{ + const struct log_priority_entry* l_entry = (const struct log_priority_entry*) l; + const struct log_priority_entry* r_entry = (const struct log_priority_entry*) r; + + return strcmp(l_entry->name, r_entry->name); +} + +static int string_to_priority(const char* name) +{ + static const struct log_priority_entry LOG_PRIORITY_ENTRIES[] = + { + // NOTE: If you make changes to this array, ensure that it remains alphabetically ordered. + { "debug", LOG_DEBUG }, + { "err", LOG_ERR }, + { "info", LOG_INFO }, + { "notice", LOG_NOTICE }, + { "warning", LOG_WARNING }, + }; + + const size_t N_LOG_PRIORITY_ENTRIES = sizeof(LOG_PRIORITY_ENTRIES) / sizeof(LOG_PRIORITY_ENTRIES[0]); + + struct log_priority_entry key = { name, -1 }; + struct log_priority_entry* result = bsearch(&key, + LOG_PRIORITY_ENTRIES, + N_LOG_PRIORITY_ENTRIES, + sizeof(struct log_priority_entry), + compare_log_priority_entries); + + return result ? result->priority : -1; +} + +/** + * The log-priority enable action + */ + +static void enable_log_priority(DCB *dcb, char *arg1) +{ + int priority = string_to_priority(arg1); + + if (priority != -1) + { + mxs_log_set_priority_enabled(priority, true); + } + else + { + dcb_printf(dcb, "'%s' is not a supported log priority\n", arg1); + } +} + +/** + * The log-priority disable action + */ + +static void disable_log_priority(DCB *dcb, char *arg1) +{ + int priority = string_to_priority(arg1); + + if (priority != -1) + { + mxs_log_set_priority_enabled(priority, false); + } + else + { + dcb_printf(dcb, "'%s' is not a supported log priority\n", arg1); + } } /** * Set the duration of the sleep passed to the poll wait * - * @param dcb DCB for output - * @param sleeptime Sleep time in milliseconds + * @param dcb DCB for output + * @param sleeptime Sleep time in milliseconds */ static void set_pollsleep(DCB *dcb, int sleeptime) { - poll_set_maxwait(sleeptime); + poll_set_maxwait(sleeptime); } /** * Set the number of non-blockign spins to make * - * @param dcb DCB for output - * @param nb Number of spins + * @param dcb DCB for output + * @param nb Number of spins */ static void set_nbpoll(DCB *dcb, int nb) { - poll_set_nonblocking_polls(nb); + poll_set_nonblocking_polls(nb); } /** @@ -1431,8 +1661,8 @@ set_nbpoll(DCB *dcb, int nb) static void enable_feedback_action(void) { - config_enable_feedback_task(); - return; + config_enable_feedback_task(); + return; } /** @@ -1442,52 +1672,52 @@ enable_feedback_action(void) static void disable_feedback_action(void) { - config_disable_feedback_task(); - return; + config_disable_feedback_task(); + return; } #if defined(FAKE_CODE) static void fail_backendfd(void) -{ - fail_next_backend_fd = true; +{ + fail_next_backend_fd = true; } static void fail_clientfd(void) -{ - fail_next_client_fd = true; +{ + fail_next_client_fd = true; } static void fail_accept( - DCB* dcb, - char* arg1, - char* arg2) + DCB* dcb, + char* arg1, + char* arg2) { - int failcount = MIN(atoi(arg2), 100); - fail_accept_errno = atoi(arg1); - char errbuf[STRERROR_BUFLEN]; + int failcount = MIN(atoi(arg2), 100); + fail_accept_errno = atoi(arg1); + char errbuf[STRERROR_BUFLEN]; - switch(fail_accept_errno) { - case EAGAIN: -// case EWOULDBLOCK: - case EBADF: - case EINTR: - case EINVAL: - case EMFILE: - case ENFILE: - case ENOTSOCK: - case EOPNOTSUPP: - case ENOBUFS: - case ENOMEM: - case EPROTO: - fail_next_accept = failcount; + switch(fail_accept_errno) { + case EAGAIN: +// case EWOULDBLOCK: + case EBADF: + case EINTR: + case EINVAL: + case EMFILE: + case ENFILE: + case ENOTSOCK: + case EOPNOTSUPP: + case ENOBUFS: + case ENOMEM: + case EPROTO: + fail_next_accept = failcount; break; - default: - dcb_printf(dcb, - "[%d, %s] is not valid errno for accept.\n", - fail_accept_errno, - strerror_r(fail_accept_errno, errbuf, sizeof(errbuf))); - return ; - } + default: + dcb_printf(dcb, + "[%d, %s] is not valid errno for accept.\n", + fail_accept_errno, + strerror_r(fail_accept_errno, errbuf, sizeof(errbuf))); + return ; + } } #endif /* FAKE_CODE */ diff --git a/server/modules/routing/maxinfo/maxinfo.c b/server/modules/routing/maxinfo/maxinfo.c index 386a4c850..bea41c218 100644 --- a/server/modules/routing/maxinfo/maxinfo.c +++ b/server/modules/routing/maxinfo/maxinfo.c @@ -26,6 +26,7 @@ * Date Who Description * 16/02/15 Mark Riddoch Initial implementation * 27/02/15 Massimiliano Pinto Added maxinfo_add_mysql_user + * 09/09/2015 Martin Brampton Modify error handler * * @endverbatim */ @@ -63,11 +64,6 @@ MODULE_INFO info = { "The MaxScale Information Schema" }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - extern char *create_hex_sha1_sha1_passwd(char *passwd); static char *version_str = "V1.0.0"; @@ -86,7 +82,7 @@ static void closeSession(ROUTER *instance, void *router_session); static void freeSession(ROUTER *instance, void *router_session); static int execute(ROUTER *instance, void *router_session, GWBUF *queue); static void diagnostics(ROUTER *instance, DCB *dcb); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); static void handleError( ROUTER *instance, void *router_session, @@ -291,7 +287,8 @@ static void freeSession( * @param router_session The router session * @param message The error message to reply * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true iff router can continue * */ static void handleError( @@ -307,13 +304,6 @@ static void handleError( SESSION *session = backend_dcb->session; session_state_t sesstate; - /** Reset error handle flag from a given DCB */ - if (action == ERRACT_RESET) - { - backend_dcb->dcb_errhandle_called = false; - return; - } - /** Don't handle same error twice on same DCB */ if (backend_dcb->dcb_errhandle_called) { @@ -341,6 +331,7 @@ static void handleError( } /** false because connection is not available anymore */ + dcb_close(backend_dcb); *succp = false; } @@ -424,10 +415,8 @@ diagnostics(ROUTER *instance, DCB *dcb) * * Not used for the maxinfo router */ -static uint8_t -getCapabilities( - ROUTER* inst, - void* router_session) +static int +getCapabilities() { return 0; } @@ -631,7 +620,7 @@ MAXINFO_TREE *tree; PARSE_ERROR err; LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, - "maxinfo: SQL statement: '%s' for 0x%x.", + "maxinfo: SQL statement: '%s' for 0x%p.", sql, session->dcb))); if (strcmp(sql, "select @@version_comment limit 1") == 0) { @@ -791,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_error.c b/server/modules/routing/maxinfo/maxinfo_error.c index dd5a8f233..afb4a6270 100644 --- a/server/modules/routing/maxinfo/maxinfo_error.c +++ b/server/modules/routing/maxinfo/maxinfo_error.c @@ -45,11 +45,6 @@ #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * Process a parse error and send error report to client * diff --git a/server/modules/routing/maxinfo/maxinfo_exec.c b/server/modules/routing/maxinfo/maxinfo_exec.c index 6257228f6..8672c29e6 100644 --- a/server/modules/routing/maxinfo/maxinfo_exec.c +++ b/server/modules/routing/maxinfo/maxinfo_exec.c @@ -49,16 +49,17 @@ #include #include -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - static void exec_show(DCB *dcb, MAXINFO_TREE *tree); 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 * @@ -76,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: @@ -275,7 +293,449 @@ char errmsg[120]; tree->value[80] = 0; sprintf(errmsg, "Unsupported show command '%s'", tree->value); maxinfo_send_error(dcb, 0, errmsg); - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg))); + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s", 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, "%s", 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, "%s", 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, "%s", 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, "%s", 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, "%s", errmsg); } /** @@ -768,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/server/modules/routing/readconnroute.c b/server/modules/routing/readconnroute.c index ccb071e1a..545cb5423 100644 --- a/server/modules/routing/readconnroute.c +++ b/server/modules/routing/readconnroute.c @@ -67,13 +67,17 @@ * 06/03/2014 Massimiliano Pinto Server connection counter is now updated in closeSession * 24/06/2014 Massimiliano Pinto New rules for selecting the Master server * 27/06/2014 Mark Riddoch Addition of server weighting - * 11/06/2015 Martin Brampton Remove decrement n_current (moved to dcb.c) + * 11/06/2015 Martin Brampton Remove decrement n_current (moved to dcb.c) + * 09/09/2015 Martin Brampton Modify error handler + * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB + * 09/11/2015 Martin Brampton Modified routeQuery - must free "queue" regardless of outcome * * @endverbatim */ #include #include #include +#include #include #include #include @@ -92,11 +96,6 @@ #include "modutil.h" -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - MODULE_INFO info = { MODULE_API_ROUTER, MODULE_GA, @@ -122,10 +121,10 @@ static void handleError( ROUTER *instance, void *router_session, GWBUF *errbuf, - DCB *backend_dcb, + DCB *problem_dcb, error_action_t action, bool *succp); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); /** The module object definition */ @@ -728,6 +727,7 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue) "server.%s", mysql_command,rses_is_closed ? " Session is closed." : ""))); rc = 0; + while((queue = GWBUF_CONSUME_ALL(queue)) != NULL); goto return_rc; } @@ -834,63 +834,68 @@ clientReply( /** * Error Handler routine * - * The routine will handle errors that occurred in backend writes. + * The routine will handle errors that occurred in writes. * * @param instance The router instance * @param router_session The router session * @param message The error message to reply - * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION + * @param problem_dcb The DCB related to the error + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true if router can continue * */ static void handleError( - ROUTER *instance, - void *router_session, - GWBUF *errbuf, - DCB *backend_dcb, - error_action_t action, - bool *succp) + ROUTER *instance, + void *router_session, + GWBUF *errbuf, + DCB *problem_dcb, + error_action_t action, + bool *succp) { - DCB *client_dcb; - SESSION *session = backend_dcb->session; - session_state_t sesstate; + DCB *client_dcb; + SESSION *session = problem_dcb->session; + session_state_t sesstate; + ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session; - /** Reset error handle flag from a given DCB */ - if (action == ERRACT_RESET) - { - backend_dcb->dcb_errhandle_called = false; - return; - } + /** Don't handle same error twice on same DCB */ + if (problem_dcb->dcb_errhandle_called) + { + /** we optimistically assume that previous call succeed */ + *succp = true; + return; + } + else + { + problem_dcb->dcb_errhandle_called = true; + } + spinlock_acquire(&session->ses_lock); + sesstate = session->state; + client_dcb = session->client; - /** Don't handle same error twice on same DCB */ - if (backend_dcb->dcb_errhandle_called) - { - /** we optimistically assume that previous call succeed */ - *succp = true; - return; - } - else - { - backend_dcb->dcb_errhandle_called = true; - } - spinlock_acquire(&session->ses_lock); - sesstate = session->state; - client_dcb = session->client; - - if (sesstate == SESSION_STATE_ROUTER_READY) - { - CHK_DCB(client_dcb); - spinlock_release(&session->ses_lock); - client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); - } - else - { - spinlock_release(&session->ses_lock); - } - - /** false because connection is not available anymore */ - *succp = false; + if (sesstate == SESSION_STATE_ROUTER_READY) + { + CHK_DCB(client_dcb); + spinlock_release(&session->ses_lock); + client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); + } + else + { + spinlock_release(&session->ses_lock); + } + + if (dcb_isclient(problem_dcb)) + { + dcb_close(problem_dcb); + } + else if (router_cli_ses && problem_dcb == router_cli_ses->backend_dcb) + { + router_cli_ses->backend_dcb = NULL; + dcb_close(problem_dcb); + } + + /** false because connection is not available anymore */ + *succp = false; } /** to be inline'd */ @@ -952,11 +957,9 @@ static void rses_end_locked_router_action( } -static uint8_t getCapabilities( - ROUTER* inst, - void* router_session) +static int getCapabilities() { - return 0; + return RCAP_TYPE_PACKET_INPUT; } /******************************** @@ -998,6 +1001,14 @@ static int handle_state_switch(DCB* dcb,DCB_REASON reason, void * routersession) SERVICE* service = session->service; ROUTER* router = (ROUTER *)service->router; + if (NULL == dcb->session->router_session && DCB_REASON_ERROR != reason) + { + /* + * We cannot handle a DCB that does not have a router session, + * except in the case where error processing is invoked. + */ + return 0; + } switch(reason) { case DCB_REASON_CLOSE: diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 15ad5ca42..9d44a095b 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -47,10 +47,6 @@ MODULE_INFO info = { #define RWSPLIT_TRACE_MSG_LEN 1000 -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; /** * @file readwritesplit.c The entry points for the read/write query splitting * router module. @@ -69,6 +65,9 @@ extern __thread log_info_t tls_log_info; * 18/07/2013 Massimiliano Pinto routeQuery now handles COM_QUIT * as QUERY_TYPE_SESSION_WRITE * 17/07/2014 Massimiliano Pinto Server connection counter is updated in closeSession + * + * 09/09/2015 Martin Brampton Modify error handler + * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB * * @endverbatim */ @@ -106,6 +105,7 @@ static DCB* rses_get_client_dcb(ROUTER_CLIENT_SES* rses); static route_target_t get_route_target ( skygw_query_type_t qtype, bool trx_active, + bool load_active, target_t use_sql_variables_in, HINT* hint); @@ -130,7 +130,7 @@ static bool route_single_stmt( GWBUF* querybuf); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities(); #if defined(NOT_USED) static bool router_option_configured( @@ -912,7 +912,6 @@ static void* newSession( client_rses->rses_master_ref = master_ref; /* assert with master_host */ ss_dassert(master_ref && (master_ref->bref_backend->backend_server && SERVER_MASTER)); - client_rses->rses_capabilities = RCAP_TYPE_STMT_INPUT; client_rses->rses_backend_ref = backend_ref; client_rses->rses_nbackends = router_nservers; /*< # of backend servers */ @@ -990,7 +989,7 @@ static void closeSession( int i; /** * This sets router closed. Nobody is allowed to use router - * whithout checking this first. + * without checking this first. */ router_cli_ses->rses_closed = true; @@ -1370,6 +1369,7 @@ static backend_ref_t* check_candidate_bref( static route_target_t get_route_target ( skygw_query_type_t qtype, bool trx_active, + bool load_active, target_t use_sql_variables_in, HINT* hint) { @@ -1377,7 +1377,7 @@ static route_target_t get_route_target ( /** * These queries are not affected by hints */ - if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || + if (!load_active && (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) || QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) || /** Configured to allow writing variables to all nodes */ @@ -1385,7 +1385,7 @@ static route_target_t get_route_target ( QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE)) || /** enable or disable autocommit are always routed to all */ QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)) + QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT))) { /** * This is problematic query because it would be routed to all @@ -1423,12 +1423,14 @@ static route_target_t get_route_target ( /** * Hints may affect on routing of the following queries */ - else if (!trx_active && + else if (!trx_active && !load_active && (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || /*< any SELECT */ QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_TABLES) || /*< 'SHOW TABLES' */ QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ)|| /*< read user var */ QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) || /*< read sys var */ QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || /*< prepared stmt exec */ + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) || QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ))) /*< read global sys var */ { /** First set expected targets before evaluating hints */ @@ -1446,6 +1448,8 @@ static route_target_t get_route_target ( if (QUERY_IS_TYPE(qtype, QUERY_TYPE_MASTER_READ) || QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) || /** Configured not to allow reading variables from slaves */ (use_sql_variables_in == TYPE_MASTER && (QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) || @@ -2143,8 +2147,13 @@ static bool route_single_stmt( if(packet_len == 0) { + /** Empty packet signals end of LOAD DATA LOCAL INFILE, send it to master*/ route_target = TARGET_MASTER; packet_type = MYSQL_COM_UNDEFINED; + rses->rses_load_active = false; + route_target = TARGET_MASTER; + skygw_log_write_flush(LT, "> LOAD DATA LOCAL INFILE finished: " + "%lu bytes sent.", rses->rses_load_data_sent + gwbuf_length(querybuf)); } else { @@ -2194,17 +2203,36 @@ static bool route_single_stmt( break; } /**< switch by packet type */ - /** - * Check if the query has anything to do with temporary tables. - */ if (!rses_begin_locked_router_action(rses)) { succp = false; goto retblock; - } + } + /** + * Check if the query has anything to do with temporary tables. + */ qtype = is_read_tmp_table(rses, querybuf, qtype); check_create_tmp_table(rses, querybuf, qtype); check_drop_tmp_table(rses, querybuf,qtype); + + /** + * Check if this is a LOAD DATA LOCAL INFILE query. If so, send all queries + * to the master until the last, empty packet arrives. + */ + if (!rses->rses_load_active) + { + skygw_query_op_t queryop = query_classifier_get_operation(querybuf); + if (queryop == QUERY_OP_LOAD) + { + rses->rses_load_active = true; + rses->rses_load_data_sent = 0; + } + } + else + { + rses->rses_load_data_sent += gwbuf_length(querybuf); + } + rses_end_locked_router_action(rses); /** * If autocommit is disabled or transaction is explicitly started @@ -2245,28 +2273,35 @@ static bool route_single_stmt( if (LOG_IS_ENABLED(LOGFILE_TRACE)) { - uint8_t* packet = GWBUF_DATA(querybuf); - unsigned char ptype = packet[4]; - size_t len = MIN(GWBUF_LENGTH(querybuf), - MYSQL_GET_PACKET_LEN((unsigned char *)querybuf->start)-1); - char* data = (char*)&packet[5]; - char* contentstr = strndup(data, MIN(len, RWSPLIT_TRACE_MSG_LEN)); - char* qtypestr = skygw_get_qtype_str(qtype); - - skygw_log_write( - LOGFILE_TRACE, - "> Autocommit: %s, trx is %s, cmd: %s, type: %s, " - "stmt: %s%s %s", - (rses->rses_autocommit_enabled ? "[enabled]" : "[disabled]"), - (rses->rses_transaction_active ? "[open]" : "[not open]"), - STRPACKETTYPE(ptype), - (qtypestr==NULL ? "N/A" : qtypestr), - contentstr, - (querybuf->hint == NULL ? "" : ", Hint:"), - (querybuf->hint == NULL ? "" : STRHINTTYPE(querybuf->hint->type))); - - free(contentstr); - free(qtypestr); + if (!rses->rses_load_active) + { + uint8_t* packet = GWBUF_DATA(querybuf); + unsigned char ptype = packet[4]; + size_t len = MIN(GWBUF_LENGTH(querybuf), + MYSQL_GET_PACKET_LEN((unsigned char *)querybuf->start) - 1); + char* data = (char*) &packet[5]; + char* contentstr = strndup(data, MIN(len, RWSPLIT_TRACE_MSG_LEN)); + char* qtypestr = skygw_get_qtype_str(qtype); + + skygw_log_write(LOGFILE_TRACE, + "> Autocommit: %s, trx is %s, cmd: %s, type: %s, " + "stmt: %s%s %s", + (rses->rses_autocommit_enabled ? "[enabled]" : "[disabled]"), + (rses->rses_transaction_active ? "[open]" : "[not open]"), + STRPACKETTYPE(ptype), + (qtypestr == NULL ? "N/A" : qtypestr), + contentstr, + (querybuf->hint == NULL ? "" : ", Hint:"), + (querybuf->hint == NULL ? "" : STRHINTTYPE(querybuf->hint->type))); + + free(contentstr); + free(qtypestr); + } + else + { + skygw_log_write(LT, "> Processing LOAD DATA LOCAL INFILE: " + "%lu bytes sent.", rses->rses_load_data_sent); + } } /** * Find out where to route the query. Result may not be clear; it is @@ -2287,9 +2322,10 @@ static bool route_single_stmt( */ route_target = get_route_target(qtype, rses->rses_transaction_active, + rses->rses_load_active, rses->rses_config.rw_use_sql_variables_in, querybuf->hint); - + if (TARGET_IS_ALL(route_target)) { /** Multiple, conflicting routing target. Return error */ @@ -2988,10 +3024,21 @@ static void clientReply ( } else { + char* sql = modutil_get_SQL(bref->bref_pending_cmd); + + if (sql) + { LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Routing query \"%s\" failed.", - bref->bref_pending_cmd))); + LOGFILE_ERROR, + "Routing query \"%s\" failed.", sql))); + free(sql); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Failed to route query."))); + } } gwbuf_free(bref->bref_pending_cmd); bref->bref_pending_cmd = NULL; @@ -3087,9 +3134,9 @@ static void bref_clear_state( if(prev2 <= 0) { skygw_log_write(LE,"[%s] Error: negative current operation count in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } } } @@ -3118,10 +3165,11 @@ static void bref_set_state( ss_dassert(prev1 >= 0); if(prev1 < 0) { - skygw_log_write(LE,"[%s] Error: negative number of connections waiting for results in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + skygw_log_write(LE,"[%s] Error: negative number of connections waiting for " + "results in backend %s:%u", + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } /** Increase global operation count */ prev2 = atomic_add( @@ -3130,9 +3178,9 @@ static void bref_set_state( if(prev2 < 0) { skygw_log_write(LE,"[%s] Error: negative current operation count in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } } } @@ -3656,10 +3704,10 @@ static bool select_connect_backend_servers( ss_dassert(backend_ref[i].bref_backend->backend_conn_count > 0); /** disconnect opened connections */ - dcb_close(backend_ref[i].bref_dcb); bref_clear_state(&backend_ref[i], BREF_IN_USE); /** Decrease backend's connection counter. */ atomic_add(&backend_ref[i].bref_backend->backend_conn_count, -1); + dcb_close(backend_ref[i].bref_dcb); } } } @@ -4374,7 +4422,7 @@ static void tracelog_routed_query( "%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", pthread_self(), funcname, - buflen, + (int)buflen, querystr, b->backend_server->name, b->backend_server->port, @@ -4396,7 +4444,7 @@ static void tracelog_routed_query( "%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", pthread_self(), funcname, - buflen, + (int)buflen, querystr, b->backend_server->name, b->backend_server->port, @@ -4410,27 +4458,11 @@ static void tracelog_routed_query( /** - * Return rc, rc < 0 if router session is closed. rc == 0 if there are no - * capabilities specified, rc > 0 when there are capabilities. + * Return RCAP_TYPE_STMT_INPUT. */ -static uint8_t getCapabilities ( - ROUTER* inst, - void* router_session) +static int getCapabilities () { - ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - uint8_t rc; - - if (!rses_begin_locked_router_action(rses)) - { - rc = 0xff; - goto return_rc; - } - rc = rses->rses_capabilities; - - rses_end_locked_router_action(rses); - -return_rc: - return rc; + return RCAP_TYPE_STMT_INPUT; } /** @@ -4544,7 +4576,7 @@ static bool route_session_write( if (router_cli_ses->rses_config.rw_max_sescmd_history_size > 0 && router_cli_ses->rses_nsescmd >= router_cli_ses->rses_config.rw_max_sescmd_history_size) { - skygw_log_write(LE, "Warning: Router session exceeded session command history limit. " + skygw_log_write(LM, "Warning: Router session exceeded session command history limit. " "Slave recovery is disabled and only slave servers with consistent session state are used " "for the duration of the session."); router_cli_ses->rses_config.rw_disable_sescmd_hist = true; @@ -4799,9 +4831,8 @@ static void rwsplit_process_router_options( * @param router_session The router session * @param errmsgbuf The error message to reply * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION - * @param succp Result of action. True if there is at least master - * and enough slaves to continue session. Otherwise false. + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true iff router can continue * * Even if succp == true connecting to new slave may have failed. succp is to * tell whether router has enough master/slave connections to continue work. @@ -4820,17 +4851,14 @@ static void handleError ( CHK_DCB(backend_dcb); - /** Reset error handle flag from a given DCB */ - if (action == ERRACT_RESET) - { - backend_dcb->dcb_errhandle_called = false; - return; - } - /** Don't handle same error twice on same DCB */ if (backend_dcb->dcb_errhandle_called) { - /** we optimistically assume that previous call succeed */ + /** we optimistically assume that previous call succeed */ + /* + * The return of true is potentially misleading, but appears to + * be safe with the code as it stands on 9 Sept 2015 - MNB + */ *succp = true; return; } @@ -4843,12 +4871,13 @@ static void handleError ( if (session == NULL || rses == NULL) { *succp = false; - return; } - CHK_SESSION(session); - CHK_CLIENT_RSES(rses); + else + { + CHK_SESSION(session); + CHK_CLIENT_RSES(rses); - switch (action) { + switch (action) { case ERRACT_NEW_CONNECTION: { SERVER* srv; @@ -4856,7 +4885,7 @@ static void handleError ( if (!rses_begin_locked_router_action(rses)) { *succp = false; - return; + break; } srv = rses->rses_master_ref->bref_backend->backend_server; /** @@ -4866,6 +4895,25 @@ static void handleError ( if (rses->rses_master_ref->bref_dcb == backend_dcb && !SERVER_IS_MASTER(srv)) { + backend_ref_t* bref; + bref = get_bref_from_dcb(rses, backend_dcb); + if (bref != NULL) + { + CHK_BACKEND_REF(bref); + bref_clear_state(bref, BREF_IN_USE); + bref_set_state(bref, BREF_CLOSED); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : server %s:%d lost the " + "master status but could not locate the " + "corresponding backend ref.", + srv->name, + srv->port))); + dcb_close(backend_dcb); + } if (!srv->master_err_is_logged) { LOGIF(LE, (skygw_log_write_flush( @@ -4909,7 +4957,9 @@ static void handleError ( default: *succp = false; break; + } } + dcb_close(backend_dcb); } @@ -4965,7 +5015,7 @@ static bool handle_error_new_connection( DCB* backend_dcb, GWBUF* errmsg) { - ROUTER_CLIENT_SES* myrses; + ROUTER_CLIENT_SES* myrses; SESSION* ses; int router_nservers; int max_nslaves; @@ -4973,7 +5023,7 @@ static bool handle_error_new_connection( backend_ref_t* bref; bool succp; - myrses = *rses; + myrses = *rses; ss_dassert(SPINLOCK_IS_LOCKED(&myrses->rses_lock)); ses = backend_dcb->session; @@ -5023,7 +5073,6 @@ static bool handle_error_new_connection( DCB_REASON_NOT_RESPONDING, &router_handle_state_switch, (void *)bref); - router_nservers = router_get_servercount(inst); max_nslaves = rses_get_max_slavecount(myrses, router_nservers); max_slave_rlag = rses_get_max_replication_lag(myrses); @@ -5281,9 +5330,16 @@ static int router_handle_state_switch( backend_ref_t* bref; int rc = 1; SERVER* srv; - ROUTER_CLIENT_SES* rses; - SESSION* ses; CHK_DCB(dcb); + if (NULL == dcb->session->router_session) + { + /* + * The following processing will fail if there is no router session, + * because the "data" parameter will not contain meaningful data, + * so we have no choice but to stop here. + */ + return 0; + } bref = (backend_ref_t *)data; CHK_BACKEND_REF(bref); @@ -5302,7 +5358,10 @@ static int router_handle_state_switch( srv->port, STRSRVSTATUS(srv)))); CHK_SESSION(((SESSION*)dcb->session)); - CHK_CLIENT_RSES(((ROUTER_CLIENT_SES *)dcb->session->router_session)); + if (dcb->session->router_session) + { + CHK_CLIENT_RSES(((ROUTER_CLIENT_SES *)dcb->session->router_session)); + } switch (reason) { case DCB_REASON_NOT_RESPONDING: diff --git a/server/modules/routing/schemarouter/CMakeLists.txt b/server/modules/routing/schemarouter/CMakeLists.txt index ae8f295c2..916db0053 100644 --- a/server/modules/routing/schemarouter/CMakeLists.txt +++ b/server/modules/routing/schemarouter/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(schemarouter SHARED schemarouter.c sharding_common.c) target_link_libraries(schemarouter log_manager query_classifier) +add_dependencies(schemarouter pcre2) install(TARGETS schemarouter DESTINATION ${MAXSCALE_LIBDIR}) add_library(shardrouter SHARED shardrouter.c svcconn.c sharding_common.c) target_link_libraries(shardrouter log_manager query_classifier) +add_dependencies(shardrouter pcre2) install(TARGETS shardrouter DESTINATION ${MAXSCALE_LIBDIR}) if(BUILD_TESTS) diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 22330c24d..a13e7c554 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -41,6 +41,9 @@ /** Size of the hashtable used to store ignored databases */ #define SCHEMAROUTER_HASHSIZE 100 +/** Hashtable size for the per user shard maps */ +#define SCHEMAROUTER_USERHASH_SIZE 10 + MODULE_INFO info = { MODULE_API_ROUTER, MODULE_BETA_RELEASE, @@ -49,10 +52,6 @@ MODULE_INFO info = { }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; /** * @file schemarouter.c The entry points for the simple sharding * router module. @@ -62,6 +61,8 @@ extern __thread log_info_t tls_log_info; * * Date Who Description * 01/12/2014 Vilho Raatikka/Markus Mäkelä Initial implementation + * 09/09/2015 Martin Brampton Modify error handler + * 25/09/2015 Martin Brampton Block callback processing when no router session in the DCB * * @endverbatim */ @@ -98,7 +99,7 @@ static route_target_t get_shard_route_target ( bool trx_active, HINT* hint); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); static bool connect_backend_servers( backend_ref_t* backend_ref, @@ -223,6 +224,7 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses, GWBUF** wbuf); bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses); void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses); +void synchronize_shard_map(ROUTER_CLIENT_SES *client); static int hashkeyfun(void* key) { if(key == NULL){ @@ -247,7 +249,39 @@ static int hashcmpfun( return strcmp(i1,i2); } +void* keyfreefun(void* data) +{ + free(data); + return NULL; +} +/** + * Allocate a shard map and initialize it. + * @return Pointer to new shard_map_t or NULL if memory allocation failed + */ +shard_map_t* shard_map_alloc() +{ + shard_map_t *rval; + + if ((rval = (shard_map_t*) malloc(sizeof(shard_map_t)))) + { + if ((rval->hash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun))) + { + HASHMEMORYFN kcopy = (HASHMEMORYFN)strdup; + HASHMEMORYFN kfree = (HASHMEMORYFN)keyfreefun; + hashtable_memory_fns(rval->hash, kcopy, kcopy, kfree, kfree); + spinlock_init(&rval->lock); + rval->last_updated = 0; + rval->state = SHMAP_UNINIT; + } + else + { + free(rval); + rval = NULL; + } + } + return rval; +} /** * Convert a length encoded string into a C string. @@ -363,6 +397,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* ptr += gw_mysql_get_byte3(ptr) + 4; } + spinlock_acquire(&rses->shardmap->lock); while (ptr < (unsigned char*) buf->end && !PTR_IS_EOF(ptr)) { int payloadlen = gw_mysql_get_byte3(ptr); @@ -371,7 +406,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* if (data) { - if (hashtable_add(rses->dbhash, data, target)) + if (hashtable_add(rses->shardmap->hash, data, target)) { skygw_log_write(LOGFILE_TRACE, "schemarouter: <%s, %s>", target, data); } @@ -385,7 +420,8 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* { duplicate_found = true; skygw_log_write(LE, "Error: Database '%s' found on servers '%s' and '%s' for user %s@%s.", - data, target, hashtable_fetch(rses->dbhash, data), + data, target, + (char*)hashtable_fetch(rses->shardmap->hash, data), rses->rses_client_dcb->user, rses->rses_client_dcb->remote); } @@ -394,6 +430,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t* } ptr += packetlen; } + spinlock_release(&rses->shardmap->lock); if (ptr < (unsigned char*) buf->end && PTR_IS_EOF(ptr) && bref->n_mapping_eof == 1) @@ -478,8 +515,8 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session) * @param buffer Query to inspect * @return Name of the backend or NULL if the query contains no known databases. */ -char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer,skygw_query_type_t qtype){ - HASHTABLE* ht = client->dbhash; +char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer,skygw_query_type_t qtype) +{ int sz = 0,i,j; char** dbnms = NULL; char* rval = NULL,*query, *tmp = NULL; @@ -491,6 +528,8 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, dbnms = skygw_get_database_names(buffer,&sz); + HASHTABLE* ht = client->shardmap->hash; + if(sz > 0){ for(i = 0; i < sz; i++){ char* name; @@ -542,20 +581,18 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, if(tmp == NULL) { - rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db); + rval = (char*) hashtable_fetch(ht, client->current_db); skygw_log_write(LOGFILE_TRACE,"schemarouter: SHOW TABLES query, current database '%s' on server '%s'", - client->rses_mysql_session->db,rval); + client->current_db,rval); } else { rval = tmp; has_dbs = true; - - } - return rval; } - + else + { if(buffer->hint && buffer->hint->type == HINT_ROUTE_TO_NAMED_SERVER) { for(i = 0; i < client->rses_nbackends; i++) @@ -571,20 +608,21 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, } } - if(rval == NULL && !has_dbs && client->rses_mysql_session->db[0] != '\0') + if(rval == NULL && !has_dbs && client->current_db[0] != '\0') { /** * If the target name has not been found and the session has an * active database, set is as the target */ - rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db); + rval = (char*) hashtable_fetch(ht, client->current_db); if(rval) { - skygw_log_write(LOGFILE_TRACE,"schemarouter: Using active database '%s'",client->rses_mysql_session->db); + skygw_log_write(LOGFILE_TRACE,"schemarouter: Using active database '%s'",client->current_db); } } - + } + return rval; } @@ -765,6 +803,17 @@ createInstance(SERVICE *service, char **options) (HASHMEMORYFN)free, NULL); + if ((router->shard_maps = hashtable_alloc(SCHEMAROUTER_USERHASH_SIZE, hashkeyfun, hashcmpfun)) == NULL) + { + skygw_log_write(LE, "Error: Memory allocation failed when allocating schemarouter database ignore list."); + hashtable_free(router->ignored_dbs); + free(router); + return NULL; + } + + hashtable_memory_fns(router->shard_maps,(HASHMEMORYFN)strdup, + NULL, (HASHMEMORYFN)keyfreefun, NULL); + /** Add default system databases to ignore */ hashtable_add(router->ignored_dbs,"mysql",""); hashtable_add(router->ignored_dbs,"information_schema",""); @@ -806,7 +855,7 @@ createInstance(SERVICE *service, char **options) PCRE2_UCHAR errbuf[512]; pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); skygw_log_write(LE, "Error: Regex compilation failed at %d for regex '%s': %s", - erroffset, param->value, errbuf); + (int)erroffset, param->value, errbuf); hashtable_free(router->ignored_dbs); free(router); return NULL; @@ -979,6 +1028,25 @@ retblock: return (ROUTER *)router; } +/** + * Check if the shard map is out of date and update its state if necessary. + * @param router Router instance + * @param map Shard map to update + * @return Current state of the shard map + */ +enum shard_map_state shard_map_update_state(shard_map_t *self, ROUTER_INSTANCE* router) +{ + spinlock_acquire(&self->lock); + double tdiff = difftime(time(NULL), self->last_updated); + if (tdiff > router->schemarouter_config.refresh_min_interval) + { + self->state = SHMAP_STALE; + } + enum shard_map_state state = self->state; + spinlock_release(&self->lock); + return state; +} + /** * Associate a new session with this instance of the router. * @@ -1007,7 +1075,7 @@ static void* newSession( memset(db,0,MYSQL_DATABASE_MAXLEN+1); - spinlock_acquire(&protocol->protocol_lock); + spinlock_acquire(&session->ses_lock); /* To enable connecting directly to a sharded database we first need * to disable it for the client DCB's protocol so that we can connect to them*/ @@ -1028,7 +1096,7 @@ static void* newSession( LOGIF(LT,(skygw_log_write(LT,"schemarouter: Client'%s' connecting with empty database.",data->user))); } - spinlock_release(&protocol->protocol_lock); + spinlock_release(&session->ses_lock); client_rses = (ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES)); @@ -1045,7 +1113,37 @@ static void* newSession( client_rses->router = router; client_rses->rses_mysql_session = (MYSQL_session*)session->data; client_rses->rses_client_dcb = (DCB*)session->client; - + + spinlock_acquire(&router->lock); + + shard_map_t *map = hashtable_fetch(router->shard_maps, session->client->user); + enum shard_map_state state; + + if (map) + { + state = shard_map_update_state(map, router); + } + + spinlock_release(&router->lock); + + if (map == NULL || state != SHMAP_READY) + { + if ((map = shard_map_alloc()) == NULL) + { + skygw_log_write(LE, "Error: Failed to allocate enough memory to create" + "new shard mapping. Session will be closed."); + free(client_rses); + return NULL; + } + client_rses->init = INIT_UNINT; + } + else + { + client_rses->init = INIT_READY; + atomic_add(&router->stats.shmap_cache_hit, 1); + } + + client_rses->shardmap = map; client_rses->dcb_reply = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); client_rses->dcb_reply->func.read = internalReply; client_rses->dcb_reply->state = DCB_STATE_POLLING; @@ -1057,7 +1155,7 @@ static void* newSession( client_rses->dcb_route->state = DCB_STATE_POLLING; client_rses->dcb_route->session = session; client_rses->rses_config.last_refresh = time(NULL); - client_rses->init = INIT_UNINT; + if(using_db) client_rses->init |= INIT_USE_DB; /** @@ -1131,12 +1229,6 @@ static void* newSession( router_nservers, session, router); - - client_rses->dbhash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun); - hashtable_memory_fns(client_rses->dbhash,(HASHMEMORYFN)strdup, - (HASHMEMORYFN)strdup, - (HASHMEMORYFN)free, - (HASHMEMORYFN)free); rses_end_locked_router_action(client_rses); @@ -1150,7 +1242,6 @@ static void* newSession( goto return_rses; } /** Copy backend pointers to router session. */ - client_rses->rses_capabilities = RCAP_TYPE_STMT_INPUT; client_rses->rses_backend_ref = backend_ref; client_rses->rses_nbackends = router_nservers; /*< # of backend servers */ @@ -1366,7 +1457,6 @@ static void freeSession( * all the memory and other resources associated * to the client session. */ - hashtable_free(router_cli_ses->dbhash); free(router_cli_ses->rses_backend_ref); free(router_cli_ses); return; @@ -1501,7 +1591,7 @@ ROUTER* instance, rses_property_t* rses_prop_tmp; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; - dbname = router_cli_ses->rses_mysql_session->db; + dbname = router_cli_ses->current_db; if (is_drop_table_query(querybuf)) { @@ -1559,7 +1649,7 @@ ROUTER* instance, rses_property_t* rses_prop_tmp; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; - dbname = router_cli_ses->rses_mysql_session->db; + dbname = router_cli_ses->current_db; if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || QUERY_IS_TYPE(qtype, QUERY_TYPE_LOCAL_READ) || @@ -1638,7 +1728,7 @@ void check_create_tmp_table( HASHTABLE* h; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; - dbname = router_cli_ses->rses_mysql_session->db; + dbname = router_cli_ses->current_db; if (QUERY_IS_TYPE(type, QUERY_TYPE_CREATE_TMP_TABLE)) @@ -1729,7 +1819,39 @@ void check_create_tmp_table( int cmpfn(const void* a, const void *b) { - return strcmp(*(char**)a,*(char**)b); + return strcmp(*(char* const *)a,*(char* const *)b); +} + +/** Internal structure used to stream the list of databases */ +struct string_array +{ + char** array; + int position; + int size; +}; + +/** + * Callback for the database list streaming. + * @param rset Result set which is being processed + * @param data Pointer to struct string_array containing the database names + * @return New resultset row or NULL if no more data is available. If memory allocation + * failed, NULL is returned. + */ +RESULT_ROW *result_set_cb(struct resultset * rset, void *data) +{ + RESULT_ROW *row = NULL; + struct string_array *strarray = (struct string_array*) data; + + if (strarray->position < strarray->size && (row = resultset_make_row(rset))) + { + if (resultset_row_set(row, 0, strarray->array[strarray->position++]) == 0) + { + resultset_free_row(row); + row = NULL; + } + } + + return row; } /** @@ -1738,184 +1860,48 @@ int cmpfn(const void* a, const void *b) * in it. * @param router Router instance * @param client Router client session - * @return Pointer to new GWBUF containing the custom result set + * @return True if the sending of the database list was successful, otherwise false */ -GWBUF* -gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client) +bool send_database_list(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client) { - GWBUF* rval = NULL; - HASHTABLE* ht = client->dbhash; - HASHITERATOR* iter = hashtable_iterator(ht); - backend_ref_t *bref = client->rses_backend_ref; - BACKEND** backends = router->servers; - unsigned int coldef_len = 0; - int i; - char dbname[MYSQL_DATABASE_MAXLEN + 1]; - char *value; - unsigned char* ptr; - char catalog[4] = {0x03, 'd', 'e', 'f'}; - const char* schema = "information_schema"; - const char* table = "SCHEMATA"; - const char* org_table = "SCHEMATA"; - const char* name = "Database"; - const char* org_name = "SCHEMA_NAME"; - char next_length = 0x0c; - char charset[2] = {0x21, 0x00}; - char column_length[4] = {MYSQL_DATABASE_MAXLEN, - MYSQL_DATABASE_MAXLEN >> 8, - MYSQL_DATABASE_MAXLEN >> 16, - MYSQL_DATABASE_MAXLEN >> 24}; - char column_type = 0xfd; - - char eof[9] = {0x05, 0x00, 0x00, - 0x03, 0xfe, 0x00, - 0x00, 0x22, 0x00}; -#if defined(NOT_USED) - char ok_packet[11] = {0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00}; -#endif - - coldef_len = sizeof(catalog) + strlen(schema) + 1 + - strlen(table) + 1 + - strlen(org_table) + 1 + - strlen(name) + 1 + - strlen(org_name) + 1 + - 1 + 2 + 4 + 1 + 2 + 1 + 2; - - - rval = gwbuf_alloc(5 + 4 + coldef_len + sizeof(eof)); - - ptr = rval->start; - - /**First packet*/ - - *ptr++ = 0x01; - *ptr++ = 0x00; - *ptr++ = 0x00; - *ptr++ = 0x01; - *ptr++ = 0x01; - - /**Second packet containing the column definitions*/ - - *ptr++ = coldef_len; - *ptr++ = coldef_len >> 8; - *ptr++ = coldef_len >> 16; - *ptr++ = 0x02; - - memcpy((void*) ptr, catalog, 4); - ptr += 4; - - *ptr++ = strlen(schema); - memcpy((void*) ptr, schema, strlen(schema)); - ptr += strlen(schema); - - *ptr++ = strlen(table); - memcpy((void*) ptr, table, strlen(table)); - ptr += strlen(table); - - *ptr++ = strlen(org_table); - memcpy((void*) ptr, org_table, strlen(org_table)); - ptr += strlen(org_table); - - *ptr++ = strlen(name); - memcpy((void*) ptr, name, strlen(name)); - ptr += strlen(name); - - *ptr++ = strlen(org_name); - memcpy((void*) ptr, org_name, strlen(org_name)); - ptr += strlen(org_name); - - *ptr++ = next_length; - *ptr++ = charset[0]; - *ptr++ = charset[1]; - *ptr++ = column_length[0]; - *ptr++ = column_length[1]; - *ptr++ = column_length[2]; - *ptr++ = column_length[3]; - *ptr++ = column_type; - *ptr++ = 0x01; - memset(ptr, 0, 4); - ptr += 4; - - memcpy(ptr, eof, sizeof(eof)); - - unsigned int packet_num = 4; - int j = 0,ndbs = 0, bufsz = 10; - char** dbs; - - if((dbs = malloc(sizeof(char*)*bufsz)) == NULL) + bool rval = false; + spinlock_acquire(&client->shardmap->lock); + if (client->shardmap->state == SHMAP_READY) { - gwbuf_free(rval); - hashtable_iterator_free(iter); - return NULL; - } + struct string_array strarray; + const int size = hashtable_size(client->shardmap->hash); + strarray.array = malloc(size * sizeof(char*)); + strarray.position = 0; + HASHITERATOR *iter = hashtable_iterator(client->shardmap->hash); + RESULTSET* resultset = resultset_create(result_set_cb, &strarray); - while((value = (char*) hashtable_next(iter))) - { - char* bend = hashtable_fetch(ht, value); - - for(i = 0; backends[i]; i++) + if (strarray.array && iter && resultset) { - if(strcmp(bref[i].bref_backend->backend_server->unique_name, bend) == 0 && - BREF_IS_IN_USE(&bref[i]) && !BREF_IS_CLOSED(&bref[i])) + char *key; + int i = 0; + while ((key = hashtable_next(iter))) { - ndbs++; - - if(ndbs >= bufsz) - { - bufsz += bufsz / 2; - char** tmp = realloc(dbs,sizeof(char*)*bufsz); - if(tmp == NULL) - { - gwbuf_free(rval); - hashtable_iterator_free(iter); - for(i=0;ishardmap->hash, key); + SERVER * server = server_find_by_unique_name(value); + if (SERVER_IS_RUNNING(server)) + { + strarray.array[i++] = key; + } + } + strarray.size = i; + qsort(strarray.array, strarray.size, sizeof(char*), cmpfn); + if (resultset_add_column(resultset, "Database", MYSQL_DATABASE_MAXLEN, + COL_TYPE_VARCHAR)) + { + resultset_stream_mysql(resultset, client->rses_client_dcb); + rval = true; } } + resultset_free(resultset); + hashtable_iterator_free(iter); + free(strarray.array); } - - qsort(&dbs[0],(size_t)ndbs,sizeof(char*),cmpfn); - - for(j = 0;jstart; - *ptr++ = plen; - *ptr++ = plen >> 8; - *ptr++ = plen >> 16; - *ptr++ = packet_num++; - *ptr++ = plen - 1; - memcpy(ptr, dbname, plen - 1); - - /** Append the row*/ - rval = gwbuf_append(rval, temp); - free(dbs[j]); - } - - eof[3] = packet_num; - - GWBUF* last_packet = gwbuf_alloc(sizeof(eof)); - memcpy(last_packet->start, eof, sizeof(eof)); - rval = gwbuf_append(rval, last_packet); - - rval = gwbuf_make_contiguous(rval); - hashtable_iterator_free(iter); - free(dbs); + spinlock_release(&client->shardmap->lock); return rval; } @@ -1957,8 +1943,9 @@ static int routeQuery( route_target_t route_target = TARGET_UNDEFINED; bool succp = false; char* tname = NULL; + char* targetserver = NULL; GWBUF* querybuf = qbuf; - char db[MYSQL_DATABASE_MAXLEN + 1]; + char db[MYSQL_DATABASE_MAXLEN + 1]; char errbuf[26+MYSQL_DATABASE_MAXLEN]; CHK_CLIENT_RSES(router_cli_ses); @@ -1987,9 +1974,15 @@ static int routeQuery( } - if(router_cli_ses->init & INIT_MAPPING) + /** + * If the databases are still being mapped or if the client connected + * with a default database but no database mapping was performed we need + * to store the query. Once the databases have been mapped and/or the + * default database is taken into use we can send the query forward. + */ + if (router_cli_ses->init & (INIT_MAPPING | INIT_USE_DB)) { - + int init_rval = 1; char* querystr = modutil_get_SQL(querybuf); skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"schemarouter: Storing query for session %p: %s", router_cli_ses->rses_client_dcb->session, @@ -2012,14 +2005,26 @@ static int routeQuery( ptr->next = querybuf; } + + if (router_cli_ses->init == (INIT_READY | INIT_USE_DB)) + { + /** + * This state is possible if a client connects with a default database + * and the shard map was found from the router cache + */ + if (!handle_default_db(router_cli_ses)) + { + init_rval = 0; + } + } rses_end_locked_router_action(router_cli_ses); - return 1; + return init_rval; } } rses_end_locked_router_action(router_cli_ses); - + packet = GWBUF_DATA(querybuf); packet_type = packet[4]; @@ -2139,32 +2144,38 @@ static int routeQuery( if (packet_type == MYSQL_COM_INIT_DB || op == QUERY_OP_CHANGE_DB) { - if (!(change_successful = change_current_db(router_cli_ses->rses_mysql_session, - router_cli_ses->dbhash, - querybuf))) + spinlock_acquire(&router_cli_ses->shardmap->lock); + change_successful = change_current_db(router_cli_ses->current_db, + router_cli_ses->shardmap->hash, + querybuf); + spinlock_release(&router_cli_ses->shardmap->lock); + if (!change_successful) { time_t now = time(NULL); if(router_cli_ses->rses_config.refresh_databases && difftime(now,router_cli_ses->rses_config.last_refresh) > router_cli_ses->rses_config.refresh_min_interval) { + spinlock_acquire(&router_cli_ses->shardmap->lock); + router_cli_ses->shardmap->state = SHMAP_STALE; + spinlock_release(&router_cli_ses->shardmap->lock); + rses_begin_locked_router_action(router_cli_ses); + router_cli_ses->rses_config.last_refresh = now; router_cli_ses->queue = querybuf; - hashtable_free(router_cli_ses->dbhash); - if((router_cli_ses->dbhash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun)) == NULL) - { - skygw_log_write(LE,"Error: Hashtable allocation failed."); - rses_end_locked_router_action(router_cli_ses); - return 1; - } - hashtable_memory_fns(router_cli_ses->dbhash,(HASHMEMORYFN)strdup, - (HASHMEMORYFN)strdup, - (HASHMEMORYFN)free, - (HASHMEMORYFN)free); - gen_databaselist(inst,router_cli_ses); + int rc_refresh = 1; + + if((router_cli_ses->shardmap = shard_map_alloc())) + { + gen_databaselist(inst,router_cli_ses); + } + else + { + rc_refresh = 0; + } rses_end_locked_router_action(router_cli_ses); - return 1; + return rc_refresh; } extract_database(querybuf,db); snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db); @@ -2186,18 +2197,15 @@ static int routeQuery( } } - if(QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_DATABASES)) - { - /** - * Generate custom response that contains all the databases - */ - - GWBUF* fake = gen_show_dbs_response(inst,router_cli_ses); - poll_add_epollin_event_to_dcb(router_cli_ses->dcb_reply,fake); - ret = 1; - - goto retblock; - } + /** Create the response to the SHOW DATABASES from the mapped databases */ + if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_DATABASES)) + { + if (send_database_list(inst, router_cli_ses)) + { + ret = 1; + } + goto retblock; + } route_target = get_shard_route_target(qtype, router_cli_ses->rses_transaction_active, @@ -2207,27 +2215,40 @@ static int routeQuery( op == QUERY_OP_CHANGE_DB) { route_target = TARGET_UNDEFINED; - tname = hashtable_fetch(router_cli_ses->dbhash,router_cli_ses->rses_mysql_session->db); - + + spinlock_acquire(&router_cli_ses->shardmap->lock); + tname = hashtable_fetch(router_cli_ses->shardmap->hash,router_cli_ses->current_db); + + if(tname) { skygw_log_write(LOGFILE_TRACE,"schemarouter: INIT_DB for database '%s' on server '%s'", - router_cli_ses->rses_mysql_session->db,tname); + router_cli_ses->current_db,tname); route_target = TARGET_NAMED_SERVER; + targetserver = strdup(tname); } else { skygw_log_write(LOGFILE_TRACE,"schemarouter: INIT_DB with unknown database"); } + spinlock_release(&router_cli_ses->shardmap->lock); } - else if(route_target != TARGET_ALL && - (tname = get_shard_target_name(inst,router_cli_ses,querybuf,qtype)) != NULL) + else if (route_target != TARGET_ALL) { + /** If no database is found in the query and there is no active database + * or hints in the query we need to route the query to the first available + * server. This isn't ideal for monitoring server status but works if + * we just want the server to send an error back. */ + + spinlock_acquire(&router_cli_ses->shardmap->lock); + if ((tname = get_shard_target_name(inst,router_cli_ses,querybuf,qtype)) != NULL) + { bool shard_ok = check_shard_status(inst,tname); if(shard_ok) { route_target = TARGET_NAMED_SERVER; + targetserver = strdup(tname); } else { @@ -2238,19 +2259,21 @@ static int routeQuery( * for an alternate backend with the database. If this is not found * the target is undefined and an error will be returned to the client. */ - } + } + } + spinlock_release(&router_cli_ses->shardmap->lock); } if(TARGET_IS_UNDEFINED(route_target)) { - + spinlock_acquire(&router_cli_ses->shardmap->lock); tname = get_shard_target_name(inst,router_cli_ses,querybuf,qtype); if( (tname == NULL && packet_type != MYSQL_COM_INIT_DB && - router_cli_ses->rses_mysql_session->db[0] == '\0') || + router_cli_ses->current_db[0] == '\0') || packet_type == MYSQL_COM_FIELD_LIST || - (router_cli_ses->rses_mysql_session->db[0] != '\0')) + (router_cli_ses->current_db[0] != '\0')) { /** * No current database and no databases in query or @@ -2262,7 +2285,11 @@ static int routeQuery( } else - { + { + if (tname) + { + targetserver = strdup(tname); + } if(!change_successful) { /** @@ -2278,11 +2305,11 @@ static int routeQuery( /** Something else went wrong, terminate connection */ ret = 0; } - + spinlock_release(&router_cli_ses->shardmap->lock); goto retblock; } - + spinlock_release(&router_cli_ses->shardmap->lock); } if (TARGET_IS_ALL(route_target)) @@ -2325,8 +2352,8 @@ static int routeQuery( { if(SERVER_IS_RUNNING(inst->servers[z]->backend_server)) { - tname = inst->servers[z]->backend_server->unique_name; route_target = TARGET_NAMED_SERVER; + targetserver = strdup(inst->servers[z]->backend_server->unique_name); break; } } @@ -2349,14 +2376,14 @@ static int routeQuery( /** * Query is routed to one of the backends */ - if (TARGET_IS_NAMED_SERVER(route_target)) + if (TARGET_IS_NAMED_SERVER(route_target) && targetserver != NULL) { /** * Search backend server by name or replication lag. * If it fails, then try to find valid slave or master. */ - succp = get_shard_dcb(&target_dcb, router_cli_ses, tname); + succp = get_shard_dcb(&target_dcb, router_cli_ses, targetserver); if (!succp) { @@ -2365,7 +2392,7 @@ static int routeQuery( "Was supposed to route to named server " "%s but couldn't find the server in a " "suitable state.", - tname))); + targetserver))); } } @@ -2422,7 +2449,7 @@ static int routeQuery( } rses_end_locked_router_action(router_cli_ses); retblock: - + free(targetserver); gwbuf_free(querybuf); return ret; @@ -2556,6 +2583,8 @@ diagnostic(ROUTER *instance, DCB *dcb) dcb_printf(dcb,"Shortest session: %.2lf seconds\n",router->stats.ses_shortest); dcb_printf(dcb,"Average session length: %.2lf seconds\n",router->stats.ses_average); } + dcb_printf(dcb,"Shard map cache hits: %d\n",router->stats.shmap_cache_hit); + dcb_printf(dcb,"Shard map cache misses: %d\n",router->stats.shmap_cache_miss); dcb_printf(dcb,"\n"); } @@ -2633,6 +2662,21 @@ static void clientReply(ROUTER* instance, if (rc == 1) { + spinlock_acquire(&router_cli_ses->shardmap->lock); + + router_cli_ses->shardmap->state = SHMAP_READY; + router_cli_ses->shardmap->last_updated = time(NULL); + spinlock_release(&router_cli_ses->shardmap->lock); + + rses_end_locked_router_action(router_cli_ses); + + synchronize_shard_map(router_cli_ses); + + if (!rses_begin_locked_router_action(router_cli_ses)) + { + return; + } + /* * Check if the session is reconnecting with a database name * that is not in the hashtable. If the database is not found @@ -2653,6 +2697,7 @@ static void clientReply(ROUTER* instance, if (router_cli_ses->queue) { + ss_dassert(router_cli_ses->init == INIT_READY); route_queued_query(router_cli_ses); } skygw_log_write_flush(LOGFILE_DEBUG, @@ -2669,19 +2714,20 @@ static void clientReply(ROUTER* instance, return; } - if (router_cli_ses->queue) - { - route_queued_query(router_cli_ses); - } - if (router_cli_ses->init & INIT_USE_DB) { skygw_log_write(LOGFILE_DEBUG, "schemarouter: Reply to USE '%s' received for session %p", router_cli_ses->connect_db, router_cli_ses->rses_client_dcb->session); router_cli_ses->init &= ~INIT_USE_DB; - strcpy(router_cli_ses->rses_mysql_session->db, router_cli_ses->connect_db); + strcpy(router_cli_ses->current_db, router_cli_ses->connect_db); ss_dassert(router_cli_ses->init == INIT_READY); + + if (router_cli_ses->queue) + { + route_queued_query(router_cli_ses); + } + rses_end_locked_router_action(router_cli_ses); if (writebuf) { @@ -2690,6 +2736,14 @@ static void clientReply(ROUTER* instance, return; } + if (router_cli_ses->queue) + { + ss_dassert(router_cli_ses->init == INIT_READY); + route_queued_query(router_cli_ses); + rses_end_locked_router_action(router_cli_ses); + return; + } + CHK_BACKEND_REF(bref); scur = &bref->bref_sescmd_cur; /** @@ -2811,10 +2865,21 @@ static void clientReply(ROUTER* instance, } else { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Routing query \"%s\" failed.", - bref->bref_pending_cmd))); + char* sql = modutil_get_SQL(bref->bref_pending_cmd); + + if (sql) + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Routing query \"%s\" failed.", sql))); + free(sql); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Routing query failed."))); + } } gwbuf_free(bref->bref_pending_cmd); bref->bref_pending_cmd = NULL; @@ -2909,9 +2974,9 @@ static void bref_clear_state( if(prev2 <= 0) { skygw_log_write(LE,"[%s] Error: negative current operation count in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } } } @@ -2940,10 +3005,11 @@ static void bref_set_state( ss_dassert(prev1 >= 0); if(prev1 < 0) { - skygw_log_write(LE,"[%s] Error: negative number of connections waiting for results in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + skygw_log_write(LE,"[%s] Error: negative number of connections waiting " + "for results in backend %s:%u", + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } /** Increase global operation count */ prev2 = atomic_add( @@ -2952,9 +3018,9 @@ static void bref_set_state( if(prev2 < 0) { skygw_log_write(LE,"[%s] Error: negative current operation count in backend %s:%u", - __FUNCTION__, - &bref->bref_backend->backend_server->name, - &bref->bref_backend->backend_server->port); + __FUNCTION__, + bref->bref_backend->backend_server->name, + bref->bref_backend->backend_server->port); } } } @@ -3588,22 +3654,6 @@ static bool execute_sescmd_in_backend( sescmd_cursor_clone_querybuf(scur)); break; - case MYSQL_COM_INIT_DB: - { - /** - * Record database name and store to session. - */ - GWBUF* tmpbuf; - MYSQL_session* data; - unsigned int qlen; - - data = dcb->session->data; - tmpbuf = scur->scmd_cur_cmd->my_sescmd_buf; - qlen = MYSQL_GET_PACKET_LEN((unsigned char*)tmpbuf->start); - memset(data->db,0,MYSQL_DATABASE_MAXLEN+1); - strncpy(data->db,tmpbuf->start+5,qlen - 1); - } - /** Fallthrough */ case MYSQL_COM_QUERY: default: /** @@ -3747,7 +3797,7 @@ static void tracelog_routed_query( "%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", pthread_self(), funcname, - buflen, + (int)buflen, querystr, b->backend_server->name, b->backend_server->port, @@ -3769,7 +3819,7 @@ static void tracelog_routed_query( "%lu [%s] %d bytes long buf, \"%s\" -> %s:%d %s dcb %p", pthread_self(), funcname, - buflen, + (int)buflen, querystr, b->backend_server->name, b->backend_server->port, @@ -3783,27 +3833,11 @@ static void tracelog_routed_query( /** - * Return rc, rc < 0 if router session is closed. rc == 0 if there are no - * capabilities specified, rc > 0 when there are capabilities. + * Return RCAP_TYPE_STMT_INPUT. */ -static uint8_t getCapabilities ( - ROUTER* inst, - void* router_session) +static int getCapabilities () { - ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - uint8_t rc; - - if (!rses_begin_locked_router_action(rses)) - { - rc = 0xff; - goto return_rc; - } - rc = rses->rses_capabilities; - - rses_end_locked_router_action(rses); - -return_rc: - return rc; + return RCAP_TYPE_STMT_INPUT; } /** @@ -4048,47 +4082,45 @@ return_succp: * @param router_session The router session * @param errmsgbuf The error message to reply * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION - * @param succp Result of action. + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true iff router can continue * * Even if succp == true connecting to new slave may have failed. succp is to * tell whether router has enough master/slave connections to continue work. */ static void handleError ( - ROUTER* instance, - void* router_session, - GWBUF* errmsgbuf, - DCB* backend_dcb, - error_action_t action, - bool* succp) + ROUTER* instance, + void* router_session, + GWBUF* errmsgbuf, + DCB* backend_dcb, + error_action_t action, + bool* succp) { - SESSION* session; - ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; - ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; + SESSION* session; + ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; + ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session; - CHK_DCB(backend_dcb); - if(succp == NULL || action == ERRACT_RESET) - { - return; - } - /** Don't handle same error twice on same DCB */ - if (backend_dcb->dcb_errhandle_called) - { - /** we optimistically assume that previous call succeed */ - *succp = true; - return; - } - else - { - backend_dcb->dcb_errhandle_called = true; - } - session = backend_dcb->session; + CHK_DCB(backend_dcb); - if (session == NULL || rses == NULL) - { - *succp = false; - return; - } + /** Don't handle same error twice on same DCB */ + if (backend_dcb->dcb_errhandle_called) + { + /** we optimistically assume that previous call succeed */ + *succp = true; + return; + } + else + { + backend_dcb->dcb_errhandle_called = true; + } + session = backend_dcb->session; + + if (session == NULL || rses == NULL) + { + *succp = false; + } + else + { CHK_SESSION(session); CHK_CLIENT_RSES(rses); @@ -4098,7 +4130,7 @@ static void handleError ( if (!rses_begin_locked_router_action(rses)) { *succp = false; - return; + break; } /** * This is called in hope of getting replacement for @@ -4126,6 +4158,8 @@ static void handleError ( *succp = false; break; } + } + dcb_close(backend_dcb); } @@ -4353,6 +4387,15 @@ router_handle_state_switch( SERVER* srv; CHK_DCB(dcb); + if (NULL == dcb->session->router_session) + { + /* + * The following processing will fail if there is no router session, + * because the "data" parameter will not contain meaningful data, + * so we have no choice but to stop here. + */ + return 0; + } bref = (backend_ref_t *) data; CHK_BACKEND_REF(bref); @@ -4447,7 +4490,7 @@ RESULT_ROW* shard_list_cb(struct resultset* rset, void* data) RESULT_ROW* rval = NULL; if((key = hashtable_next(sl->iter)) && - (value = hashtable_fetch(sl->rses->dbhash,key))) + (value = hashtable_fetch(sl->rses->shardmap->hash,key))) { if((rval = resultset_make_row(sl->rset))) { @@ -4465,23 +4508,40 @@ RESULT_ROW* shard_list_cb(struct resultset* rset, void* data) */ int process_show_shards(ROUTER_CLIENT_SES* rses) { - HASHITERATOR* iter = hashtable_iterator(rses->dbhash); - struct shard_list sl; + int rval = 0; - sl.iter = iter; - sl.rses = rses; - if((sl.rset = resultset_create(shard_list_cb,&sl)) == NULL) + spinlock_acquire(&rses->shardmap->lock); + if(rses->shardmap->state == SHMAP_READY) { - skygw_log_write(LE,"[%s] Error: Failed to create resultset.",__FUNCTION__); - return -1; + HASHITERATOR* iter = hashtable_iterator(rses->shardmap->hash); + struct shard_list sl; + if (iter) + { + sl.iter = iter; + sl.rses = rses; + if ((sl.rset = resultset_create(shard_list_cb, &sl)) == NULL) + { + skygw_log_write(LE, "[%s] Error: Failed to create resultset.", __FUNCTION__); + rval = -1; + } + else + { + resultset_add_column(sl.rset, "Database", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR); + resultset_add_column(sl.rset, "Server", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR); + resultset_stream_mysql(sl.rset, rses->rses_client_dcb); + resultset_free(sl.rset); + hashtable_iterator_free(iter); + } + } + else + { + skygw_log_write(LE, "Error: hashtable_iterator creation failed. " + "This is caused by a memory allocation failure."); + rval = -1; + } } - - resultset_add_column(sl.rset,"Database",MYSQL_DATABASE_MAXLEN,COL_TYPE_VARCHAR); - resultset_add_column(sl.rset,"Server",MYSQL_DATABASE_MAXLEN,COL_TYPE_VARCHAR); - resultset_stream_mysql(sl.rset,rses->rses_client_dcb); - resultset_free(sl.rset); - hashtable_iterator_free(iter); - return 0; + spinlock_release(&rses->shardmap->lock); + return rval; } /** @@ -4514,10 +4574,53 @@ void write_error_to_client(DCB* dcb, int errnum, const char* mysqlstate, const c */ bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses) { - char* target; + bool rval = false; + char* target = NULL; - if ((target = hashtable_fetch(router_cli_ses->dbhash, - router_cli_ses->connect_db)) == NULL) + spinlock_acquire(&router_cli_ses->shardmap->lock); + if(router_cli_ses->shardmap->state == SHMAP_READY) + { + target = hashtable_fetch(router_cli_ses->shardmap->hash, router_cli_ses->connect_db); + } + spinlock_release(&router_cli_ses->shardmap->lock); + + if (target) + { + /* Send a COM_INIT_DB packet to the server with the right database + * and set it as the client's active database */ + + unsigned int qlen = strlen(router_cli_ses->connect_db); + GWBUF* buffer = gwbuf_alloc(qlen + 5); + + if (buffer) + { + gw_mysql_set_byte3((unsigned char*) buffer->start, qlen + 1); + gwbuf_set_type(buffer, GWBUF_TYPE_MYSQL); + *((unsigned char*) buffer->start + 3) = 0x0; + *((unsigned char*) buffer->start + 4) = 0x2; + memcpy(buffer->start + 5, router_cli_ses->connect_db, qlen); + DCB* dcb = NULL; + + if (get_shard_dcb(&dcb, router_cli_ses, target)) + { + dcb->func.write(dcb, buffer); + skygw_log_write(LOGFILE_DEBUG, "schemarouter: USE '%s' sent to %s for session %p", + router_cli_ses->connect_db, + target, + router_cli_ses->rses_client_dcb->session); + rval = true; + } + else + { + skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Couldn't find target DCB for '%s'.", target); + } + } + else + { + skygw_log_write_flush(LOGFILE_ERROR, "Error : Buffer allocation failed."); + } + } + else { /** Unknown database, hang up on the client*/ skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Connecting to a non-existent database '%s'", @@ -4526,50 +4629,16 @@ bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses) sprintf(errmsg, "Unknown database '%s'", router_cli_ses->connect_db); if (router_cli_ses->rses_config.debug) { - sprintf(errmsg + strlen(errmsg), " ([%lu]: DB not found on connect)", router_cli_ses->rses_client_dcb->session->ses_id); + sprintf(errmsg + strlen(errmsg), " ([%lu]: DB not found on connect)", + router_cli_ses->rses_client_dcb->session->ses_id); } write_error_to_client(router_cli_ses->rses_client_dcb, SCHEMA_ERR_DBNOTFOUND, SCHEMA_ERRSTR_DBNOTFOUND, errmsg); - return false; } - /* Send a COM_INIT_DB packet to the server with the right database - * and set it as the client's active database */ - - unsigned int qlen; - GWBUF* buffer; - - qlen = strlen(router_cli_ses->connect_db); - buffer = gwbuf_alloc(qlen + 5); - if (buffer == NULL) - { - skygw_log_write_flush(LOGFILE_ERROR, "Error : Buffer allocation failed."); - return false; - } - - gw_mysql_set_byte3((unsigned char*) buffer->start, qlen + 1); - gwbuf_set_type(buffer, GWBUF_TYPE_MYSQL); - *((unsigned char*) buffer->start + 3) = 0x0; - *((unsigned char*) buffer->start + 4) = 0x2; - memcpy(buffer->start + 5, router_cli_ses->connect_db, qlen); - DCB* dcb = NULL; - - if (get_shard_dcb(&dcb, router_cli_ses, target)) - { - dcb->func.write(dcb, buffer); - skygw_log_write(LOGFILE_DEBUG, "schemarouter: USE '%s' sent to %s for session %p", - router_cli_ses->connect_db, - target, - router_cli_ses->rses_client_dcb->session); - } - else - { - skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Couldn't find target DCB for '%s'.", target); - return false; - } - return true; + return rval; } void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses) @@ -4684,3 +4753,75 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses, *wbuf = writebuf; return mapped ? 1 : 0; } + +/** + * Replace a shard map with another one. This function copies the contents of + * the source shard map to the target and frees the source memory. + * @param target Target shard map to replace + * @param source Source shard map to use + */ +void replace_shard_map(shard_map_t **target, shard_map_t **source) +{ + shard_map_t *tgt = *target; + shard_map_t *src = *source; + tgt->last_updated = src->last_updated; + tgt->state = src->state; + hashtable_free(tgt->hash); + tgt->hash = src->hash; + free(src); + *source = NULL; +} + +/** + * Synchronize the router client session shard map with the global shard map for + * this user. + * + * If the router doesn't have a shard map for this user then the current shard map + * of the client session is added to the router. If the shard map in the router is + * out of date, its contents are replaced with the contents of the current client + * session. If the router has a usable shard map, the current shard map of the client + * is discarded and the router's shard map is used. + * @param client Router session + */ +void synchronize_shard_map(ROUTER_CLIENT_SES *client) +{ + spinlock_acquire(&client->router->lock); + + client->router->stats.shmap_cache_miss++; + + shard_map_t *map = hashtable_fetch(client->router->shard_maps, + client->rses_client_dcb->user); + if (map) + { + spinlock_acquire(&map->lock); + if (map->state == SHMAP_STALE) + { + replace_shard_map(&map, &client->shardmap); + } + else if (map->state != SHMAP_READY) + { + skygw_log_write(LE, "Warning: Shard map state is not ready but" + "it is in use. Replacing it with a newer one."); + replace_shard_map(&map, &client->shardmap); + } + else + { + /** + * Another thread has already updated the shard map for this user + */ + hashtable_free(client->shardmap->hash); + free(client->shardmap); + } + spinlock_release(&map->lock); + client->shardmap = map; + } + else + { + hashtable_add(client->router->shard_maps, + client->rses_client_dcb->user, + client->shardmap); + ss_dassert(hashtable_fetch(client->router->shard_maps, + client->rses_client_dcb->user) == client->shardmap); + } + spinlock_release(&client->router->lock); +} diff --git a/server/modules/routing/schemarouter/sharding_common.c b/server/modules/routing/schemarouter/sharding_common.c index cf6bf2c12..e68d35a2c 100644 --- a/server/modules/routing/schemarouter/sharding_common.c +++ b/server/modules/routing/schemarouter/sharding_common.c @@ -18,11 +18,6 @@ #include -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; - /** * Extract the database name from a COM_INIT_DB or literal USE ... query. * @param buf Buffer with the database change query @@ -100,17 +95,17 @@ void create_error_reply(char* fail_str,DCB* dcb) } /** - * Read new database name from MYSQL_COM_INIT_DB packet or a literal USE ... COM_QUERY packet, check that it exists - * in the hashtable and copy its name to MYSQL_session. + * Read new database name from MYSQL_COM_INIT_DB packet or a literal USE ... COM_QUERY + * packet, check that it exists in the hashtable and copy its name to MYSQL_session. * - * @param mysql_session The MySQL session structure + * @param dest Destination where the database name will be written * @param dbhash Hashtable containing valid databases * @param buf Buffer containing the database change query * * @return true if new database is set, false if non-existent database was tried * to be set */ -bool change_current_db(MYSQL_session* mysql_session, +bool change_current_db(char* dest, HASHTABLE* dbhash, GWBUF* buf) { @@ -139,7 +134,7 @@ bool change_current_db(MYSQL_session* mysql_session, } else { - strncpy(mysql_session->db,db,MYSQL_DATABASE_MAXLEN); + strcpy(dest,db); skygw_log_write(LOGFILE_TRACE,"change_current_db: database is on server: '%s'.",target); succp = true; goto retblock; @@ -151,7 +146,8 @@ bool change_current_db(MYSQL_session* mysql_session, skygw_log_write_flush(LOGFILE_ERROR, "change_current_db: failed to change database: Query buffer too large"); skygw_log_write_flush(LOGFILE_TRACE, - "change_current_db: failed to change database: Query buffer too large [%d bytes]",GWBUF_LENGTH(buf)); + "change_current_db: failed to change database: " + "Query buffer too large [%ld bytes]", GWBUF_LENGTH(buf)); succp = false; goto retblock; } diff --git a/server/modules/routing/schemarouter/shardrouter.c b/server/modules/routing/schemarouter/shardrouter.c index 9efcd20e9..c0279395b 100644 --- a/server/modules/routing/schemarouter/shardrouter.c +++ b/server/modules/routing/schemarouter/shardrouter.c @@ -46,10 +46,6 @@ MODULE_INFO info = { }; -/** Defined in log_manager.cc */ -extern int lm_enabled_logfiles_bitmask; -extern size_t log_ses_count[]; -extern __thread log_info_t tls_log_info; /** * @file shardrouter.c * @@ -63,6 +59,7 @@ extern __thread log_info_t tls_log_info; * * Date Who Description * 20/01/2015 Markus Mäkelä/Vilho Raatikka Initial implementation + * 09/09/2015 Martin Brampton Modify error handler * * @endverbatim */ @@ -119,7 +116,7 @@ static route_target_t get_shard_route_target( bool trx_active, HINT* hint); -static uint8_t getCapabilities(ROUTER* inst, void* router_session); +static int getCapabilities(); void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state); void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state); @@ -971,8 +968,8 @@ createInstance(SERVICE *service, char **options) { skygw_log_write(LOGFILE_ERROR, "Error : Memory reallocation failed."); LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, "shardrouter.c: realloc returned NULL. " - "service count[%d] buffer size [%u] tried to allocate [%u]", - sz, sizeof(SERVICE*)*(sz), sizeof(SERVICE*)*(sz * 2)))); + "service count[%d] buffer size [%lu] tried to allocate [%lu]", + sz, sizeof(SERVICE*) * (sz), sizeof(SERVICE*) * (sz * 2)))); free(res_svc); free(router); return NULL; @@ -1161,9 +1158,6 @@ newSession( free(dummy_upstream); } - - /** Copy backend pointers to router session. */ - client_rses->rses_capabilities = RCAP_TYPE_STMT_INPUT; router->stats.n_sessions += 1; /** @@ -1698,7 +1692,7 @@ routeQuery(ROUTER* instance, if(packet_type == MYSQL_COM_INIT_DB) { - if(!(change_successful = change_current_db(router_cli_ses->rses_mysql_session, + if(!(change_successful = change_current_db(router_cli_ses->current_db, router_cli_ses->dbhash, querybuf))) { @@ -2471,23 +2465,6 @@ execute_sescmd_in_backend(SUBSERVICE* subsvc) rc = SESSION_ROUTE_QUERY(subsvc->session,sescmd_cursor_clone_querybuf(scur)); break; - case MYSQL_COM_INIT_DB: - { - /** - * Record database name and store to session. - */ - GWBUF* tmpbuf; - MYSQL_session* data; - unsigned int qlen; - - data = subsvc->session->data; - tmpbuf = scur->scmd_cur_cmd->my_sescmd_buf; - qlen = MYSQL_GET_PACKET_LEN((unsigned char*) tmpbuf->start); - memset(data->db, 0, MYSQL_DATABASE_MAXLEN + 1); - strncpy(data->db, tmpbuf->start + 5, qlen - 1); - rc = SESSION_ROUTE_QUERY(subsvc->session,sescmd_cursor_clone_querybuf(scur)); - } - /** Fallthrough */ case MYSQL_COM_QUERY: default: /** @@ -2591,27 +2568,12 @@ mysql_sescmd_get_property( } /** - * Return rc, rc < 0 if router session is closed. rc == 0 if there are no - * capabilities specified, rc > 0 when there are capabilities. + * Return RCAP_TYPE_STMT_INPUT */ -static uint8_t -getCapabilities(ROUTER* inst, - void* router_session) +static int +getCapabilities() { - ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *) router_session; - uint8_t rc; - - if(!rses_begin_locked_router_action(rses)) - { - rc = 0xff; - goto return_rc; - } - rc = rses->rses_capabilities; - - rses_end_locked_router_action(rses); - -return_rc: - return rc; + return RCAP_TYPE_STMT_INPUT; } /** @@ -2793,8 +2755,8 @@ return_succp: * @param router_session The router session * @param errmsgbuf The error message to reply * @param backend_dcb The backend DCB - * @param action The action: REPLY, REPLY_AND_CLOSE, NEW_CONNECTION - * @param succp Result of action. + * @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT + * @param succp Result of action: true if router can continue * * Even if succp == true connecting to new slave may have failed. succp is to * tell whether router has enough master/slave connections to continue work. @@ -2811,10 +2773,8 @@ handleError( SESSION* session; ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *) router_session; - if(action == ERRACT_RESET) - return; - CHK_DCB(backend_dcb); + /** Don't handle same error twice on same DCB */ if(backend_dcb->dcb_errhandle_called) { @@ -2830,38 +2790,39 @@ handleError( if(session == NULL || rses == NULL) { - if(succp) - *succp = false; - return; - } - CHK_SESSION(session); - CHK_CLIENT_RSES(rses); - - switch(action) - { - case ERRACT_NEW_CONNECTION: - { - if(!rses_begin_locked_router_action(rses)) - { - *succp = false; - return; - } - - rses_end_locked_router_action(rses); - break; - } - - case ERRACT_REPLY_CLIENT: - { - - *succp = false; /*< no new backend servers were made available */ - break; - } - - default: *succp = false; - break; } + else + { + CHK_SESSION(session); + CHK_CLIENT_RSES(rses); + + switch(action) + { + case ERRACT_NEW_CONNECTION: + { + if(!rses_begin_locked_router_action(rses)) + { + *succp = false; + break; + } + + rses_end_locked_router_action(rses); + break; + } + + case ERRACT_REPLY_CLIENT: + { + *succp = false; /*< no new backend servers were made available */ + break; + } + + default: + *succp = false; + break; + } + } + dcb_close(backend_dcb); } diff --git a/server/modules/routing/testroute.c b/server/modules/routing/testroute.c index 968e3892c..b0d963601 100644 --- a/server/modules/routing/testroute.c +++ b/server/modules/routing/testroute.c @@ -35,7 +35,7 @@ static void freeSession(ROUTER *instance, void *session); static int routeQuery(ROUTER *instance, void *session, GWBUF *queue); static void clientReply(ROUTER *instance, void *session, GWBUF *queue,DCB*); static void diagnostic(ROUTER *instance, DCB *dcb); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); +static int getCapabilities (); static void handleError( ROUTER *instance, void *router_session, @@ -166,9 +166,7 @@ diagnostic(ROUTER *instance, DCB *dcb) { } -static uint8_t getCapabilities( - ROUTER* inst, - void* router_session) +static int getCapabilities() { return 0; } diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 10811abae..2b467f34e 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,2 +1,3 @@ add_library(utils skygw_utils.cc ../server/core/atomic.c) target_link_libraries(utils stdc++ ${PCRE2_LIBRARIES}) +add_dependencies(utils pcre2) diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index f9b586a0c..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 @@ -206,16 +206,16 @@ typedef enum skygw_chk_t { ((s) == DCB_STATE_LISTENING ? "DCB_STATE_LISTENING" : \ ((s) == DCB_STATE_DISCONNECTED ? "DCB_STATE_DISCONNECTED" : \ ((s) == DCB_STATE_NOPOLLING ? "DCB_STATE_NOPOLLING" : \ - ((s) == DCB_STATE_FREED ? "DCB_STATE_FREED" : \ - ((s) == DCB_STATE_ZOMBIE ? "DCB_STATE_ZOMBIE" : \ - ((s) == DCB_STATE_UNDEFINED ? "DCB_STATE_UNDEFINED" : "DCB_STATE_UNKNOWN")))))))) + ((s) == DCB_STATE_ZOMBIE ? "DCB_STATE_ZOMBIE" : \ + ((s) == DCB_STATE_UNDEFINED ? "DCB_STATE_UNDEFINED" : "DCB_STATE_UNKNOWN"))))))) #define STRSESSIONSTATE(s) ((s) == SESSION_STATE_ALLOC ? "SESSION_STATE_ALLOC" : \ - ((s) == SESSION_STATE_READY ? "SESSION_STATE_READY" : \ - ((s) == SESSION_STATE_LISTENER ? "SESSION_STATE_LISTENER" : \ - ((s) == SESSION_STATE_LISTENER_STOPPED ? "SESSION_STATE_LISTENER_STOPPED" : \ - (s) == SESSION_STATE_ROUTER_READY ? "SESSION_STATE_ROUTER_READY":\ - "SESSION_STATE_UNKNOWN")))) + ((s) == SESSION_STATE_DUMMY ? "SESSION_STATE_DUMMY" : \ + ((s) == SESSION_STATE_READY ? "SESSION_STATE_READY" : \ + ((s) == SESSION_STATE_LISTENER ? "SESSION_STATE_LISTENER" : \ + ((s) == SESSION_STATE_LISTENER_STOPPED ? "SESSION_STATE_LISTENER_STOPPED" : \ + (s) == SESSION_STATE_ROUTER_READY ? "SESSION_STATE_ROUTER_READY":\ + "SESSION_STATE_UNKNOWN"))))) #define STRPROTOCOLSTATE(s) ((s) == MYSQL_ALLOC ? "MYSQL_ALLOC" : \ ((s) == MYSQL_PENDING_CONNECT ? "MYSQL_PENDING_CONNECT" : \ @@ -408,9 +408,6 @@ typedef enum skygw_chk_t { lf->lf_name_suffix != NULL && \ lf->lf_full_file_name != NULL, \ "NULL in name variable\n"); \ - ss_info_dassert(lf->lf_id >= LOGFILE_FIRST && \ - lf->lf_id <= LOGFILE_LAST, \ - "Invalid logfile id\n"); \ ss_debug( \ (lf->lf_chk_top != CHK_NUM_LOGFILE || \ lf->lf_chk_tail != CHK_NUM_LOGFILE ? \ diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 95391039e..a082d1cf8 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -33,6 +33,7 @@ #include #include "skygw_utils.h" #include +#include #include #if defined(MLIST) @@ -1276,7 +1277,7 @@ void acquire_lock( misscount += 1; if (misscount > 10) { - ts1.tv_nsec = (rand()%misscount)*1000000; + ts1.tv_nsec = (random_jkiss()%misscount)*1000000; nanosleep(&ts1, NULL); } }