Merge branch '2.0' into develop

This commit is contained in:
Johan Wikman 2016-09-09 15:12:58 +03:00
commit d7f79942be
48 changed files with 1658 additions and 547 deletions

View File

@ -293,3 +293,17 @@ add_custom_target(generate_html
-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}
-P generate-html.cmake
COMMENT "Generating HTML files" VERBATIM)
if(PACKAGE)
message(STATUS "You can install startup scripts and system configuration files for MaxScale by running the 'postinst' shell script located at ${CMAKE_INSTALL_PREFIX}.")
message(STATUS "To remove these installed files, run the 'postrm' shell script located in the same folder.")
endif()
# NOTE: If you make changes here, ensure they are compatible with the
# situation in gwdirs.h.in.
if (NOT CMAKE_INSTALL_PREFIX EQUAL "/usr")
install(DIRECTORY DESTINATION var/cache/maxscale)
install(DIRECTORY DESTINATION var/log/maxscale)
install(DIRECTORY DESTINATION var/run/maxscale)
endif()

View File

@ -7,29 +7,52 @@ to specific plugins or to MariaDB MaxScale as a whole this document is divided
into a number of sections, the purpose of which are to isolate the
limitations to the components which illustrate them.
## Limitations in the MariaDB MaxScale core
# Limitations in the MariaDB MaxScale core
This section describes the limitations that are common to all
configuration of plugins with MariaDB MaxScale.
## Limitations with MySQL Protocol support
## Crash if one of several listeners for a Service fails as startup
If a service has multiple listeners and one of those listeners fails
at startup, MariaDB MaxScale will crash.
A typical reason for a listener to fail is that it has been configured
with a non-existing socket path or a port that MariaDB MaxScale is not allowed
to use.
Workaround: Ensure that socket paths and ports are valid.
Issues [MXS-710](https://jira.mariadb.org/browse/MXS-710) and
[MXS-711](https://jira.mariadb.org/browse/MXS-711) relate to this.
# Protocol limitations
## Limitations with MySQL Protocol support (MySQLClient)
Compression is not included in MySQL server handshake
## Limitations with Galera Cluster Monitoring
# Monitor limitations
A server can only be monitored by one monitor. If multiple monitors monitor the
same server, the state of the server is non-deterministic.
## Limitations with Galera Cluster Monitoring (galeramon)
The default master selection is based only on MIN(wsrep_local_index). This
can be influenced with the server priority mechanic described in the
[Galera Monitor](../Monitors/Galera-Monitor.md) manual.
## Limitations in the connection router
# Router limitations
## Limitations in the connection router (readconnroute)
* If Master changes (ie. new Master promotion) during current connection
the router cannot check the change.
* Sending of binary data with LOAD DATA LOCAL INFILE is not supported
## Limitations in the Read/Write Splitter
## Limitations in the Read/Write Splitter (readwritesplit)
Read queries are routed to the master server in the following situations:
@ -42,6 +65,20 @@ Read queries are routed to the master server in the following situations:
* if there are multiple statements inside one query e.g.
`INSERT INTO ... ; SELECT LAST_INSERT_ID();`
### Backend write timeout handling
The backend connections opened by the readwritesplit will not be kept alive if
they aren't used. To keep all of the connections alive, a session command must
be periodically executed (for example `SET @a = 1;`).
If the backend server is configured with a low
[wait_timeout](https://mariadb.com/kb/en/mariadb/server-system-variables/#wait_timeout),
it is possible that connections get closed during long sessions. It is
recommended to set the wait_timeout to a high value and let MaxScale handle the
client timeouts. This can be achieved by using the
[_connection_timeout_](../Getting-Started/Configuration-Guide.md#connection_timeout)
parameter for the service.
### Limitations in multi-statement handling
When a multi-statement query is executed through the readwritesplit
@ -163,21 +200,7 @@ possible that a slave fails to execute something because of some
non-fatal, temporary failure, while the execution of the same command
succeeds in other backends.
## Authentication Related Limitations
* MariaDB MaxScale can not manage authentication that uses wildcard matching in hostnames
in the mysql.user table of the backend database. The only wildcards that can be
used are in IP address entries.
* MySQL old style passwords are not supported. MySQL versions 4.1 and newer use
a new authentication protocol which does not support pre-4.1 style passwords.
* When users have different passwords based on the host from which they connect
MariaDB MaxScale is unable to determine which password it should use to connect to the
backend database. This results in failed connections and unusable usernames
in MariaDB MaxScale.
## Schemarouter limitations
## Schemarouter limitations (schemarouter)
The schemarouter router currently has some limitations due to the nature of
the sharding implementation and the way the session variables are detected
@ -206,12 +229,7 @@ and routed. Here is a list of the current limitations.
the query will be routed to the first available server. This possibly
returns an error about database rights instead of a missing database.
## Dbfwfilter limitations
The Database Firewall filter does not support multi-statements. Using them
will result in an error being sent to the client.
## Avrorouter limitations
## Avrorouter limitations (avrorouter)
The avrorouter does not support the following data types and conversions.
@ -221,3 +239,30 @@ The avrorouter does not support the following data types and conversions.
The avrorouter does not do any crash recovery. This means that the avro files
need to be truncated to valid block lengths before starting the avrorouter.
# Authenticator limitations
## MySQL Authentication Related Limitations (MySQLAuth)
* MariaDB MaxScale can not manage authentication that uses wildcard matching in hostnames
in the mysql.user table of the backend database. The only wildcards that can be
used are in IP address entries.
* MySQL old style passwords are not supported. MySQL versions 4.1 and newer use
a new authentication protocol which does not support pre-4.1 style passwords.
* When users have different passwords based on the host from which they connect
MariaDB MaxScale is unable to determine which password it should use to connect to the
backend database. This results in failed connections and unusable usernames
in MariaDB MaxScale.
# Filter limitations
Filters are not guaranteed to receive complete MySQL packets if they are used
with the readconnroute router. This can be fixed by using the readwritesplit
router.
## Database Firewall limitations (dbfwfilter)
The Database Firewall filter does not support multi-statements. Using them
will result in an error being sent to the client.

View File

@ -0,0 +1,56 @@
# Installing MariaDB MaxScale using a tarball
MariaDB MaxScale is also made available as a tarball, which is named like `maxscale-X.Y.X.tar.gz` where `X.Y.Z` is the same as the corresponding version, e.g. `maxscale-2.0.1.tar.gz`.
The tarball has been built with the assumption that it will be installed in `/usr/local`. However, it is possible to install it in any directory, but in that case MariaDB MaxScale must be invoked with a flag.
## Installing as root in `/usr/local`
If you have root access to the system you probably want to install MariaDB MaxScale under the user and group `maxscale`.
The required steps are as follows:
$ sudo groupadd maxscale
$ sudo useradd -g maxscale maxscale
$ cd /usr/local
$ sudo tar -xzvf maxscale-X.Y.Z.tar.gz
$ sudo ln -s maxscale-X.Y.Z maxscale
$ cd maxscale
$ chown -R maxscale var
Creating the symbolic link is necessary, since MariaDB MaxScale has been built with with the assumption that its base-directory is `/usr/local/maxscale`. It also makes it easy to switch between different versions of MariaDB MaxScale that have been installed side by side in `/usr/local`; just make the symbolic link point to another installation.
The following step is to create the MariaDB MaxScale configuration file `/etc/maxscale.cnf`. The file `etc/maxscale.cnf.template` can be used as a base. Please refer to [Configuration Guide](Configuration-Guide.md) for details.
When the configuration file has been created, MariaDB MaxScale can be started.
$ sudo bin/maxscale --user=maxscale -d
The `-d` flag causes maxscale _not_ to turn itself into a daemon, which is adviseable the first time MariaDB MaxScale is started, as it makes it easier to spot problems.
If you want to place the configuration file somewhere else but in `/etc` you can invoke MariaDB MaxScale with the `--config` flag, for instance, `--config=/usr/local/maxscale/etc/maxscale.cnf`.
## Installing in any Directory
Enter a directory where you have the right to create a subdirectory. Then do as follows.
$ tar -xzvf maxscale-X.Y.Z.tar.gz
The next step is to create the MaxScale configuration file `maxscale-X.Y.Z/etc/maxscale.cnf`. The file `maxscale-X.Y.Z/etc/maxscale.cnf.template` can be used as a base. Please refer to [Configuration Guide](Configuration-Guide.md) for details.
When the configuration file has been created, MariaDB MaxScale can be started.
$ cd maxscale-X.Y.Z
$ LD_LIBRARY_PATH=lib/maxscale bin/maxscale -d --basedir=.
With the flag `--basedir`, MariaDB MaxScale is told where the `bin`, `etc`, `lib`
and `var` directories are found. Unless it is specified, MariaDB MaxScale assumes
the directories are found in `/usr/local/maxscale` and the configuration
file in `/etc`.
It is also possible to specify the directories and the location of the
configuration file individually. Invoke MaxScale like
$ LD_LIBRARY_PATH=lib/maxscale bin/maxscale --help
to find out the appropriate flags.

View File

@ -34,6 +34,10 @@ service maxscale start
An example configuration file is installed into the `/etc/` folder. This file should be changed according to your needs.
## Install MariaDB MaxScale Using a Tarball
MaxScale can also be installed using a tarball. That may be required if you are using a Linux distribution for which there exist no installation package or if you want to install many different MaxScale versions side by side. For instructions on how to do that, please refer to [Install MariaDB MaxScale using a Tarball](Install-MariaDB-MaxScale-Using-a-Tarball.md).
## Building MariaDB MaxScale From Source Code
Alternatively you may download the MariaDB MaxScale source and build your own binaries. To do this, refer to the separate document [Building MariaDB MaxScale from Source Code](Building-MaxScale-from-Source-Code.md)

View File

@ -125,3 +125,8 @@ Example:
```
QUERY-TRANSACTION 0-14-1245
```
## Example Client
MaxScale includes an example CDC client application written in Python 3. You can
find the source code for it [in the MaxScale repository](https://github.com/mariadb-corporation/MaxScale/tree/2.0/server/modules/protocol/examples/cdc.py).

View File

@ -21,16 +21,15 @@ password=cdc_password
## Creating new CDC users
```
bash$ cdc_users [-h] USER PASSWORD
bash$ cdc_users.py [-h] USER PASSWORD
```
The output of this command should be appended to the _cdcusers_ file at
`/var/lib/maxscale/<service name>/`.
```
bash$ cdc_users user1 pass1 >> /var/lib/maxscale/avro-service/cdcusers
bash$ cdc_users.py user1 pass1 >> /var/lib/maxscale/avro-service/cdcusers
```
Users can be deleted by removing the related rows in 'cdcusers' file. For
more details on the format of the _cdcusers_ file, read the [CDC Protocol documentation](CDC.md).

View File

@ -3,6 +3,7 @@
# The Maxscale Administrative & Monitoring Client Application
- [Overview](#overview)
- [Configuring MariaDB MaxScale for MaxAdmin](#configuring)
- [Running MaxAdmin](#running)
- [Working With Administration Interface Users](#interface)
- [Getting Help](#help)
@ -14,7 +15,6 @@
- [Working with Monitors](#monitors)
- [MariaDB MaxScale Status Commands](#statuscommands)
- [Administration Commands](#admincommands)
- [Configuring MariaDB MaxScale to Accept MaxAdmin Connections](#connections)
- [Tuning MariaDB MaxScale](#tuning)
<a name="overview"></a>
@ -30,53 +30,145 @@ MaxAdmin supports
* Execution of command scripts
<a name="configuring"></a>
# Configuring MariaDB MaxScale for MaxAdmin
In order to be able to use MaxAdmin, MariaDB MaxScale must be configured for it.
There are two ways MaxAdmin can connect to to MaxScale.
* Using a Unix domain socket.
* Using a hostname and port.
The first alternative is introduced in MaxScale 2.0 and is the secure and
recommended way. The second alternative is available for backward compatibility,
but is _insecure_ and **deprecated** and _will be removed in a future version of
MaxScale_.
An example configuration looks as follows:
```
[MaxAdmin]
type=service
router=cli
[MaxAdmin Unix Listener]
type=listener
service=MaxAdmin
protocol=maxscaled
socket=default
[MaxAdmin Inet Listener]
type=listener
service=MaxAdmin
protocol=maxscaled
address=localhost
port=6603
```
In the configuration above, two listeners are created; one listening on the default
Unix domain socket and one listening on the default port.
Which approach is used has other implications than just how the communication between
MaxAdmin and MariaDB MaxScale is handled. In the former case, the authorization is
based upon the Linux identity and in the latter case on explicitly created user
accounts that have **no** relationship to the Linux accounts.
Note that if the socket path or port are changed, then MaxAdmin has to be invoked
with `-S` or `-P` respectively.
<a name="running"></a>
# Running MaxAdmin
Depending on whether MariaDB MaxScale has been configured to use Unix domain sockets
or internet sockets, MaxAdmin needs to be invoked slightly differently.
If Unix domain sockets are used, then MaxAdmin needs no additional arguments:
alice@host$ maxadmin
MaxAdmin>
The above implies that the Linux user _alice_ has been enabled to use MaxAdmin.
If internet sockets are used, then either the host, port, user or password has
to be specified explicitly:
alice@host$ maxadmin -u maxscale-admin
Password:
MaxScale>
If Unix domain sockets are used, then initially only `root` has access. MaxAdmin
usage can subsequently be enabled for other Linux users.
The MaxAdmin client application may be run in two different modes, either as an interactive command shell for executing commands against MariaDB MaxScale or by passing commands on the MaxAdmin command line itself.
<a name="interface"></a>
# Working With Administration Interface Users
MaxScale communicates with MariaDB MaxScale using UNIX domain sockets, which means that it can only be used on the very host where MariaDB MaxScale is running. Initially MaxAdmin can connect only when run as `root`. Other Linux users can subsequently be allowed access.
**NOTE**: Remote access with -h and -P options is no longer supported.
## What Users Have Been Defined?
In order to see the current users (UNIX users) that have been defined for the administration interface use the command _show users_.
In order to see the Linux users for whom MaxAdmin usage has been enabled and
any explicitly created accounts, use the command _show users_.
MaxScale> show users
Administration interface users:
User names: vilho, dba, massi, mark
Enabled Linux accounts (secure) : alice, bob, cecil
Created network accounts (insecure): maxscale-admin
MaxScale>
Please note `root` will not be shown.
Please note that `root` will not be shown.
MaxScale> show users
Administration interface users:
No administration users have been defined.
MaxScale>
## Enabling a Linux account
To enable MaxAdmin usage for a particular Linux account, use the command _enable account_.
This command is passed a user name, which should be the same as that of an existing Linux user.
MaxScale> enable account bob
Note that it is not checked that the provided name indeed corresponds to an existing
Linux account, so it is possible to enable an account that does not exist yet.
Note also that it is possible to enable a Linux account irrespective of how MaxAdmin
has connected to MariaDB MaxScale. That is, the command is not restricted to MaxAdmin
users connecting over a Unix domain socket.
## Disabling a Linux account
To disable MaxAdmin usage for a particular Linux account, use the command _disable account_.
This command is passed a user name, which should be a Linux user for whom MaxAdmin usage
earlier has been enabled.
MaxScale> disable account bob
Note also that it is possible to disable a Linux account irrespective of how MaxAdmin
has connected to MariaDB MaxScale. That is, the command is not restricted to MaxAdmin
users connecting over a Unix domain socket.
Note that it is possible to disable the current user, but that will only affect the
next attempt to use MaxAdmin. `root` cannot be removed.
## Add A New User
To add a new administrative user to the MariaDB MaxScale server use the command _add user_. This command is passed a user name, which should be the same as that of an existing Linux user.
To add a new MaxAdmin user to be used when MaxAdmin connects over an internet socket,
use the command _add user_. This command is passed a user name and a password.
MaxScale> add user maria
User maria has been successfully added.
MaxScale> add user maxscale-admin secretpwd
User maxscale-admin has been successfully added.
MaxScale>
Note that a user that is given the rights to use MaxAdmin, has the very same rights `root` has, including adding and removing users.
Note that there is no difference in rights between an enabled Linux account and an
explicitly created user.
## Delete A User
To remove a user the command _remove user_ is used and it is simply invoked with the user to be removed.
To remove a user the command _remove user_ is used and it is invoked with the
username and password.
MaxScale> remove user maria
User maria has been successfully removed.
MaxScale> remove user maxscale-admin secretpwd
User maxscale-admin has been successfully removed.
MaxScale>
Note that it is possible to remove the current user, but that will only affect the next attempt to use MaxAdmin. `root` cannot be removed.
Note that it is possible to remove the current user, but that will only affect the
next attempt to use MaxAdmin.
# Command Line Switches
@ -93,6 +185,26 @@ The MaxAdmin command accepts a number of switches
<td>--socket=...</td>
<td>The UNIX domain socket path that MaxAdmin will use to connect to the MariaDB MaxScale server. If no -S option is given then the default socket path /tmp/maxadmin.sock will be used.</td>
</tr>
<tr>
<td>-u user</td>
<td>--user=...</td>
<td>Sets the username that will be used for the MaxScale connection. If no -u option is passed on the MaxAdmin command line then the default username of ‘admin’ will be used.</td>
</tr>
<tr>
<td>-p password</td>
<td>--password=...</td>
<td>Sets the user password that will be used. If no -p option is passed on the command line then MaxAdmin will prompt for interactive entry of the password.</td>
</tr>
<tr>
<td>-h hostname</td>
<td>--hostname=...</td>
<td>The hostname of the MaxScale server to connect to. If no -h option is passed on the command line then MaxAdmin will attempt to connect to the host ‘localhost’.</td>
</tr>
<tr>
<td>-P port</td>
<td>--port=...</td>
<td>The port that MaxAdmin will use to connect to the MaxScale server. if no -P option is given then the default port of 6603 will be used.</td>
</tr>
<tr>
<td>-?</td>
<td>--help</td>
@ -859,28 +971,6 @@ A command, _reload config_, is available that will cause MariaDB MaxScale to rel
The MariaDB MaxScale server may be shutdown using the _shutdown maxscale_ command.
<a name="connections"></a>
# Configuring MariaDB MaxScale to Accept MaxAdmin Connections
In order to enable the use of MaxAdmin, the service CLI with an accompanying listener must be added to the MariaDB MaxScale configuration file.
The default entries required are shown below.
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
#socket=/somepath/maxadmin.socket
**NOTE**: As the protocol maxscaled only supports UNIX domain sockets it is thus not possible to connect remotely to MariaDB MaxScale.
In the example, the default socket path (/tmp/maxadmin.sock) is used. It can be changed to an specific path and in that case MaxAdmin must be invoked with the -S option.
<a name="tuning"></a>
# Tuning MariaDB MaxScale

View File

@ -126,4 +126,4 @@ The source code of MaxScale is tagged at GitHub with a tag, which is identical
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
The source code is available [here](https://github.com/mariadb-corporation/maxscale-bsl).
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).

View File

@ -0,0 +1,130 @@
# MariaDB MaxScale 2.0.1 Release Notes
Release 2.0.1 is a GA release.
This document describes the changes in release 2.0.1, when compared to
[release 2.0.0](MaxScale-2.0.0-Release-Notes.md).
If you are upgrading from 1.4.3, please also read the release notes
of [2.0.0](./MaxScale-2.0.0-Release-Notes.md).
For any problems you encounter, please consider submitting a bug
report at [Jira](https://jira.mariadb.org).
## Updated Features
### Starting MariaDB MaxScale
There is now a new command line parameter `--basedir=PATH` that will
cause all directory paths and the location of the configuration file
to be defined relative to that path.
For instance, invoking MariaDB MaxScale like
$ maxscale --basedir=/path/maxscale
has the same effect as invoking MariaDB MaxScale like
$ maxscale --config=/path/maxscale/etc/maxscale.cnf
--configdir=/path/maxscale/etc
--logdir=/path/maxscale/var/log/maxscale
--cachhedir=/path/maxscale/var/cache/maxscale
--libdir=/path/maxscale/lib/maxscale
--datadir=/path/maxscale/var/lib/maxscale
--execdir=/path/maxscale/bin
--language=/path/maxscale/var/lib/maxscale
--piddir=/path/maxscale/var/run/maxscale
### Password parameter
In the configuration entry for a _service_ or _monitor_, the value of
the password to be used can now be specified using `password` in addition
to `passwd`. The use of the latter will be deprecated and removed in later
releases of MaxScale.
[SomeService]
...
password=mypasswd
### Routing hint priority change
Routing hints now have the highest priority when a routing decision is made. If
there is a conflict between the original routing decision made by the
readwritesplit and the routing hint attached to the query, the routing hint
takes higher priority.
What this change means is that, if a query would normally be routed to the
master but the routing hint instructs the router to route it to the slave, it
would be routed to the slave.
**WARNING**: This change can alter the way some statements are routed and could
possibly cause data loss, corruption or inconsisteny. Please consult the [Hint
Syntax](../Reference/Hint-Syntax.md) and
[ReadWriteSplit](../Routers/ReadWriteSplit.md) documentation before using
routing hints.
### MaxAdmin Usage
In 2.0.0 (Beta), the authentication mechanism of MaxAdmin was completely
changed, so that MaxAdmin could only connect to MaxScale using a Unix domain
socket, thus _only when run on the same host_, and authorization was based
on the Unix identity. Remote access was no longer supported.
To the user this was visible so that while you in 1.4.3 had to provide
a password when starting _maxadmin_ and when adding a user
```
user@host $ maxadmin -p password
MaxAdmin> add user john johns-password
```
in 2.0.0 (Beta), where only Unix domain sockets could be used, you did not
have to provide a password neither when starting _maxadmin_, nor when adding
users
```
user@host $ maxadmin
MaxAdmin> add user john
```
as the MaxScale user corresponded to a Unix user, provided the Linux user
had been added as a user of MaxScale.
In 2.0.1 (GA) this has been changed so that the 1.4.3 behaviour is intact
but _deprecated_, and the 2.0.0 (Beta) behaviour is exposed using a new set
of commands:
```
MaxAdmin> enable account alice
MaxAdmin> disable account alice
```
Note that the way you need to invoke _maxadmin_ depends upon how MariaDB
MaxScale has been configued.
Please consult
[MaxAdmin documentation](../Reference/MaxAdmin.md) for more details.
## Bug fixes
[Here is a list of bugs fixed since the release of MaxScale 2.0.1.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20in%20(2.0.0%2C%202.0.1)%20AND%20resolved%20%3E%3D%20-21d%20ORDER%20BY%20priority%20DESC%2C%20updated%20DESC)
* [MXS-847](https://jira.mariadb.org/browse/MXS-847): server_down event is executed 8 times due to putting sever into maintenance mode
* [MXS-845](https://jira.mariadb.org/browse/MXS-845): "Server down" event is re-triggered after maintenance mode is repeated
* [MXS-842](https://jira.mariadb.org/browse/MXS-842): Unexpected / undocumented behaviour when multiple available masters from mmmon monitor
* [MXS-846](https://jira.mariadb.org/browse/MXS-846): MMMon: Maintenance mode on slave logs error message every second
## Known Issues and Limitations
There are some limitations and known issues within this version of MaxScale.
For more information, please refer to the [Limitations](../About/Limitations.md) document.
## Packaging
RPM and Debian packages are provided for the Linux distributions supported
by MariaDB Enterprise.
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
## Source Code
The source code of MaxScale is tagged at GitHub with a tag, which is identical
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
is maxscale-X.Y.Z. Further, *master* always refers to the latest released
non-beta version.
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).

View File

@ -152,16 +152,16 @@ conversion process, delete these two files and restart MaxScale.
# Example Client
The avrorouter comes with an example client program, _cdc_, written in Python 3.
The avrorouter comes with an example client program, _cdc.py_, written in Python 3.
This client can connect to a MaxScale configured with the CDC protocol and the
avrorouter.
Before using this client, you will need to install the Python 3 interpreter and
add users to the service with the _cdc_users_ script. Fore more details about
add users to the service with the _cdc_users.py_ script. Fore more details about
the user creation, please refer to the [CDC Protocol](../Protocols/CDC.md)
and [CDC Users](../Protocols/CDC_users.md) documentation.
Read the output of `cdc --help` for a full list of supported options
Read the output of `cdc.py --help` for a full list of supported options
and a short usage description of the client program.
# Avro Schema Generator
@ -206,27 +206,27 @@ protocol=CDC
port=4001
```
Here is an example how you can query for data in JSON format using the _cdc_ Python script.
Here is an example how you can query for data in JSON format using the _cdc.py_ Python script.
It queries the table _test.mytable_ for all change records.
```
cdc --user=myuser --password=mypasswd --host=127.0.0.1 --port=4001 test.mytable
cdc.py --user=myuser --password=mypasswd --host=127.0.0.1 --port=4001 test.mytable
```
You can then combine it with the _cdc_kafka_producer_ to publish these change records to a Kafka broker.
You can then combine it with the _cdc_kafka_producer.py_ to publish these change records to a Kafka broker.
```
cdc --user=myuser --password=mypasswd --host=127.0.0.1 --port=4001 test.mytable | cdc_kafka_producer --kafka-broker 127.0.0.1:9092 --kafka-topic test.mytable
cdc.py --user=myuser --password=mypasswd --host=127.0.0.1 --port=4001 test.mytable | cdc_kafka_producer.py --kafka-broker 127.0.0.1:9092 --kafka-topic test.mytable
```
For more information on how to use these scripts, see the output of `cdc -h` and `cdc_kafka_producer -h`.
For more information on how to use these scripts, see the output of `cdc.py -h` and `cdc_kafka_producer.py -h`.
# Building Avrorouter
To build the avrorouter from source, you will need the [Avro C](https://avro.apache.org/docs/current/api/c/)
library, liblzma and sqlite3 development headers. When configuring MaxScale with
CMake, you will need to add `-DBUILD_AVRO=Y -DBUILD_CDC=Y` to build the
avrorouter and the CDC protocol module.
library, liblzma, [the Jansson library](http://www.digip.org/jansson/) and sqlite3 development headers. When
configuring MaxScale with CMake, you will need to add `-DBUILD_AVRO=Y
-DBUILD_CDC=Y` to build the avrorouter and the CDC protocol module.
For more details about building MaxScale from source, please refer to the
[Building MaxScale from Source Code](../Getting-Started/Building-MaxScale-from-Source-Code.md) document.

View File

@ -1,10 +1,13 @@
# CLI
The command line interface as used by `maxadmin`. This is a variant of the debugcli that is built slightly differently so that it may be accessed by the client application `maxadmin`. The CLI requires the use of the `maxscaled` protocol.
The command line interface as used by `maxadmin`. The _CLI_ router requires the use
of the `maxscaled` protocol.
## Configuration
There are two components to the definition required in order to run the command line interface to use with MaxAdmin; a service and a listener.
Two components are required in order to run the command line interface for use with
_maxadmin_; a service and a listener. The listener may either use a Unix domain socket
or an internet socket.
The default entries required are shown below.
@ -13,9 +16,34 @@ The default entries required are shown below.
type=service
router=cli
[CLI Listener]
# Unix Domain Socket
[CLI Unix Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
# Internet Socket
[CLI Inet Listener]
type=listener
service=CLI
protocol=maxscaled
address=localhost
port=6603
```
In the example above, two listeners have been specified; one that listens on the
default Unix domain socket and one that listens on the default port. In the latter
case, if the `address=` entry is removed, connections are allowed from any machine
on your network.
In the former case, if the value of `socket` is changed and in the latter case,
if the value of `port` is changed, _maxadmin_ must be invoked with the `-S` and
`-P` options respectively.
Note that if Unix domain sockets are used, the connection is secure, but _maxadmin_
can only be used on the same host where MariaDB MaxScale runs. If internet sockets
are used, the connection is _inherently insecure_ but _maxadmin_ can be used from
another host than the one where MariaDB MaxScale runs.
Note that the latter approach is **deprecated** and will be removed in a future
version of MariaDB MaxScale.

View File

@ -30,6 +30,11 @@ running|A server that is up and running. All servers that MariaDB MaxScale can c
If no `router_options` parameter is configured in the service definition, the router will use the default value of `running`. This means that it will load balance connections across all running servers defined in the `servers` parameter of the service.
When a connection is being created and the candidate server is being chosen, the
list of servers is processed in from first entry to last. This means that if two
servers with equal weight and status are found, the one that's listed first in
the _servers_ parameter for the service is chosen.
## Limitations
For a list of readconnroute limitations, please read the [Limitations](../About/Limitations.md) document.

View File

@ -117,9 +117,6 @@ The final stage in the configuration is to add the option service which is used
protocol=maxscaled
socket=default
**Note**: maxscaled protocol supports only UNIX domain sockets and in the example default is set.
Changing it requires maxadmin to use -S with the new path. Default /tmp/maxadmin.sock is for both maxadmin and maxscaled.
## Starting MariaDB MaxScale
Upon completion of the configuration process MariaDB MaxScale is ready to be started for the first time. This may either be done manually by running the maxscale command or via the service interface.

View File

@ -147,9 +147,6 @@ protocol=maxscaled
socket=default
```
**Note**: maxscaled protocol supports only UNIX domain sockets and in the example default is set.
Changing it requires maxadmin to use -S with the new path. Default /tmp/maxadmin.sock is for both maxadmin and maxscaled.
## Starting MariaDB MaxScale
Upon completion of the configuration process MariaDB MaxScale is ready to be started for the first time. This may either be done manually by running the maxscale command or via the service interface.

View File

@ -174,8 +174,6 @@ service=CLI
protocol=maxscaled
socket=default
```
**Note**: maxscaled protocol supports only UNIX domain sockets and in the example default is set.
Changing it requires maxadmin to use -S with the new path. Default /tmp/maxadmin.sock is for both maxadmin and maxscaled.
# Starting MariaDB MaxScale

View File

@ -133,8 +133,6 @@ service=CLI
protocol=maxscaled
socket=default
```
**Note**: maxscaled protocol supports only UNIX domain sockets and in the example default is set.
Changing it requires maxadmin to use -S with the new path. Default /tmp/maxadmin.sock is for both maxadmin and maxscaled.
# Starting MariaDB MaxScale

View File

@ -62,8 +62,7 @@ service=AdminInterface
protocol=maxscaled
socket=default
```
**Note**: maxscaled protocol supports only UNIX domain sockets and in the example default is set.
Changing it requires maxadmin to use -S with the new path. Default /tmp/maxadmin.sock is for both maxadmin and maxscaled.
## Prepare Nagios configuration files.
Assuming Nagios installed on a separated server and the plugins are in /usr/lib64/nagios/plugins and configuration files are in /etc/nagios:

View File

@ -208,7 +208,7 @@ router=cli
type=listener
service=MaxAdmin Service
protocol=maxscaled
port=6603
socket=default
```
Now we have created the MariaDB MaxScale configuration file and all we need to do is to save

View File

@ -12,14 +12,18 @@ configuration file.
## MaxAdmin
The way a user of MaxAdmin is authenticated has been completely changed.
In 2.0, MaxAdmin can only connect to MariaDB MaxScale using a domain socket, thus
_only when run on the same host_, and authorization is based upon the UNIX
identity. Remote access is no longer supported.
The default way the communication between MaxAdmin and MariaDB MaxScale is
handled has been changed from an internet socket to a Unix domain socket.
The former alternative is still available but has been _deprecated_.
When 2.0 has been installed, MaxAdmin can only be used by `root` and
other users must be added anew. Please consult
[MaxAdmin documentation](../Reference/MaxAdmin.md) for more details.
If no arguments are given to MaxAdmin, it will attempt to connect to
MariaDB MaxScale using a Unix domain socket. After the upgrade you will
need to provide at least one internet socket related flag - `-h`, `-P`,
`-u` or `-p` - to force MaxAdmin to use the internet socket approach.
E.g.
user@host $ maxadmin -u admin
## MySQL Monitor

View File

@ -27,6 +27,7 @@
* @endverbatim
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
@ -45,6 +46,7 @@
#include <locale.h>
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <version.h>
@ -60,15 +62,21 @@
#define STRERROR_BUFLEN 512
#endif
static int connectMaxScale(char *socket);
static int setipaddress(struct in_addr *a, char *p);
static int authMaxScale(int so);
#define MAX_PASSWORD_LEN 80
static int connectUsingUnixSocket(const char *socket);
static int connectUsingInetSocket(const char *hostname, const char *port,
const char *user, const char* password);
static int setipaddress(struct in_addr *a, const char *p);
static bool authUnixSocket(int so);
static bool authInetSocket(int so, const char *user, const char *password);
static int sendCommand(int so, char *cmd);
static void DoSource(int so, char *cmd);
static void DoUsage(const char*);
static int isquit(char *buf);
static void PrintVersion(const char *progname);
static void read_inifile(char **, int*);
static bool getPassword(char *password, size_t length);
#ifdef HISTORY
@ -79,10 +87,15 @@ prompt(EditLine *el __attribute__((__unused__)))
return prompt;
}
#endif
static struct option long_options[] =
{
{"host", required_argument, 0, 'h'},
{"user", required_argument, 0, 'u'},
{"password", required_argument, 0, 'p'},
{"port", required_argument, 0, 'P'},
{"socket", required_argument, 0, 'S'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, '?'},
@ -90,6 +103,10 @@ static struct option long_options[] =
{0, 0, 0, 0}
};
#define MAXADMIN_DEFAULT_HOST "localhost"
#define MAXADMIN_DEFAULT_PORT "6603"
#define MAXADMIN_DEFAULT_USER "admin"
/**
* The main for the maxadmin client
*
@ -112,7 +129,12 @@ main(int argc, char **argv)
#else
char buf[1024];
#endif
char *conn_socket = MAXADMIN_DEFAULT_SOCKET;
char *hostname = NULL;
char *port = NULL;
char *user = NULL;
char *passwd = NULL;
char *conn_socket = NULL;
char *default_socket = MAXADMIN_DEFAULT_SOCKET;
int use_emacs = 0;
int so;
int option_index = 0;
@ -120,11 +142,28 @@ main(int argc, char **argv)
read_inifile(&conn_socket, &use_emacs);
while ((c = getopt_long(argc, argv, "S:v?e",
while ((c = getopt_long(argc, argv, "h:p:P:u:S:v?e",
long_options, &option_index)) >= 0)
{
switch (c)
{
case 'h':
hostname = strdup(optarg);
break;
case 'p':
passwd = strdup(optarg);
memset(optarg, '\0', strlen(optarg));
break;
case 'P':
port = strdup(optarg);
break;
case 'u':
user = strdup(optarg);
break;
case 'S':
conn_socket = strdup(optarg);
break;
@ -138,22 +177,72 @@ main(int argc, char **argv)
break;
case '?':
DoUsage(*argv);
DoUsage(argv[0]);
exit(optopt ? EXIT_FAILURE : EXIT_SUCCESS);
}
}
/* Connet to MaxScale using UNIX domain socket */
if ((so = connectMaxScale(conn_socket)) == -1)
if ((hostname || port || user || passwd) && (conn_socket))
{
exit(1);
// Either socket or any parameters related to hostname/port.
DoUsage(argv[0]);
exit(EXIT_FAILURE);
}
/* Check for successful authentication */
if (!authMaxScale(so))
if (hostname || port || user || passwd)
{
fprintf(stderr, "Failed to connect to MaxScale, incorrect username.\n");
exit(1);
assert(!conn_socket);
if (!hostname)
{
hostname = MAXADMIN_DEFAULT_HOST;
}
if (!port)
{
port = MAXADMIN_DEFAULT_PORT;
}
if (!user)
{
user = MAXADMIN_DEFAULT_USER;
}
}
else
{
if (!conn_socket)
{
conn_socket = MAXADMIN_DEFAULT_SOCKET;
}
}
assert(!((hostname || port) && conn_socket));
if (conn_socket)
{
if ((so = connectUsingUnixSocket(conn_socket)) == -1)
{
exit(EXIT_FAILURE);
}
}
else
{
char password[MAX_PASSWORD_LEN];
if (passwd == NULL)
{
if (!getPassword(password, MAX_PASSWORD_LEN))
{
exit(EXIT_FAILURE);
}
passwd = password;
}
if ((so = connectUsingInetSocket(hostname, port, user, passwd)) == -1)
{
exit(EXIT_FAILURE);
}
}
if (optind < argc)
@ -309,44 +398,114 @@ main(int argc, char **argv)
* @return The connected socket or -1 on error
*/
static int
connectMaxScale(char *conn_socket)
connectUsingUnixSocket(const char *conn_socket)
{
struct sockaddr_un local_addr;
int so;
int keepalive = 1;
int optval = 1;
int so = -1;
if ((so = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
if ((so = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
{
struct sockaddr_un local_addr;
memset(&local_addr, 0, sizeof local_addr);
local_addr.sun_family = AF_UNIX;
strncpy(local_addr.sun_path, conn_socket, sizeof(local_addr.sun_path) - 1);
if (connect(so, (struct sockaddr *) &local_addr, sizeof(local_addr)) == 0)
{
int keepalive = 1;
if (setsockopt(so, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)))
{
fprintf(stderr, "Warning: Could not set keepalive.\n");
}
/* Client is sending connection credentials (Pid, User, Group) */
int optval = 1;
if (setsockopt(so, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == 0)
{
if (!authUnixSocket(so))
{
close(so);
so = -1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr, "Could not set SO_PASSCRED: %s\n",
strerror_r(errno, errbuf, sizeof(errbuf)));
close(so);
so = -1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr, "Unable to connect to MaxScale at %s: %s\n",
conn_socket, strerror_r(errno, errbuf, sizeof(errbuf)));
close(so);
so = -1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr, "Unable to create socket: %s\n",
strerror_r(errno, errbuf, sizeof(errbuf)));
return -1;
}
memset(&local_addr, 0, sizeof local_addr);
local_addr.sun_family = AF_UNIX;
strncpy(local_addr.sun_path, conn_socket, sizeof(local_addr.sun_path)-1);
return so;
}
if (connect(so, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
/**
* Connect to the MaxScale server
*
* @param hostname The hostname to connect to
* @param port The port to use for the connection
* @return The connected socket or -1 on error
*/
static int
connectUsingInetSocket(const char *hostname, const char *port,
const char *user, const char *passwd)
{
int so;
if ((so = socket(AF_INET, SOCK_STREAM, 0)) != -1)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
setipaddress(&addr.sin_addr, hostname);
addr.sin_port = htons(atoi(port));
if (connect(so, (struct sockaddr *) &addr, sizeof(addr)) == 0)
{
int keepalive = 1;
if (setsockopt(so, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)))
{
fprintf(stderr, "Warning: Could not set keepalive.\n");
}
if (!authInetSocket(so, user, passwd))
{
close(so);
so = -1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr, "Unable to connect to MaxScale at %s, %s: %s\n",
hostname, port, strerror_r(errno, errbuf, sizeof(errbuf)));
close(so);
so = -1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr, "Unable to connect to MaxScale at %s: %s\n",
conn_socket, strerror_r(errno, errbuf, sizeof(errbuf)));
close(so);
return -1;
}
if (setsockopt(so, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)))
{
perror("setsockopt");
}
/* Client is sending connection credentials (Pid, User, Group) */
if (setsockopt(so, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) != 0)
{
fprintf(stderr, "SO_PASSCRED failed\n");
return -1;
fprintf(stderr, "Unable to create socket: %s\n",
strerror_r(errno, errbuf, sizeof(errbuf)));
}
return so;
@ -360,7 +519,7 @@ connectMaxScale(char *conn_socket)
* @return 1 on success, 0 on failure
*/
static int
setipaddress(struct in_addr *a, char *p)
setipaddress(struct in_addr *a, const char *p)
{
#ifdef __USE_POSIX
struct addrinfo *ai = NULL, hint;
@ -418,17 +577,84 @@ setipaddress(struct in_addr *a, char *p)
* @param so The socket connected to MaxScale
* @return Non-zero of succesful authentication
*/
static int
authMaxScale(int so)
static bool
authUnixSocket(int so)
{
char buf[MAXADMIN_AUTH_REPLY_LEN];
if (read(so, buf, MAXADMIN_AUTH_REPLY_LEN) != MAXADMIN_AUTH_REPLY_LEN)
{
fprintf(stderr, "Could not read authentication response from MaxScale.\n");
return 0;
}
return strncmp(buf, MAXADMIN_FAILED_AUTH_MESSAGE, MAXADMIN_AUTH_REPLY_LEN);
bool authenticated = (strncmp(buf, MAXADMIN_AUTH_SUCCESS_REPLY, MAXADMIN_AUTH_REPLY_LEN) == 0);
if (!authenticated)
{
fprintf(stderr, "Could connect to MaxScale, but was not authorized.\n");
}
return authenticated;
}
/**
* Perform authentication using the maxscaled protocol conventions
*
* @param so The socket connected to MaxScale
* @param user The username to authenticate
* @param password The password to authenticate with
* @return Non-zero of succesful authentication
*/
static bool
authInetSocket(int so, const char *user, const char *password)
{
char buf[20];
size_t len;
len = MAXADMIN_AUTH_USER_PROMPT_LEN;
if (read(so, buf, len) != len)
{
fprintf(stderr, "Could not read user prompt from MaxScale.\n");
return false;
}
len = strlen(user);
if (write(so, user, len) != len)
{
fprintf(stderr, "Could not write user to MaxScale.\n");
return false;
}
len = MAXADMIN_AUTH_PASSWORD_PROMPT_LEN;
if (read(so, buf, len) != len)
{
fprintf(stderr, "Could not read password prompt from MaxScale.\n");
return false;
}
len = strlen(password);
if (write(so, password, len) != len)
{
fprintf(stderr, "Could not write password to MaxScale.\n");
return false;
}
len = MAXADMIN_AUTH_REPLY_LEN;
if (read(so, buf, len) != len)
{
fprintf(stderr, "Could not read authentication response from MaxScale.\n");
return false;
}
bool authenticated = (strncmp(buf, MAXADMIN_AUTH_SUCCESS_REPLY, MAXADMIN_AUTH_REPLY_LEN) == 0);
if (!authenticated)
{
fprintf(stderr, "Could connect to MaxScale, but was not authorized.\n");
}
return authenticated;
}
/**
@ -550,13 +776,27 @@ DoUsage(const char *progname)
{
PrintVersion(progname);
printf("The MaxScale administrative and monitor client.\n\n");
printf("Usage: %s [-S socket] [<command file> | <command>]\n\n", progname);
printf(" -S|--socket=... The UNIX socket to connect to, The default is\n");
printf(" /tmp/maxadmin.sock\n");
printf(" -v|--version print version information and exit\n");
printf(" -?|--help Print this help text.\n");
printf("Usage: %s [(-S socket)|([-u user] [-p password] [-h hostname] [-P port])]"
"[<command file> | <command>]\n\n", progname);
printf(" -S|--socket=... The UNIX domain socket to connect to, The default is\n");
printf(" %s\n", MAXADMIN_DEFAULT_SOCKET);
printf(" -u|--user=... The user name to use for the connection, default\n");
printf(" is %s.\n", MAXADMIN_DEFAULT_USER);
printf(" -p|--password=... The user password, if not given the password will\n");
printf(" be prompted for interactively\n");
printf(" -h|--host=... The maxscale host to connecto to. The default is\n");
printf(" %s\n", MAXADMIN_DEFAULT_HOST);
printf(" -P|--port=... The port to use for the connection, the default\n");
printf(" port is %s.\n", MAXADMIN_DEFAULT_PORT);
printf(" -v|--version Print version information and exit\n");
printf(" -?|--help Print this help text.\n");
printf("\n");
printf("Any remaining arguments are treated as MaxScale commands or a file\n");
printf("containing commands to execute.\n");
printf("\n");
printf("Either a socket or a hostname/port combination should be provided.\n");
printf("If a port or hostname is provided, but not the other, then the default\n"
"value is used.\n");
}
/**
@ -687,3 +927,55 @@ read_inifile(char **conn_socket, int* editor)
}
fclose(fp);
}
/**
* Get password
*
* @param password Buffer for password.
* @param len The size of the buffer.
*
* @return Whether the password was obtained.
*/
bool getPassword(char *passwd, size_t len)
{
bool gotten = false;
struct termios tty_attr;
tcflag_t c_lflag;
if (tcgetattr(STDIN_FILENO, &tty_attr) == 0)
{
c_lflag = tty_attr.c_lflag;
tty_attr.c_lflag &= ~ICANON;
tty_attr.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) == 0)
{
printf("Password: ");
fgets(passwd, len, stdin);
tty_attr.c_lflag = c_lflag;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) == 0)
{
int i = strlen(passwd);
if (i > 1)
{
passwd[i - 1] = '\0';
}
printf("\n");
gotten = true;
}
}
}
if (!gotten)
{
fprintf(stderr, "Could not configure terminal.\n");
}
return gotten;
}

View File

@ -37,13 +37,25 @@
* 23/07/13 Mark Riddoch Addition of error mechanism to add user
* 23/05/16 Massimiliano Pinto admin_add_user and admin_remove_user
* no longer accept password parameter
* 02/09/16 Johan Wikman Enabled Linux accounts and MaxScale users
*
* @endverbatim
*/
static USERS *loadUsers();
static void initialise();
static USERS *users = NULL;
static USERS *loadLinuxUsers();
static USERS *loadInetUsers();
static const char *admin_add_user(USERS** pusers, const char* fname,
const char* uname, const char* password);
static const char* admin_remove_user(USERS *users, const char* fname,
const char *uname, const char *passwd);
static bool admin_search_user(USERS *users, const char *uname);
static USERS *linux_users = NULL;
static USERS *inet_users = NULL;
static int admin_init = 0;
static char *ADMIN_ERR_NOMEM = "Out of memory";
@ -61,7 +73,11 @@ static char *ADMIN_SUCCESS = NULL;
static const int LINELEN = 80;
static const char USERS_FILE_NAME[] = "maxadmin-users";
static const char LINUX_USERS_FILE_NAME[] = "maxadmin-users";
static const char INET_USERS_FILE_NAME[] = "passwd";
static const char INET_DEFAULT_USERNAME[] = "admin";
static const char INET_DEFAULT_PASSWORD[] = "mariadb";
/**
* Admin Users initialisation
@ -75,125 +91,15 @@ initialise()
}
admin_init = 1;
users = loadUsers();
linux_users = loadLinuxUsers();
inet_users = loadInetUsers();
}
/**
* Verify a username and password
*
* @param username Username to verify
* @param password Password to verify
* @return Non-zero if the username/password combination is valid
*/
int
admin_verify(char *username, char *password)
{
char *pw;
initialise();
if (users == NULL)
{
if (strcmp(username, "admin") == 0 && strcmp(password, "mariadb") == 0)
{
return 1;
}
}
else
{
if ((pw = users_fetch(users, username)) == NULL)
{
return 0;
}
struct crypt_data cdata;
cdata.initialized = 0;
if (strcmp(pw, crypt_r(password, ADMIN_SALT, &cdata)) == 0)
{
return 1;
}
}
return 0;
}
/**
* Load the admin users
*
* @return Table of users
*/
static USERS *
loadUsers()
{
USERS *rval;
FILE *fp;
char fname[PATH_MAX], *home;
char uname[80];
int added_users = 0;
initialise();
snprintf(fname, sizeof(fname), "%s/%s", get_datadir(), USERS_FILE_NAME);
if ((fp = fopen(fname, "r")) == NULL)
{
return NULL;
}
if ((rval = users_alloc()) == NULL)
{
fclose(fp);
return NULL;
}
while (fgets(uname, sizeof(uname), fp))
{
char *nl = strchr(uname, '\n');
if (nl)
{
*nl = '\0';
}
else if (!feof(fp))
{
MXS_ERROR("Line length exceeds %d characters, possible corrupted "
"'passwd' file in: %s", LINELEN, fname);
users_free(rval);
rval = NULL;
break;
}
char *tmp_ptr = strchr(uname, ':');
if (tmp_ptr)
{
*tmp_ptr = '\0';
MXS_WARNING("Found user '%s' with password. "
"This user might not be compatible with new maxadmin in MaxScale 2.0. "
"Remove it with \"remove user %s\" through MaxAdmin", uname, uname);
}
if (users_add(rval, uname, ""))
{
added_users++;
}
}
fclose(fp);
if (!added_users)
{
users_free(rval);
rval = NULL;
}
return rval;
}
/**
* Add user
*
* @param uname Name of the new user
* @return NULL on success or an error string on failure
*/
char *
admin_add_user(char *uname)
static const char *admin_add_user(USERS** pusers, const char* fname,
const char* uname, const char* password)
{
FILE *fp;
char fname[PATH_MAX], *home;
initialise();
char path[PATH_MAX], *home;
if (access(get_datadir(), F_OK) != 0)
{
@ -203,51 +109,51 @@ admin_add_user(char *uname)
}
}
snprintf(fname, sizeof(fname), "%s/%s", get_datadir(), USERS_FILE_NAME);
if (users == NULL)
snprintf(path, sizeof(path), "%s/%s", get_datadir(), fname);
if (*pusers == NULL)
{
MXS_NOTICE("Create initial password file.");
if ((users = users_alloc()) == NULL)
if ((*pusers = users_alloc()) == NULL)
{
return ADMIN_ERR_NOMEM;
}
if ((fp = fopen(fname, "w")) == NULL)
if ((fp = fopen(path, "w")) == NULL)
{
MXS_ERROR("Unable to create password file %s.", fname);
MXS_ERROR("Unable to create password file %s.", path);
return ADMIN_ERR_PWDFILEOPEN;
}
fclose(fp);
}
if (users_fetch(users, uname) != NULL)
if (users_fetch(*pusers, (char*)uname) != NULL) // TODO: Make users const correct.
{
return ADMIN_ERR_DUPLICATE;
}
users_add(users, uname, "");
if ((fp = fopen(fname, "a")) == NULL)
users_add(*pusers, (char*)uname, password ? (char*)password : ""); // TODO: Make users const correct.
if ((fp = fopen(path, "a")) == NULL)
{
MXS_ERROR("Unable to append to password file %s.", fname);
MXS_ERROR("Unable to append to password file %s.", path);
return ADMIN_ERR_FILEAPPEND;
}
fprintf(fp, "%s\n", uname);
if (password)
{
fprintf(fp, "%s:%s\n", uname, password);
}
else
{
fprintf(fp, "%s\n", uname);
}
fclose(fp);
return ADMIN_SUCCESS;
}
/**
* Remove maxscale user from in-memory structure and from password file
*
* @param uname Name of the new user
* @return NULL on success or an error string on failure
*/
char* admin_remove_user(
char* uname)
static const char* admin_remove_user(USERS *users, const char* fname,
const char *uname, const char *passwd)
{
FILE* fp;
FILE* fp_tmp;
char fname[PATH_MAX];
char fname_tmp[PATH_MAX];
char path[PATH_MAX];
char path_tmp[PATH_MAX];
char* home;
char fusr[LINELEN];
char fpwd[LINELEN];
@ -260,43 +166,53 @@ char* admin_remove_user(
return ADMIN_ERR_DELROOT;
}
if (!admin_search_user(uname))
if (!admin_search_user(users, uname))
{
MXS_ERROR("Couldn't find user %s. Removing user failed.", uname);
return ADMIN_ERR_USERNOTFOUND;
}
if (passwd)
{
if (admin_verify_inet_user(uname, passwd) == 0)
{
MXS_ERROR("Authentication failed, wrong user/password "
"combination. Removing user failed.");
return ADMIN_ERR_AUTHENTICATION;
}
}
/** Remove user from in-memory structure */
users_delete(users, uname);
users_delete(users, (char*)uname); // TODO: Make users const correct.
/**
* Open passwd file and remove user from the file.
*/
snprintf(fname, sizeof(fname), "%s/%s", get_datadir(), USERS_FILE_NAME);
snprintf(fname_tmp, sizeof(fname_tmp), "%s/%s_tmp", get_datadir(), USERS_FILE_NAME);
snprintf(path, sizeof(path), "%s/%s", get_datadir(), fname);
snprintf(path_tmp, sizeof(path_tmp), "%s/%s_tmp", get_datadir(), fname);
/**
* Rewrite passwd file from memory.
*/
if ((fp = fopen(fname, "r")) == NULL)
if ((fp = fopen(path, "r")) == NULL)
{
int err = errno;
MXS_ERROR("Unable to open password file %s : errno %d.\n"
"Removing user from file failed; it must be done "
"manually.",
fname,
path,
err);
return ADMIN_ERR_PWDFILEOPEN;
}
/**
* Open temporary passwd file.
*/
if ((fp_tmp = fopen(fname_tmp, "w")) == NULL)
if ((fp_tmp = fopen(path_tmp, "w")) == NULL)
{
int err = errno;
MXS_ERROR("Unable to open tmp file %s : errno %d.\n"
"Removing user from passwd file failed; it must be done "
"manually.",
fname_tmp,
path_tmp,
err);
fclose(fp);
return ADMIN_ERR_TMPFILEOPEN;
@ -311,11 +227,11 @@ char* admin_remove_user(
MXS_ERROR("Unable to process passwd file %s : errno %d.\n"
"Removing user from file failed, and must be done "
"manually.",
fname,
path,
err);
fclose(fp);
fclose(fp_tmp);
unlink(fname_tmp);
unlink(path_tmp);
return ADMIN_ERR_PWDFILEACCESS;
}
@ -330,7 +246,7 @@ char* admin_remove_user(
else if (!feof(fp))
{
MXS_ERROR("Line length exceeds %d characters, possible corrupted "
"'passwd' file in: %s", LINELEN, fname);
"'passwd' file in: %s", LINELEN, path);
fclose(fp);
fclose(fp_tmp);
return ADMIN_ERR_PWDFILEACCESS;
@ -358,11 +274,11 @@ char* admin_remove_user(
"errno %d.\n"
"Removing user from file failed, and must be "
"done manually.",
fname,
path,
err);
fclose(fp);
fclose(fp_tmp);
unlink(fname_tmp);
unlink(path_tmp);
return ADMIN_ERR_PWDFILEACCESS;
}
}
@ -370,16 +286,16 @@ char* admin_remove_user(
/**
* Replace original passwd file with new.
*/
if (rename(fname_tmp, fname))
if (rename(path_tmp, path))
{
int err = errno;
MXS_ERROR("Unable to rename new passwd file %s : errno "
"%d.\n"
"Rename it to %s manually.",
fname_tmp,
path_tmp,
err,
fname);
unlink(fname_tmp);
path);
unlink(path_tmp);
fclose(fp_tmp);
return ADMIN_ERR_PWDFILEACCESS;
}
@ -387,47 +303,284 @@ char* admin_remove_user(
return ADMIN_SUCCESS;
}
/**
* Check for existance of the user
*
* @param user The user name to test
* @return Non-zero if the user exists
* @param uname The user name to test
* @return True if the user exists
*/
int
admin_search_user(char *user)
static bool admin_search_user(USERS *users, const char *uname)
{
return (users_fetch(users, (char*)uname) != NULL); // TODO: Make users const correct.
}
/**
*/
void dcb_print_users(DCB *dcb, const char* heading, USERS *users)
{
dcb_printf(dcb, "%s", heading);
if (users)
{
HASHITERATOR *iter = hashtable_iterator(users->data);
if (iter)
{
char *sep = "";
const char *user;
while ((user = hashtable_next(iter)) != NULL)
{
dcb_printf(dcb, "%s%s", sep, user);
sep = ", ";
}
hashtable_iterator_free(iter);
}
}
dcb_printf(dcb, "%s", "\n");
}
/**
* Load the admin users
*
* @return Table of users
*/
static USERS *
loadUsers(const char *fname)
{
USERS *rval;
FILE *fp;
char path[PATH_MAX], *home;
char uname[80];
int added_users = 0;
initialise();
snprintf(path, sizeof(path), "%s/%s", get_datadir(), fname);
if ((fp = fopen(path, "r")) == NULL)
{
return NULL;
}
if ((rval = users_alloc()) == NULL)
{
fclose(fp);
return NULL;
}
while (fgets(uname, sizeof(uname), fp))
{
char *nl = strchr(uname, '\n');
if (nl)
{
*nl = '\0';
}
else if (!feof(fp))
{
MXS_ERROR("Line length exceeds %d characters, possibly corrupted "
"'passwd' file in: %s", LINELEN, path);
users_free(rval);
rval = NULL;
break;
}
char *password;
char *colon = strchr(uname, ':');
if (colon)
{
// Inet case
*colon = 0;
password = colon + 1;
}
else
{
// Linux case.
password = "";
}
if (users_add(rval, uname, password))
{
added_users++;
}
}
fclose(fp);
if (!added_users)
{
users_free(rval);
rval = NULL;
}
return rval;
}
static USERS *loadLinuxUsers()
{
return loadUsers(LINUX_USERS_FILE_NAME);
}
static USERS *loadInetUsers()
{
return loadUsers(INET_USERS_FILE_NAME);
}
/**
* Enable Linux account
*
* @param uname Name of Linux user
*
* @return NULL on success or an error string on failure.
*/
const char *admin_enable_linux_account(const char *uname)
{
initialise();
int rv = 0;
return admin_add_user(&linux_users, LINUX_USERS_FILE_NAME, uname, NULL);
}
if (strcmp(user, DEFAULT_ADMIN_USER) == 0)
/**
* Disable Linux account
*
* @param uname Name of Linux user
*
* @return NULL on success or an error string on failure.
*/
const char* admin_disable_linux_account(const char* uname)
{
initialise();
return admin_remove_user(linux_users, LINUX_USERS_FILE_NAME, uname, NULL);
}
/**
* Check whether Linux account is enabled
*
* @param uname The user name
*
* @return True if the account is enabled, false otherwise.
*/
bool admin_linux_account_enabled(const char *uname)
{
initialise();
bool rv = false;
if (strcmp(uname, DEFAULT_ADMIN_USER) == 0)
{
rv = 1;
rv = true;
}
else if (users)
else if (linux_users)
{
rv = (users_fetch(users, user) != NULL);
rv = admin_search_user(linux_users, uname);
}
return rv;
}
/**
* Print the statistics and user names of the administration users
* Add insecure remote (network) user.
*
* @param dcb A DCB to send the output to
* @param uname Name of the new user.
* @param password Password of the new user.
*
* @return NULL on success or an error string on failure.
*/
void
dcb_PrintAdminUsers(DCB *dcb)
const char *admin_add_inet_user(const char *uname, const char* password)
{
if (users)
initialise();
struct crypt_data cdata;
cdata.initialized = 0;
char *cpassword = crypt_r(password, ADMIN_SALT, &cdata);
return admin_add_user(&inet_users, INET_USERS_FILE_NAME, uname, cpassword);
}
/**
* Remove insecure remote (network) user
*
* @param uname Name of user to be removed.
* @param password Password of user to be removed.
*
* @return NULL on success or an error string on failure.
*/
const char* admin_remove_inet_user(const char* uname, const char *password)
{
initialise();
return admin_remove_user(inet_users, INET_USERS_FILE_NAME, uname, password);
}
/**
* Check for existance of remote user.
*
* @param user The user name to test.
*
* @return True if the user exists, false otherwise.
*/
bool admin_inet_user_exists(const char *uname)
{
initialise();
bool rv = false;
if (inet_users)
{
dcb_usersPrint(dcb, users);
rv = admin_search_user(inet_users, uname);
}
return rv;
}
/**
* Verify a remote user name and password
*
* @param username Username to verify
* @param password Password to verify
*
* @return True if the username/password combination is valid
*/
bool
admin_verify_inet_user(const char *username, const char *password)
{
bool rv = false;
initialise();
if (inet_users)
{
const char* pw = users_fetch(inet_users, (char*)username); // TODO: Make users const-correct.
if (pw)
{
struct crypt_data cdata;
cdata.initialized = 0;
if (strcmp(pw, crypt_r(password, ADMIN_SALT, &cdata)) == 0)
{
rv = true;
}
}
}
else
{
dcb_printf(dcb, "No administration users have been defined.\n");
if (strcmp(username, INET_DEFAULT_USERNAME) == 0
&& strcmp(password, INET_DEFAULT_PASSWORD) == 0)
{
rv = true;
}
}
return rv;
}
/**
* Print Linux and and inet users
*
* @param dcb A DCB to send the output to.
*/
void dcb_PrintAdminUsers(DCB *dcb)
{
dcb_print_users(dcb, "Enabled Linux accounts (secure) : ", linux_users);
dcb_print_users(dcb, "Created network accounts (insecure): ", inet_users);
}

View File

@ -84,6 +84,7 @@ static bool 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 char *config_get_password(CONFIG_PARAMETER *);
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 *);
@ -119,7 +120,8 @@ static char *service_params[] =
"router_options",
"servers",
"user",
"passwd",
"passwd", // DEPRECATE: See config_get_password.
"password",
"enable_root_user",
"max_connections",
"max_queued_connections",
@ -139,6 +141,7 @@ static char *service_params[] =
"ignore_databases_regex",
"log_auth_warnings",
"source", /**< Avrorouter only */
"retry_on_failure",
NULL
};
@ -166,7 +169,8 @@ static char *monitor_params[] =
"module",
"servers",
"user",
"passwd",
"passwd", // DEPRECATE: See config_get_password.
"password",
"script",
"events",
"mysql51_replication",
@ -594,6 +598,30 @@ config_get_value(CONFIG_PARAMETER *params, const char *name)
return NULL;
}
// DEPRECATE: In 2.1 complain but accept if "passwd" is provided, in 2.2
// DEPRECATE: drop support for "passwd".
/**
* Get the value of the password parameter
*
* The words looked for are "password" and "passwd".
*
* @param params The linked list of config parameters
* @return the parameter value or NULL if not found
*/
static char *
config_get_password(CONFIG_PARAMETER *params)
{
char *password = config_get_value(params, "password");
char *passwd = config_get_value(params, "passwd");
if (password && passwd)
{
MXS_WARNING("Both 'password' and 'passwd' specified. Using value of 'password'.");
}
return passwd ? passwd : password;
}
/**
* Get the value of a config parameter as a string
*
@ -1386,7 +1414,7 @@ process_config_update(CONFIG_CONTEXT *context)
max_queued_connections = config_get_value_string(obj->parameters, "max_queued_connections");
queued_connection_timeout = config_get_value_string(obj->parameters, "queued_connection_timeout");
user = config_get_value(obj->parameters, "user");
auth = config_get_value(obj->parameters, "passwd");
auth = config_get_password(obj->parameters);
auth_all_servers = config_get_value(obj->parameters, "auth_all_servers");
strip_db_esc = config_get_value(obj->parameters, "strip_db_esc");
@ -2318,7 +2346,7 @@ int create_new_service(CONFIG_CONTEXT *obj)
}
char *user = config_get_value(obj->parameters, "user");
char *auth = config_get_value(obj->parameters, "passwd");
char *auth = config_get_password(obj->parameters);
if (user && auth)
{
@ -2331,7 +2359,7 @@ int create_new_service(CONFIG_CONTEXT *obj)
obj->object,
user ? "" : "the 'user' parameter",
!user && !auth ? " and " : "",
auth ? "" : "the 'passwd' parameter");
auth ? "" : "the 'password' or 'passwd' parameter");
}
char *subservices = config_get_value(obj->parameters, "subservices");
@ -2736,7 +2764,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
}
char *user = config_get_value(obj->parameters, "user");
char *passwd = config_get_value(obj->parameters, "passwd");
char *passwd = config_get_password(obj->parameters);
if (user && passwd)
{
monitorAddUser(obj->element, user, passwd);

View File

@ -137,6 +137,7 @@ static struct option long_options[] =
{"execdir", required_argument, 0, 'E'},
{"language", required_argument, 0, 'N'},
{"piddir", required_argument, 0, 'P'},
{"basedir", required_argument, 0, 'R'},
{"user", required_argument, 0, 'U'},
{"syslog", required_argument, 0, 's'},
{"maxlog", required_argument, 0, 'S'},
@ -164,7 +165,7 @@ static void write_footer(void);
static int ntfw_cb(const char*, const struct stat*, int, struct FTW*);
static bool file_is_readable(const char* absolute_pathname);
static bool file_is_writable(const char* absolute_pathname);
bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr);
bool handle_path_arg(char** dest, const char* path, char* arg, bool rd, bool wr);
static void set_log_augmentation(const char* value);
static void usage(void);
static char* get_expanded_pathname(
@ -697,30 +698,27 @@ static void print_log_n_stderr(
const char* fprstr, /*< string to be printed to stderr */
int eno) /*< errno, if it is set, zero, otherwise */
{
char* log_err = "Error :";
char* fpr_err = "*\n* Error :";
char* fpr_end = "\n*\n";
if (do_log)
{
mxs_log_init(NULL, get_logdir(), MXS_LOG_TARGET_FS);
char errbuf[STRERROR_BUFLEN];
MXS_ERROR("%s %s %s %s",
log_err,
logstr,
eno == 0 ? " " : "Error :",
eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf)));
if (mxs_log_init(NULL, get_logdir(), MXS_LOG_TARGET_FS))
{
char errbuf[STRERROR_BUFLEN];
MXS_ERROR("%s%s%s%s",
logstr,
eno == 0 ? "" : " (",
eno == 0 ? "" : strerror_r(eno, errbuf, sizeof(errbuf)),
eno == 0 ? "" : ")");
}
}
if (do_stderr)
{
char errbuf[STRERROR_BUFLEN];
fprintf(stderr,
"%s %s %s %s %s",
fpr_err,
"* Error: %s%s%s%s\n",
fprstr,
eno == 0 ? " " : "Error :",
eno == 0 ? " " : strerror_r(eno, errbuf, sizeof(errbuf)),
fpr_end);
eno == 0 ? "" : " (",
eno == 0 ? "" : strerror_r(eno, errbuf, sizeof(errbuf)),
eno == 0 ? "" : ")");
}
}
@ -887,32 +885,52 @@ static void usage(void)
{
fprintf(stderr,
"\nUsage : %s [OPTION]...\n\n"
" -c, --config-check Validate configuration file and exit\n"
" -d, --nodaemon enable running in terminal process (default:disabled)\n"
" -f, --config=FILE relative or absolute pathname of MaxScale configuration file\n"
" (default:/etc/maxscale.cnf)\n"
" -l, --log=[file|shm|stdout] log to file, shared memory or stdout (default: file)\n"
" -L, --logdir=PATH path to log file directory (default: /var/log/maxscale)\n"
" -A, --cachedir=PATH path to cache directory (default: /var/cache/maxscale)\n"
" -B, --libdir=PATH path to module directory (default: /usr/lib64/maxscale)\n"
" -C, --configdir=PATH path to configuration file directory (default: /etc/)\n"
" -D, --datadir=PATH path to data directory, stored embedded mysql tables\n"
" (default: /var/cache/maxscale)\n"
" -c, --config-check validate configuration file and exit\n"
" -d, --nodaemon enable running in terminal process\n"
" -f, --config=FILE relative or absolute pathname of config file\n"
" -l, --log=[file|shm|stdout] log to file, shared memory or stdout\n"
" (default: file)\n"
" -L, --logdir=PATH path to log file directory\n"
" -A, --cachedir=PATH path to cache directory\n"
" -B, --libdir=PATH path to module directory\n"
" -C, --configdir=PATH path to configuration file directory\n"
" -D, --datadir=PATH path to data directory,\n"
" stored embedded mysql tables\n"
" -E, --execdir=PATH path to the maxscale and other executable files\n"
" (default: /usr/bin)\n"
" -N, --language=PATH path to errmsg.sys file (default: /var/lib/maxscale)\n"
" -P, --piddir=PATH path to PID file directory (default: /var/run/maxscale)\n"
" -U, --user=USER run MaxScale as another user.\n"
" The user ID and group ID of this user are used to run MaxScale.\n"
" -N, --language=PATH path to errmsg.sys file\n"
" -P, --piddir=PATH path to PID file directory\n"
" -R, --basedir=PATH base path for all other paths\n"
" -U, --user=USER user ID and group ID of specified user are used to\n"
" run MaxScale\n"
" -s, --syslog=[yes|no] log messages to syslog (default:yes)\n"
" -S, --maxlog=[yes|no] log messages to MaxScale log (default: yes)\n"
" -G, --log_augmentation=0|1 augment messages with the name of the function where\n"
" the message was logged (default: 0). Primarily for \n"
" development purposes.\n"
" -G, --log_augmentation=0|1 augment messages with the name of the function\n"
" where the message was logged (default: 0)\n"
" -v, --version print version info and exit\n"
" -V, --version-full print full version info and exit\n"
" -?, --help show this help\n"
, progname);
"\n"
"Defaults paths:\n"
" config file: %s/%s\n"
" configdir : %s\n"
" logdir : %s\n"
" cachedir : %s\n"
" libdir : %s\n"
" datadir : %s\n"
" execdir : %s\n"
" language : %s\n"
" piddir : %s\n"
"\n"
"If '--basedir' is provided then all other paths, including the default\n"
"configuration file path, are defined relative to that. As an example,\n"
"if '--basedir /path/maxscale' is specified, then, for instance, the log\n"
"dir will be '/path/maxscale/var/log/maxscale', the config dir will be\n"
"'/path/maxscale/etc' and the default config file will be\n"
"'/path/maxscale/etc/maxscale.cnf'.\n",
progname,
get_configdir(), default_cnf_fname,
get_configdir(), get_logdir(), get_cachedir(), get_libdir(),
get_datadir(), get_execdir(), get_langdir(), get_piddir());
}
@ -1135,6 +1153,61 @@ bool configure_signals(void)
return true;
}
/**
* Set the directories of MaxScale relative to a basedir
*
* @param basedir The base directory relative to which the other are set.
*
* @return True if the directories could be set, false otherwise.
*/
bool set_dirs(const char *basedir)
{
bool rv = true;
char *path;
if (rv && (rv = handle_path_arg(&path, basedir, "var/" MXS_DEFAULT_LOG_SUBPATH, true, false)))
{
set_logdir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, "var/" MXS_DEFAULT_CACHE_SUBPATH, true, true)))
{
set_cachedir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, MXS_DEFAULT_LIB_SUBPATH, true, false)))
{
set_libdir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, MXS_DEFAULT_CONFIG_SUBPATH, true, false)))
{
set_configdir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, "var/" MXS_DEFAULT_DATA_SUBPATH, true, false)))
{
set_datadir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, MXS_DEFAULT_EXEC_SUBPATH, true, false)))
{
set_execdir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, "var/" MXS_DEFAULT_LANG_SUBPATH, true, false)))
{
set_langdir(path);
}
if (rv && (rv = handle_path_arg(&path, basedir, "var/" MXS_DEFAULT_PID_SUBPATH, true, true)))
{
set_piddir(path);
}
return rv;
}
/**
* @mainpage
* The main entry point into MaxScale
@ -1304,7 +1377,6 @@ int main(int argc, char **argv)
}
break;
case 'L':
if (handle_path_arg(&tmp_path, optarg, NULL, true, false))
{
set_logdir(tmp_path);
@ -1381,6 +1453,17 @@ int main(int argc, char **argv)
succp = false;
}
break;
case 'R':
if (handle_path_arg(&tmp_path, optarg, NULL, true, false))
{
succp = set_dirs(tmp_path);
free(tmp_path);
}
else
{
succp = false;
}
break;
case 'S':
{
@ -2252,7 +2335,7 @@ static int write_pid_file()
return 0;
}
bool handle_path_arg(char** dest, char* path, char* arg, bool rd, bool wr)
bool handle_path_arg(char** dest, const char* path, char* arg, bool rd, bool wr)
{
char pathbuffer[PATH_MAX + 2];
char* errstr;

View File

@ -584,7 +584,7 @@ return_succ:
{
/** This releases memory of all created objects */
logmanager_done_nomutex();
LOG_ERROR("MaxScale Log: Error, initialization failed.\n");
fprintf(stderr, "* Error: Initializing the log manager failed.\n");
}
return succ;
}
@ -1693,7 +1693,7 @@ static bool logfile_open_file(filewriter_t* fw,
if (fw->fwr_file == NULL)
{
LOG_ERROR("MaxScale Log: Error, opening log file %s failed.\n", lf->lf_full_file_name);
// Error logged by skygw_file_init to stderr.
rv = false;
}
@ -2125,9 +2125,6 @@ static void filewriter_done(filewriter_t* fw, bool write_footer)
{
case RUN:
CHK_FILEWRITER(fw);
case INIT:
fw->fwr_logmes = NULL;
fw->fwr_clientmes = NULL;
if (log_config.use_stdout)
{
skygw_file_free(fw->fwr_file);
@ -2141,7 +2138,11 @@ static void filewriter_done(filewriter_t* fw, bool write_footer)
skygw_file_close(fw->fwr_file);
}
case INIT:
fw->fwr_logmes = NULL;
fw->fwr_clientmes = NULL;
fw->fwr_state = DONE;
break;
case DONE:
case UNINIT:
default:

View File

@ -826,7 +826,9 @@ mon_status_changed(MONITOR_SERVERS* mon_srv)
{
/* Previous status is -1 if not yet set */
return (mon_srv->mon_prev_status != -1
&& mon_srv->mon_prev_status != mon_srv->server->status);
&& mon_srv->mon_prev_status != mon_srv->server->status
/** If the server is going into maintenance or coming out of it, don't trigger a state change */
&& ((mon_srv->mon_prev_status | mon_srv->server->status) & SERVER_MAINT) == 0);
}
/**

View File

@ -89,7 +89,6 @@ server_alloc(char *servname, char *protocol, unsigned short port)
server->rlag = -2;
server->master_id = -1;
server->depth = -1;
server->slaves = NULL;
server->parameters = NULL;
server->server_string = NULL;
spinlock_init(&server->lock);

View File

@ -384,6 +384,9 @@ int serviceStartAllPorts(SERVICE* service)
(void*) service, retry_after);
MXS_NOTICE("Failed to start service %s, retrying in %d seconds.",
service->name, retry_after);
/** This will prevent MaxScale from shutting down if service start is retried later */
listeners = 1;
}
}
else

View File

@ -50,12 +50,12 @@
static int
test1()
{
if (admin_verify("admin", "mariadb") == 0)
if (admin_verify_inet_user("admin", "mariadb") == 0)
{
fprintf(stderr, "admin_verify: test 1.1 (default user) failed.\n");
return 1;
}
if (admin_verify("bad", "user"))
if (admin_verify_inet_user("bad", "user"))
{
fprintf(stderr, "admin_verify: test 1.2 (wrong user) failed.\n");
return 1;
@ -74,15 +74,15 @@ test1()
static int
test2()
{
char *err;
const char *err;
if ((err = admin_add_user("user0")) != NULL)
if ((err = admin_enable_linux_account("user0")) != NULL)
{
fprintf(stderr, "admin_add_user: test 2.1 (add user) failed, %s.\n", err);
return 1;
}
if (admin_add_user("user0") == NULL)
if (admin_enable_linux_account("user0") == NULL)
{
fprintf(stderr, "admin_add_user: test 2.2 (add user) failed, duplicate.\n");
@ -90,7 +90,7 @@ test2()
}
/* Deleting the last user is not forbidden so we expect this to succeed */
if ((err = admin_remove_user("user0")) != NULL)
if ((err = admin_disable_linux_account("user0")) != NULL)
{
fprintf(stderr, "admin_remove_user: test 2.3 (add user) failed, %s.\n", err);
@ -98,7 +98,7 @@ test2()
}
/* Add the user back, for test5. */
if ((err = admin_add_user("user0")) != NULL)
if ((err = admin_enable_linux_account("user0")) != NULL)
{
fprintf(stderr, "admin_add_user: test 2.4 (add user) failed, %s.\n", err);
@ -120,37 +120,37 @@ test2()
static int
test3()
{
char *err;
const char *err;
if ((err = admin_add_user("user1")) != NULL)
if ((err = admin_enable_linux_account("user1")) != NULL)
{
fprintf(stderr, "admin_add_user: test 3.1 (add user) failed, %s.\n", err);
return 1;
}
if (admin_search_user("user1") == 0)
if (admin_linux_account_enabled("user1") == 0)
{
fprintf(stderr, "admin_search_user: test 3.2 (search user) failed.\n");
return 1;
}
if (admin_search_user("user2") != 0)
if (admin_linux_account_enabled("user2") != 0)
{
fprintf(stderr, "admin_search_user: test 3.3 (search user) failed, unexpeted user found.\n");
return 1;
}
if ((err = admin_remove_user("user1")) != NULL)
if ((err = admin_disable_linux_account("user1")) != NULL)
{
fprintf(stderr, "admin_remove_user: test 3.4 (add user) failed, %s.\n", err);
return 1;
}
if (admin_search_user("user1"))
if (admin_linux_account_enabled("user1"))
{
fprintf(stderr, "admin_search_user: test 3.5 (search user) failed - user was deleted.\n");
@ -173,13 +173,14 @@ test3()
static int
test4()
{
char *err, user[40], passwd[40];
const char *err;
char user[40], passwd[40];
int i, n_users = 50;
for (i = 1; i < n_users; i++)
{
sprintf(user, "user%d", i);
if ((err = admin_add_user(user)) != NULL)
if ((err = admin_enable_linux_account(user)) != NULL)
{
fprintf(stderr, "admin_add_user: test 4.1 (add user) failed, %s.\n", err);
@ -190,7 +191,7 @@ test4()
for (i = 1; i < n_users; i++)
{
sprintf(user, "user%d", i);
if (admin_search_user(user) == 0)
if (admin_linux_account_enabled(user) == 0)
{
fprintf(stderr, "admin_search_user: test 4.2 (search user) failed.\n");
@ -201,7 +202,7 @@ test4()
for (i = 1; i < n_users; i++)
{
sprintf(user, "user%d", i);
if ((err = admin_remove_user(user)) != NULL)
if ((err = admin_disable_linux_account(user)) != NULL)
{
fprintf(stderr, "admin_remove_user: test 4.3 (add user) failed, %s.\n", err);
@ -221,16 +222,16 @@ test4()
static int
test5()
{
char *err;
const char *err;
if ((err = admin_add_user("user")) != NULL)
if ((err = admin_enable_linux_account("user")) != NULL)
{
fprintf(stderr, "admin_add_user: test 5.1 (add user) failed, %s.\n", err);
return 1;
}
if ((err = admin_remove_user("user0")) != NULL)
if ((err = admin_disable_linux_account("user0")) != NULL)
{
fprintf(stderr, "admin_remove_user: test 5.2 (add user) failed, %s.\n", err);

View File

@ -51,12 +51,16 @@ typedef struct admin_session
#endif
} ADMIN_session;
extern int admin_verify(char *, char *);
extern char *admin_add_user(char *);
extern int admin_search_user(char *);
extern void dcb_PrintAdminUsers(DCB *dcb);
extern const char *admin_enable_linux_account(const char *uname);
extern const char *admin_disable_linux_account(const char *uname);
extern bool admin_linux_account_enabled(const char *uname);
char* admin_remove_user(char* uname);
extern const char *admin_add_inet_user(const char *uname, const char *password);
extern const char *admin_remove_inet_user(const char *uname, const char *password);
extern bool admin_inet_user_exists(const char *uname);
extern bool admin_verify_inet_user(const char *uname, const char *password);
extern void dcb_PrintAdminUsers(DCB *dcb);
#endif

View File

@ -21,19 +21,30 @@
EXTERN_C_BLOCK_BEGIN
// NOTE: If you make changes here, ensure they are compatible with the
// situation in <root>/CMakeLists.txt, where directories are installed.
#define MXS_DEFAULT_PID_SUBPATH "run/maxscale"
#define MXS_DEFAULT_LOG_SUBPATH "log/maxscale"
#define MXS_DEFAULT_DATA_SUBPATH "lib/maxscale"
#define MXS_DEFAULT_LIB_SUBPATH "@MAXSCALE_LIBDIR@"
#define MXS_DEFAULT_CACHE_SUBPATH "cache/maxscale"
#define MXS_DEFAULT_LANG_SUBPATH "lib/maxscale"
#define MXS_DEFAULT_EXEC_SUBPATH "@MAXSCALE_BINDIR@"
#define MXS_DEFAULT_CONFIG_SUBPATH "etc"
/** Default file locations, configured by CMake */
static const char* default_cnf_fname = "maxscale.cnf";
static const char* default_configdir = "/etc";
static const char* default_configdir = "/" MXS_DEFAULT_CONFIG_SUBPATH;
/*< This should be changed to just /run eventually,
* the /var/run folder is an old standard and the newer FSH 3.0
* uses /run for PID files.*/
static const char* default_piddir = "@MAXSCALE_VARDIR@/run/maxscale";
static const char* default_logdir = "@MAXSCALE_VARDIR@/log/maxscale";
static const char* default_datadir = "@MAXSCALE_VARDIR@/lib/maxscale";
static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_LIBDIR@";
static const char* default_cachedir = "@MAXSCALE_VARDIR@/cache/maxscale";
static const char* default_langdir = "@MAXSCALE_VARDIR@/lib/maxscale";
static const char* default_execdir = "@CMAKE_INSTALL_PREFIX@/@MAXSCALE_BINDIR@";
static const char* default_piddir = "@MAXSCALE_VARDIR@/" MXS_DEFAULT_PID_SUBPATH;
static const char* default_logdir = "@MAXSCALE_VARDIR@/" MXS_DEFAULT_LOG_SUBPATH;
static const char* default_datadir = "@MAXSCALE_VARDIR@/" MXS_DEFAULT_DATA_SUBPATH;
static const char* default_libdir = "@CMAKE_INSTALL_PREFIX@/" MXS_DEFAULT_LIB_SUBPATH;
static const char* default_cachedir = "@MAXSCALE_VARDIR@/" MXS_DEFAULT_CACHE_SUBPATH;
static const char* default_langdir = "@MAXSCALE_VARDIR@/" MXS_DEFAULT_LANG_SUBPATH;
static const char* default_execdir = "@CMAKE_INSTALL_PREFIX@/" MXS_DEFAULT_EXEC_SUBPATH;
static char* configdir = NULL;
static char* logdir = NULL;

View File

@ -133,7 +133,6 @@ typedef enum
#define MONITOR_INTERVAL 10000 // in milliseconds
#define MONITOR_DEFAULT_ID 1UL // unsigned long value
#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server
/*
* Create declarations of the enum for monitor events and also the array of

View File

@ -45,6 +45,7 @@
*/
#define MAX_SERVER_NAME_LEN 1024
#define MAX_NUM_SLAVES 128 /**< Maximum number of slaves under a single server*/
/**
* The server parameters used for weighting routing decissions
@ -99,7 +100,7 @@ typedef struct server
SERVER_PARAM *parameters; /**< Parameters of a server that may be used to weight routing decisions */
long master_id; /**< Master server id of this node */
int depth; /**< Replication level in the tree */
long *slaves; /**< Slaves of this node */
long slaves[MAX_NUM_SLAVES]; /**< Slaves of this node */
bool master_err_is_logged; /*< If node failed, this indicates whether it is logged */
DCB *persistent; /**< List of unused persistent connections to the server */
SPINLOCK persistlock; /**< Lock for adjusting the persistent connections list */

View File

@ -145,7 +145,7 @@ max_admin_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
dcb->data = (void *)session_data;
/* Check for existance of the user */
if (admin_search_user(session_data->user))
if (admin_linux_account_enabled(session_data->user))
{
session_data->validated = true;
return 0;

View File

@ -13,12 +13,19 @@
* Public License.
*/
#define MAXADMIN_DEFAULT_SOCKET "/tmp/maxadmin.sock"
#define MAXADMIN_DEFAULT_SOCKET "/tmp/maxadmin.sock"
#define MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG_LEN 7
#define MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG "default"
#define MAXADMIN_GETPWUID_BUF_LEN 255
#define MAXADMIN_AUTH_REPLY_LEN 6
#define MAXADMIN_FAILED_AUTH_MESSAGE "FAILED"
#define MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG "default"
#define MAXADMIN_AUTH_REPLY_LEN 6
#define MAXADMIN_AUTH_FAILED_REPLY "FAILED"
#define MAXADMIN_AUTH_SUCCESS_REPLY "OK----"
#define MAXADMIN_AUTH_USER_PROMPT "USER"
#define MAXADMIN_AUTH_USER_PROMPT_LEN 4
#define MAXADMIN_AUTH_PASSWORD_PROMPT "PASSWORD"
#define MAXADMIN_AUTH_PASSWORD_PROMPT_LEN 8
#endif

View File

@ -28,16 +28,17 @@
#include <spinlock.h>
#include <housekeeper.h>
/**
* The telnetd specific protocol structure to put in the DCB.
* The maxscaled specific protocol structure to put in the DCB.
*/
typedef struct maxscaled
typedef struct maxscaled
{
SPINLOCK lock; /**< Protocol structure lock */
int state; /**< The connection state */
char *username; /**< The login name of the user */
SPINLOCK lock; /**< Protocol structure lock */
int state; /**< The connection state */
char *username; /**< The login name of the user */
} MAXSCALED;
#define MAXSCALED_STATE_LOGIN 1 /**< Waiting for credentials */
#define MAXSCALED_STATE_DATA 2 /**< User logged in */
#define MAXSCALED_STATE_LOGIN 1 /**< Waiting for user */
#define MAXSCALED_STATE_PASSWD 2 /**< Waiting for password */
#define MAXSCALED_STATE_DATA 3 /**< User logged in */
#endif

View File

@ -547,7 +547,7 @@ static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon)
if (mysql_num_rows(result) > 0)
{
ismaster = true;
while (nslaves < MONITOR_MAX_NUM_SLAVES && (row = mysql_fetch_row(result)))
while (nslaves < MAX_NUM_SLAVES && (row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
database->server->slaves[nslaves] = atol(row[0]);
@ -835,13 +835,7 @@ monitorMain(void *arg)
monitorDatabase(mon, ptr);
/* reset the slave list of current node */
if (ptr->server->slaves)
{
MXS_FREE(ptr->server->slaves);
}
/* create a new slave list */
ptr->server->slaves = (long *) MXS_CALLOC(MONITOR_MAX_NUM_SLAVES, sizeof(long));
MXS_ABORT_IF_NULL(ptr->server->slaves);
memset(&ptr->server->slaves, 0, sizeof(ptr->server->slaves));
num_servers++;
@ -1448,7 +1442,8 @@ static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers)
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);
add_slave_to_master(master->server->slaves, sizeof(master->server->slaves),
current->node_id);
master->server->depth = current->depth - 1;
monitor_set_pending_status(master, SERVER_MASTER);
handle->master = master;
@ -1509,7 +1504,7 @@ static int add_slave_to_master(long *slaves_list, int list_size, long node_id)
{
if (slaves_list[i] == 0)
{
memcpy(&slaves_list[i], &node_id, sizeof(long));
slaves_list[i] = node_id;
return 1;
}
}

View File

@ -0,0 +1,5 @@
install_script(cdc.py core)
install_script(cdc_users.py core)
install_script(cdc_last_transaction.py core)
install_script(cdc_kafka_producer.py core)
install_file(cdc_schema.go core)

View File

@ -66,6 +66,8 @@ MODULE_INFO info =
static char *version_str = "V2.0.0";
#define GETPWUID_BUF_LEN 255
static int maxscaled_read_event(DCB* dcb);
static int maxscaled_write_event(DCB *dcb);
static int maxscaled_write(DCB *dcb, GWBUF *queue);
@ -76,6 +78,100 @@ static int maxscaled_close(DCB *dcb);
static int maxscaled_listen(DCB *dcb, char *config);
static char *mxsd_default_auth();
static bool authenticate_unix_socket(MAXSCALED *protocol, DCB *dcb)
{
bool authenticated = false;
struct ucred ucred;
socklen_t len = sizeof(struct ucred);
/* Get UNIX client credentials from socket*/
if (getsockopt(dcb->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0)
{
struct passwd pw_entry;
struct passwd *pw_tmp;
char buf[GETPWUID_BUF_LEN];
/* Fetch username from UID */
if (getpwuid_r(ucred.uid, &pw_entry, buf, sizeof(buf), &pw_tmp) == 0)
{
GWBUF *username;
/* Set user in protocol */
protocol->username = strdup(pw_entry.pw_name);
username = gwbuf_alloc(strlen(protocol->username) + 1);
strcpy(GWBUF_DATA(username), protocol->username);
/* Authenticate the user */
if (dcb->authfunc.extract(dcb, username) == 0 &&
dcb->authfunc.authenticate(dcb) == 0)
{
dcb_printf(dcb, MAXADMIN_AUTH_SUCCESS_REPLY);
protocol->state = MAXSCALED_STATE_DATA;
dcb->user = strdup(protocol->username);
}
else
{
dcb_printf(dcb, MAXADMIN_AUTH_FAILED_REPLY);
}
gwbuf_free(username);
authenticated = true;
}
else
{
MXS_ERROR("Failed to get UNIX user %ld details for 'MaxScale Admin'",
(unsigned long)ucred.uid);
}
}
else
{
MXS_ERROR("Failed to get UNIX domain socket credentials for 'MaxScale Admin'.");
}
return authenticated;
}
static bool authenticate_inet_socket(MAXSCALED *protocol, DCB *dcb)
{
dcb_printf(dcb, MAXADMIN_AUTH_USER_PROMPT);
return true;
}
static bool authenticate_socket(MAXSCALED *protocol, DCB *dcb)
{
bool authenticated = false;
struct sockaddr address;
socklen_t address_len = sizeof(address);
if (getsockname(dcb->fd, &address, &address_len) == 0)
{
if (address.sa_family == AF_UNIX)
{
authenticated = authenticate_unix_socket(protocol, dcb);
}
else
{
authenticated = authenticate_inet_socket(protocol, dcb);
}
}
else
{
char errbuf[STRERROR_BUFLEN];
MXS_ERROR("Could not get socket family of client connection: %s",
strerror_r(errno, errbuf, sizeof(errbuf)));
}
return authenticated;
}
/**
* The "module object" for the maxscaled protocol module.
*/
@ -161,10 +257,41 @@ static int maxscaled_read_event(DCB* dcb)
{
if (GWBUF_LENGTH(head))
{
if (maxscaled->state == MAXSCALED_STATE_DATA)
switch (maxscaled->state)
{
SESSION_ROUTE_QUERY(dcb->session, head);
dcb_printf(dcb, "OK");
case MAXSCALED_STATE_LOGIN:
{
maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
maxscaled->state = MAXSCALED_STATE_PASSWD;
dcb_printf(dcb, MAXADMIN_AUTH_PASSWORD_PROMPT);
gwbuf_free(head);
}
break;
case MAXSCALED_STATE_PASSWD:
{
char *password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
if (admin_verify_inet_user(maxscaled->username, password))
{
dcb_printf(dcb, MAXADMIN_AUTH_SUCCESS_REPLY);
maxscaled->state = MAXSCALED_STATE_DATA;
}
else
{
dcb_printf(dcb, MAXADMIN_AUTH_FAILED_REPLY);
maxscaled->state = MAXSCALED_STATE_LOGIN;
}
gwbuf_free(head);
free(password);
}
break;
case MAXSCALED_STATE_DATA:
{
SESSION_ROUTE_QUERY(dcb->session, head);
dcb_printf(dcb, "OK");
}
break;
}
}
else
@ -241,9 +368,9 @@ static int maxscaled_accept(DCB *listener)
while ((client_dcb = dcb_accept(listener, &MyObject)) != NULL)
{
MAXSCALED *maxscaled_protocol = NULL;
MAXSCALED *maxscaled_protocol = (MAXSCALED *)calloc(1, sizeof(MAXSCALED));
if ((maxscaled_protocol = (MAXSCALED *)MXS_CALLOC(1, sizeof(MAXSCALED))) == NULL)
if (!maxscaled_protocol)
{
dcb_close(client_dcb);
continue;
@ -252,52 +379,14 @@ static int maxscaled_accept(DCB *listener)
maxscaled_protocol->username = NULL;
maxscaled_protocol->state = MAXSCALED_STATE_LOGIN;
/* Get UNIX client credentials from socket*/
if (getsockopt(client_dcb->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
bool authenticated = false;
if (!authenticate_socket(maxscaled_protocol, client_dcb))
{
MXS_ERROR("Failed to get UNIX socket credentials for 'MaxScale Admin'");
dcb_close(client_dcb);
free(maxscaled_protocol);
continue;
}
else
{
struct passwd pw_entry;
struct passwd *pw_tmp;
char buf[MAXADMIN_GETPWUID_BUF_LEN];
/* Fetch username from UID */
if (!getpwuid_r(ucred.uid, &pw_entry, buf, sizeof(buf), &pw_tmp))
{
GWBUF *username;
/* Set user in protocol */
maxscaled_protocol->username = MXS_STRDUP_A(pw_entry.pw_name);
username = gwbuf_alloc(strlen(maxscaled_protocol->username) + 1);
strcpy(GWBUF_DATA(username), maxscaled_protocol->username);
/* Authenticate the user */
if (client_dcb->authfunc.extract(client_dcb, username) == 0 &&
client_dcb->authfunc.authenticate(client_dcb) == 0)
{
dcb_printf(client_dcb, "OK----");
maxscaled_protocol->state = MAXSCALED_STATE_DATA;
client_dcb->user = MXS_STRDUP_A(maxscaled_protocol->username);
}
else
{
dcb_printf(client_dcb, "FAILED");
}
}
else
{
MXS_ERROR("Failed to get UNIX user %ld details for 'MaxScale Admin'",
(unsigned long)ucred.uid);
dcb_close(client_dcb);
continue;
}
}
spinlock_init(&maxscaled_protocol->lock);
client_dcb->protocol = (void *)maxscaled_protocol;
@ -350,7 +439,7 @@ static int maxscaled_close(DCB *dcb)
*/
static int maxscaled_listen(DCB *listener, char *config)
{
char *socket_path;
char *socket_path = NULL;
/* check for default UNIX socket */
if (strncmp(config, MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG, MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG_LEN) == 0)
@ -359,19 +448,8 @@ static int maxscaled_listen(DCB *listener, char *config)
}
else
{
socket_path = config;
socket_path = config;
}
/* check for UNIX socket path*/
if (strchr(socket_path,'/') == NULL)
{
MXS_ERROR("Failed to start listening on '%s' with 'MaxScale Admin' protocol,"
" only UNIX domain sockets are supported. Remove all 'port' and 'address'"
" parameters from this listener and add 'socket=default' to enable UNIX domain sockets.", socket_path);
return -1;
}
else
{
return (dcb_listen(listener, socket_path, "MaxScale Admin") < 0) ? 0 : 1;
}
return (dcb_listen(listener, socket_path, "MaxScale Admin") < 0) ? 0 : 1;
}

View File

@ -203,7 +203,7 @@ static int telnetd_read_event(DCB* dcb)
{
*t = 0;
}
if (admin_verify(telnetd->username, password))
if (admin_verify_inet_user(telnetd->username, password))
{
telnetd_echo(dcb, 1);
telnetd->state = TELNETD_STATE_DATA;

View File

@ -5,11 +5,6 @@ if(AVRO_FOUND)
set_target_properties(avrorouter PROPERTIES LINK_FLAGS -Wl,-z,defs)
target_link_libraries(avrorouter maxscale-common jansson ${AVRO_LIBRARIES} maxavro sqlite3 lzma)
install_module(avrorouter core)
install_script(cdc core)
install_script(cdc_users core)
install_script(cdc_last_transaction core)
install_script(cdc_kafka_producer core)
install_file(cdc_schema.go core)
else()
message(STATUS "Avro C libraries were not found, avrorouter will not be built.")
endif()

View File

@ -223,8 +223,8 @@ struct subcommand showoptions[] = {
"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",
"Show all maxadmin enabled Linux accounts and created maxadmin users",
"Show all maxadmin enabled Linux accounts and created maxadmin users",
{0, 0, 0} },
{ NULL, 0, NULL, NULL, NULL,
{0, 0, 0} }
@ -449,6 +449,8 @@ static void enable_syslog();
static void disable_syslog();
static void enable_maxlog();
static void disable_maxlog();
static void enable_account(DCB *, char *user);
static void disable_account(DCB *, char *user);
/**
* * The subcommands of the enable command
@ -536,6 +538,16 @@ struct subcommand enableoptions[] = {
"Enable maxlog logging",
{0, 0, 0}
},
{
"account",
1,
enable_account,
"Enable maxadmin usage for Linux user. E.g.:\n"
" MaxScale> enable account alice",
"Enable maxadmin usage for Linux user. E.g.:\n"
" MaxScale> enable account alice",
{ARG_TYPE_STRING, 0, 0}
},
{
NULL,
0,
@ -634,6 +646,16 @@ struct subcommand disableoptions[] = {
"Disable maxlog logging",
{0, 0, 0}
},
{
"account",
1,
disable_account,
"Disable maxadmin usage for Linux user. E.g.:\n"
" MaxScale> disable account alice",
"Disable maxadmin usage for Linux user. E.g.:\n"
" MaxScale> disable account alice",
{ARG_TYPE_STRING, 0, 0}
},
{
NULL,
0,
@ -688,32 +710,38 @@ struct subcommand failoptions[] = {
};
#endif /* FAKE_CODE */
static void telnetdAddUser(DCB *, char *);
static void telnetdAddUser(DCB *, char *user, char *password);
/**
* The subcommands of the add command
*/
struct subcommand addoptions[] = {
{ "user", 1, telnetdAddUser,
"Add a new user for the debug interface. E.g. add user john",
"Add a new user for the debug interface. E.g. add user john",
{ARG_TYPE_STRING, 0, 0} },
{ "user", 2, telnetdAddUser,
"Add insecure account for using maxadmin over the network. E.g.:\n"
" MaxScale> add user bob somepass",
"Add insecure account for using maxadmin over the network. E.g.:\n"
" MaxScale> add user bob somepass",
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0} },
{ NULL, 0, NULL, NULL, NULL,
{0, 0, 0} }
};
static void telnetdRemoveUser(DCB *, char *);
static void telnetdRemoveUser(DCB *, char *user, char *password);
/**
* The subcommands of the remove command
*/
struct subcommand removeoptions[] = {
{
"user",
1,
2,
telnetdRemoveUser,
"Remove existing maxscale user. Example : remove user john",
"Remove existing maxscale user. Example : remove user john",
{ARG_TYPE_STRING, 0, 0}
"Remove account for using maxadmin over the network. E.g.:\n"
" MaxAdmin> remove user bob somepass",
"Remove account for using maxadmin over the network. E.g.:\n"
" MaxAdmin> remove user bob somepass",
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0}
},
{
NULL, 0, NULL, NULL, NULL, {0, 0, 0}
@ -1302,63 +1330,61 @@ reload_config(DCB *dcb)
}
/**
* Add a new maxscale admin user
* Add a new remote (insecure, over the network) maxscale admin user
*
* @param dcb The DCB for messages
* @param user The user name
* @param dcb The DCB for messages
* @param user The user name
* @param user The user password
*/
static void
telnetdAddUser(DCB *dcb, char *user)
telnetdAddUser(DCB *dcb, char *user, char *password)
{
char *err;
const char *err;
if (admin_search_user(user))
if (admin_inet_user_exists(user))
{
dcb_printf(dcb, "User %s already exists.\n", user);
dcb_printf(dcb, "Account %s for remote (network) usage already exists.\n", user);
return;
}
if ((err = admin_add_user(user)) == NULL)
if ((err = admin_add_inet_user(user, password)) == NULL)
{
dcb_printf(dcb, "User %s has been successfully added.\n", user);
dcb_printf(dcb, "Account %s for remote (network) usage has been successfully added.\n", user);
}
else
{
dcb_printf(dcb, "Failed to add new user. %s\n", err);
dcb_printf(dcb, "Failed to add new remote account %s: %s.\n", user, err);
}
}
/**
* Remove a maxscale admin user
* Remove a remote (insecure, over the network) maxscale admin user
*
* @param dcb The DCB for messages
* @param user The user name
* @param dcb The DCB for messages
* @param user The user name
* @param user The user password
*/
static void telnetdRemoveUser(
DCB* dcb,
char* user)
static void telnetdRemoveUser(DCB *dcb, char *user, char *password)
{
char* err;
const char* err;
if (!admin_search_user(user))
if (!admin_inet_user_exists(user))
{
dcb_printf(dcb, "User %s doesn't exist.\n", user);
dcb_printf(dcb, "Account %s for remote (network) usage does not exist.\n", user);
return;
}
if ((err = admin_remove_user(user)) == NULL)
if ((err = admin_remove_inet_user(user, password)) == NULL)
{
dcb_printf(dcb, "User %s has been successfully removed.\n", user);
dcb_printf(dcb, "Account %s for remote (network) usage has been successfully removed.\n", user);
}
else
{
dcb_printf(dcb, "Failed to remove user %s. %s\n", user, err);
dcb_printf(dcb, "Failed to remove remote account %s: %s\n", user, err);
}
}
/**
* Print the adminsitration users
*
@ -1367,7 +1393,6 @@ static void telnetdRemoveUser(
static void
telnetdShowUsers(DCB *dcb)
{
dcb_printf(dcb, "Administration interface users:\n");
dcb_PrintAdminUsers(dcb);
}
@ -1869,6 +1894,60 @@ disable_maxlog()
mxs_log_set_maxlog_enabled(false);
}
/**
* Enable a Linux account
*
* @param dcb The DCB for messages
* @param user The Linux user name
*/
static void
enable_account(DCB *dcb, char *user)
{
const char *err;
if (admin_linux_account_enabled(user))
{
dcb_printf(dcb, "The Linux user %s has already been enabled.\n", user);
return;
}
if ((err = admin_enable_linux_account(user)) == NULL)
{
dcb_printf(dcb, "The Linux user %s has successfully been enabled.\n", user);
}
else
{
dcb_printf(dcb, "Failed to enable the Linux user %s: %s\n", user, err);
}
}
/**
* Disable a Linux account
*
* @param dcb The DCB for messages
* @param user The Linux user name
*/
static void
disable_account(DCB *dcb, char *user)
{
const char* err;
if (!admin_linux_account_enabled(user))
{
dcb_printf(dcb, "The Linux user %s has not been enabled.\n", user);
return;
}
if ((err = admin_disable_linux_account(user)) == NULL)
{
dcb_printf(dcb, "The Linux user %s has successfully been disabled.\n", user);
}
else
{
dcb_printf(dcb, "Failed to disable the Linux user %s: %s\n", user, err);
}
}
#if defined(FAKE_CODE)
static void fail_backendfd(void)
{

View File

@ -1016,12 +1016,18 @@ static BACKEND *get_root_master(BACKEND **servers)
{
if (servers[i] && (servers[i]->server->status & (SERVER_MASTER | SERVER_MAINT)) == SERVER_MASTER)
{
if (master_host && servers[i]->server->depth < master_host->server->depth)
if (master_host == NULL)
{
master_host = servers[i];
}
else if (master_host == NULL)
else if (servers[i]->server->depth < master_host->server->depth ||
(servers[i]->server->depth == master_host->server->depth &&
servers[i]->weight > master_host->weight))
{
/**
* This master has a lower depth than the candidate master or
* the depths are equal but this master has a higher weight
*/
master_host = servers[i];
}
}

View File

@ -1384,7 +1384,6 @@ static route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
}
else
{
/** hints don't affect on routing */
ss_dassert(trx_active ||
(QUERY_IS_TYPE(qtype, QUERY_TYPE_WRITE) ||
QUERY_IS_TYPE(qtype, QUERY_TYPE_MASTER_READ) ||