SEE ALSO
ctdb(1),
ctdbd(1),
diff -Nru samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.xml samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.xml
--- samba-4.5.8+dfsg/ctdb/doc/ctdb-tunables.7.xml 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/ctdb-tunables.7.xml 2017-01-11 07:55:14.000000000 +0000
@@ -30,6 +30,14 @@
+ Unless otherwise stated, tunables should be set to the same
+ value on all nodes. Setting tunables to different values across
+ nodes may produce unexpected results. Future releases may set
+ (some or most) tunables globally across the cluster but doing so
+ is currently a manual process.
+
+
+
The tunable variables are listed alphabetically.
@@ -128,24 +136,6 @@
- DeterministicIPs
- Default: 0
-
- When set to 1, ctdb will try to keep public IP addresses locked
- to specific nodes as far as possible. This makes it easier
- for debugging since you can know that as long as all nodes are
- healthy public IP X will always be hosted by node Y.
-
-
- The cost of using deterministic IP address assignment is that it
- disables part of the logic where ctdb tries to reduce the number
- of public IP assignment changes in the cluster. This tunable may
- increase the number of IP failover/failbacks that are performed
- on the cluster by a small margin.
-
-
-
-
DisableIPFailover
Default: 0
@@ -177,13 +167,12 @@
EnableBans
Default: 1
- This parameter allows ctdb to ban a node if the node is misbehaving.
+ This parameter allows ctdb to ban a node if the node is misbehaving.
When set to 0, this disables banning completely in the cluster
and thus nodes can not get banned, even it they break. Don't
- set to 0 unless you know what you are doing. You should set
- this to the same value on all nodes to avoid unexpected behaviour.
+ set to 0 unless you know what you are doing.
@@ -243,6 +232,70 @@
+ IPAllocAlgorithm
+ Default: 2
+
+ Selects the algorithm that CTDB should use when doing public
+ IP address allocation. Meaningful values are:
+
+
+
+ 0
+
+
+ Deterministic IP address allocation.
+
+
+ This is a simple and fast option. However, it can cause
+ unnecessary address movement during fail-over because
+ each address has a "home" node. Works badly when some
+ nodes do not have any addresses defined. Should be used
+ with care when addresses are defined across multiple
+ networks.
+
+
+
+
+ 1
+
+
+ Non-deterministic IP address allocation.
+
+
+ This is a relatively fast option that attempts to do a
+ minimise unnecessary address movements. Addresses do
+ not have a "home" node. Rebalancing is limited but it
+ usually adequate. Works badly when addresses are
+ defined across multiple networks.
+
+
+
+
+ 2
+
+
+ LCP2 IP address allocation.
+
+
+ Uses a heuristic to assign addresses defined across
+ multiple networks, usually balancing addresses on each
+ network evenly across nodes. Addresses do not have a
+ "home" node. Minimises unnecessary address movements.
+ The algorithm is complex, so is slower than other
+ choices for a large number of addresses. However, it
+ can calculate an optimal assignment of 900 addresses in
+ under 10 seconds on modern hardware.
+
+
+
+
+
+ If the specified value is not one of these then the default
+ will be used.
+
+
+
+
KeepaliveInterval
Default: 5
@@ -270,14 +323,6 @@
- LCP2PublicIPs
- Default: 1
-
- When set to 1, ctdb uses the LCP2 ip allocation algorithm.
-
-
-
-
LockProcessesPerDB
Default: 200
@@ -362,10 +407,9 @@
If no nodes are HEALTHY then by default ctdb will happily host
public IPs on disabled (unhealthy or administratively disabled)
nodes. This can cause problems, for example if the underlying
- cluster filesystem is not mounted. When set to 1 on a node and
- that node is disabled, any IPs hosted by this node will be
- released and the node will not takeover any IPs until it is no
- longer disabled.
+ cluster filesystem is not mounted. When set to 1 and a node
+ is disabled, any IPs hosted by this node will be released and
+ the node will not takeover any IPs until it is no longer disabled.
@@ -374,9 +418,11 @@
Default: 0
When set to 1, ctdb will not allow IP addresses to be failed
- over onto this node. Any IP addresses that the node currently
- hosts will remain on the node but no new IP addresses can be
- failed over to the node.
+ over to other nodes. Any IP addresses already hosted on
+ healthy nodes will remain. Usually IP addresses hosted on
+ unhealthy nodes will also remain, if NoIPHostOnAllDisabled is
+ 0. However, if NoIPHostOnAllDisabled is 1 then IP addresses
+ will be released by unhealthy nodes and will become un-hosted.
@@ -461,27 +507,6 @@
-
- RecoverPDBBySeqNum
- Default: 1
-
- When set to zero, database recovery for persistent databases is
- record-by-record and recovery process simply collects the most
- recent version of every individual record.
-
-
- When set to non-zero, persistent databases will instead be
- recovered as a whole db and not by individual records. The
- node that contains the highest value stored in the record
- "__db_sequence_number__" is selected and the copy of that nodes
- database is used as the recovered database.
-
-
- By default, recovery of persistent databses is done using
- __db_sequence_number__ record.
-
-
-
RecoverTimeout
Default: 120
diff -Nru samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1 samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1
--- samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1 2016-10-24 19:44:55.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1 2017-06-06 07:49:57.000000000 +0000
@@ -2,12 +2,12 @@
.\" Title: ltdbtool
.\" Author:
.\" Generator: DocBook XSL Stylesheets v1.78.1
-.\" Date: 10/24/2016
+.\" Date: 06/06/2017
.\" Manual: CTDB - clustered TDB database
.\" Source: ctdb
.\" Language: English
.\"
-.TH "LTDBTOOL" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database"
+.TH "LTDBTOOL" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
diff -Nru samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1.html samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1.html
--- samba-4.5.8+dfsg/ctdb/doc/ltdbtool.1.html 2016-10-24 19:44:55.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/ltdbtool.1.html 2017-06-06 07:49:57.000000000 +0000
@@ -1,4 +1,4 @@
-ltdbtoolName
ltdbtool — manipulate CTDB's local TDB files
Synopsis
ltdbtool
[OPTION
...] {COMMAND
} [COMMAND-ARGS
]
DESCRIPTION
+
ltdbtoolName
ltdbtool — manipulate CTDB's local TDB files
Synopsis
ltdbtool
[OPTION
...] {COMMAND
} [COMMAND-ARGS
]
DESCRIPTION
ltdbtool is a utility to manipulate CTDB's local TDB databases
(LTDBs) without connecting to a CTDB daemon.
@@ -11,7 +11,7 @@
by adding or removing CTDB headers and
convert between 64 and 32 bit LTDBs where the CTDB record
headers differ by 4 bytes of padding.
-
OPTIONS
- -e
Dump empty records. These are normally excluded.
- -p
Dump with header information, similar to "ctdb catdb".
@@ -37,7 +37,7 @@
output database in bytes.
- -h
Print help text.
-
COMMANDS
- help
Print help text.
- dump
IDB
Dump the contents of an LTDB input file IDB to standard
@@ -47,7 +47,7 @@
Copy an LTDB input file IDB to output file ODB, optionally
adding or removing CTDB headers.
-
EXAMPLES
Print a local tdb in "tdbdump" style:
ltdbtool dump idmap2.tdb.0
@@ -75,7 +75,7 @@
Add a default header:
ltdbtool convert -s0 idmap.tdb idmap2.tdb.0
-
SEE ALSO
ctdb(1),
tdbdump(1),
diff -Nru samba-4.5.8+dfsg/ctdb/doc/Makefile samba-4.6.5+dfsg/ctdb/doc/Makefile
--- samba-4.5.8+dfsg/ctdb/doc/Makefile 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/Makefile 1970-01-01 00:00:00.000000000 +0000
@@ -1,22 +0,0 @@
-DOCS = ctdb.1 ctdb.1.html \
- ctdb_diagnostics.1 ctdb_diagnostics.1.html \
- ctdbd.1 ctdbd.1.html \
- ctdbd_wrapper.1 ctdbd_wrapper.1.html \
- onnode.1 onnode.1.html \
- ltdbtool.1 ltdbtool.1.html \
- ping_pong.1 ping_pong.1.html \
- ctdbd.conf.5 ctdbd.conf.5.html \
- ctdb.7 ctdb.7.html \
- ctdb-statistics.7 ctdb-statistics.7.html \
- ctdb-tunables.7 ctdb-tunables.7.html
-
-all: $(DOCS)
-
-%: %.xml
- xsltproc -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
-
-%.html: %.xml
- xsltproc -o $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $<
-
-distclean:
- rm -f $(DOCS)
diff -Nru samba-4.5.8+dfsg/ctdb/doc/onnode.1 samba-4.6.5+dfsg/ctdb/doc/onnode.1
--- samba-4.5.8+dfsg/ctdb/doc/onnode.1 2016-10-24 19:44:55.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/onnode.1 2017-06-06 07:49:58.000000000 +0000
@@ -2,12 +2,12 @@
.\" Title: onnode
.\" Author:
.\" Generator: DocBook XSL Stylesheets v1.78.1
-.\" Date: 10/24/2016
+.\" Date: 06/06/2017
.\" Manual: CTDB - clustered TDB database
.\" Source: ctdb
.\" Language: English
.\"
-.TH "ONNODE" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database"
+.TH "ONNODE" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
diff -Nru samba-4.5.8+dfsg/ctdb/doc/onnode.1.html samba-4.6.5+dfsg/ctdb/doc/onnode.1.html
--- samba-4.5.8+dfsg/ctdb/doc/onnode.1.html 2016-10-24 19:44:55.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/onnode.1.html 2017-06-06 07:49:59.000000000 +0000
@@ -1,4 +1,4 @@
-
onnodeName
onnode — run commands on CTDB cluster nodes
Synopsis
onnode
[OPTION
...] {NODES
} {COMMAND
}
DESCRIPTION
+
onnodeName
onnode — run commands on CTDB cluster nodes
Synopsis
onnode
[OPTION
...] {NODES
} {COMMAND
}
DESCRIPTION
onnode is a utility to run commands on a specific node of a CTDB
cluster, or on all nodes.
@@ -9,7 +9,7 @@
COMMAND
can be any shell command. The
onnode utility uses ssh or rsh to connect to the remote nodes
and run the command.
-
OPTIONS
OPTIONS
- -c
Execute COMMAND in the current working directory on the
specified nodes.
- -f
FILENAME
@@ -49,7 +49,7 @@
more than one node is specified.
- -h, --help
Show a short usage guide.
-
NODES SPECIFICATION
Nodes can be specified via numeric node numbers (from 0 to N-1)
or mnemonics. Multiple nodes are specified using lists of
nodes, separated by commas, and ranges of numeric node numbers,
@@ -68,7 +68,7 @@
unhealthy.
con | connected
All nodes that are not disconnected.
-
EXAMPLES
The following command would show the process ID of ctdbd on all nodes
onnode all ctdb getpid
@@ -87,14 +87,14 @@
directory, in parallel, on nodes 0, 2, 3 and 4.
onnode -c -p 0,2-4 ./foo
-
ENVIRONMENT
ENVIRONMENT
CTDB_BASE
Directory containing CTDB configuration files. The
default is /usr/local/etc/ctdb
.
CTDB_NODES_FILE
Name of alternative nodes file to use instead of the
default. See the FILES section for
more details.
-
FILES
/usr/local/etc/ctdb/nodes
+
FILES
/usr/local/etc/ctdb/nodes
Default file containing a list of each node's IP address
or hostname.
@@ -119,7 +119,7 @@
SSH
to something other than "ssh". In this
case the -t option is ignored. For example, the
administrator may choose to use use rsh instead of ssh.
-
SEE ALSO
ctdb(7),
http://ctdb.samba.org/
diff -Nru samba-4.5.8+dfsg/ctdb/doc/ping_pong.1 samba-4.6.5+dfsg/ctdb/doc/ping_pong.1
--- samba-4.5.8+dfsg/ctdb/doc/ping_pong.1 2016-10-24 19:44:55.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/ping_pong.1 2017-06-06 07:49:57.000000000 +0000
@@ -2,12 +2,12 @@
.\" Title: ping_pong
.\" Author:
.\" Generator: DocBook XSL Stylesheets v1.78.1
-.\" Date: 10/24/2016
+.\" Date: 06/06/2017
.\" Manual: CTDB - clustered TDB database
.\" Source: ctdb
.\" Language: English
.\"
-.TH "PING_PONG" "1" "10/24/2016" "ctdb" "CTDB \- clustered TDB database"
+.TH "PING_PONG" "1" "06/06/2017" "ctdb" "CTDB \- clustered TDB database"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
diff -Nru samba-4.5.8+dfsg/ctdb/doc/ping_pong.1.html samba-4.6.5+dfsg/ctdb/doc/ping_pong.1.html
--- samba-4.5.8+dfsg/ctdb/doc/ping_pong.1.html 2016-10-24 19:44:56.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/doc/ping_pong.1.html 2017-06-06 07:49:57.000000000 +0000
@@ -1,4 +1,4 @@
-
ping_pongName
ping_pong — measures the ping-pong byte range lock latency
Synopsis
ping_pong
{ -r | -w | -rw } [-m] [-c] {FILENAME
} {NUM-LOCKS
}
DESCRIPTION
+
ping_pongName
ping_pong — measures the ping-pong byte range lock latency
Synopsis
ping_pong
{ -r | -w | -rw } [-m] [-c] {FILENAME
} {NUM-LOCKS
}
DESCRIPTION
ping_pong measures the byte range lock latency. It is especially
useful on a cluster of nodes sharing a common lock manager as it
will give some indication of the lock manager's performance
@@ -9,7 +9,7 @@
NUM-LOCKS is the number of byte range locks, so needs to be
(strictly) greater than the number of nodes in the cluster.
-
OPTIONS
OPTIONS
- -r
test read performance
- -w
test write performance
@@ -17,7 +17,7 @@
use mmap
- -c
validate the locks
-
EXAMPLES
Testing lock coherence
ping_pong test.dat N
@@ -29,7 +29,7 @@
Testing IO coherence
ping_pong -rw test.dat N
-
SEE ALSO
ctdb(7),
https://wiki.samba.org/index.php/Ping_pong
diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_client.h samba-4.6.5+dfsg/ctdb/include/ctdb_client.h
--- samba-4.5.8+dfsg/ctdb/include/ctdb_client.h 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/include/ctdb_client.h 2017-01-11 07:55:14.000000000 +0000
@@ -528,12 +528,6 @@
int ctdb_ctrl_recd_ping(struct ctdb_context *ctdb);
-int ctdb_ctrl_getscriptstatus(struct ctdb_context *ctdb,
- struct timeval timeout, uint32_t destnode,
- TALLOC_CTX *mem_ctx,
- enum ctdb_event type,
- struct ctdb_script_list_old **script_status);
-
int ctdb_ctrl_report_recd_lock_latency(struct ctdb_context *ctdb,
struct timeval timeout, double latency);
@@ -553,11 +547,6 @@
struct timeval timeout, uint32_t destnode,
uint32_t recmasterrole);
-int ctdb_ctrl_enablescript(struct ctdb_context *ctdb, struct timeval timeout,
- uint32_t destnode, const char *script);
-int ctdb_ctrl_disablescript(struct ctdb_context *ctdb, struct timeval timeout,
- uint32_t destnode, const char *script);
-
int ctdb_ctrl_set_ban(struct ctdb_context *ctdb, struct timeval timeout,
uint32_t destnode, struct ctdb_ban_state *bantime);
int ctdb_ctrl_get_ban(struct ctdb_context *ctdb, struct timeval timeout,
diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_private.h samba-4.6.5+dfsg/ctdb/include/ctdb_private.h
--- samba-4.5.8+dfsg/ctdb/include/ctdb_private.h 2016-09-13 08:21:35.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/include/ctdb_private.h 2017-01-11 07:55:14.000000000 +0000
@@ -45,12 +45,6 @@
*/
#define ctdb_validate_pnn(ctdb, pnn) (((uint32_t)(pnn)) < (ctdb)->num_nodes)
-
-/* called from the queue code when a packet comes in. Called with data==NULL
- on error */
-typedef void (*ctdb_queue_cb_fn_t)(uint8_t *data, size_t length,
- void *private_data);
-
/* used for callbacks in ctdb_control requests */
typedef void (*ctdb_control_callback_fn_t)(struct ctdb_context *,
int32_t status, TDB_DATA data,
@@ -248,10 +242,10 @@
struct ctdb_cluster_mutex_handle;
+struct eventd_context;
enum ctdb_freeze_mode {CTDB_FREEZE_NONE, CTDB_FREEZE_PENDING, CTDB_FREEZE_FROZEN};
-#define NUM_DB_PRIORITIES 3
/* main state of the ctdb daemon */
struct ctdb_context {
struct tevent_context *ev;
@@ -318,11 +312,7 @@
TALLOC_CTX *recd_ctx; /* a context used to track recoverd monitoring events */
TALLOC_CTX *release_ips_ctx; /* a context used to automatically drop all IPs if we fail to recover the node */
- TALLOC_CTX *event_script_ctx;
- int active_events;
-
- struct ctdb_event_script_state *current_monitor;
- struct ctdb_script_list_old *last_status[CTDB_EVENT_MAX];
+ struct eventd_context *ectx;
TALLOC_CTX *banning_ctx;
@@ -337,9 +327,6 @@
/* if we are a child process, do we have a domain socket to send controls on */
bool can_send_controls;
- /* list of event script callback functions that are active */
- struct event_script_callback *script_callbacks;
-
struct ctdb_reloadips_handle *reload_ips;
const char *nodes_file;
@@ -600,17 +587,16 @@
void ctdb_shutdown_sequence(struct ctdb_context *ctdb, int exit_code);
-int switch_from_server_to_client(struct ctdb_context *ctdb,
- const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+int switch_from_server_to_client(struct ctdb_context *ctdb);
/* From server/ctdb_fork.c */
-void ctdb_set_child_info(TALLOC_CTX *mem_ctx, const char *child_name_fmt,
- ...) PRINTF_ATTRIBUTE(2,3);
-
void ctdb_track_child(struct ctdb_context *ctdb, pid_t pid);
pid_t ctdb_fork(struct ctdb_context *ctdb);
+pid_t ctdb_vfork_exec(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
+ const char *helper, int helper_argc,
+ const char **helper_argv);
struct tevent_signal *ctdb_init_sigchld(struct ctdb_context *ctdb);
@@ -675,15 +661,8 @@
/* from ctdb_logging.c */
-extern const char *debug_extra;
-
-typedef int (*ctdb_log_setup_fn_t)(TALLOC_CTX *mem_ctx,
- const char *logging,
- const char *app_name);
-
-void ctdb_log_register_backend(const char *prefix, ctdb_log_setup_fn_t init);
-
-bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging);
+bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging,
+ const char *debug_level);
struct ctdb_log_state *ctdb_vfork_with_logging(TALLOC_CTX *mem_ctx,
struct ctdb_context *ctdb,
@@ -696,7 +675,6 @@
void *logfn_private, pid_t *pid);
int ctdb_set_child_logging(struct ctdb_context *ctdb);
-int ctdb_init_tevent_logging(struct ctdb_context *ctdb);
/* from ctdb_logging_file.c */
@@ -909,9 +887,6 @@
int ctdb_set_public_addresses(struct ctdb_context *ctdb, bool check_addresses);
-int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map_old *nodemap,
- uint32_t *force_rebalance_nodes);
-
int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id,
TDB_DATA indata);
int32_t ctdb_control_tcp_add(struct ctdb_context *ctdb, TDB_DATA indata,
@@ -1011,9 +986,8 @@
/* from eventscript.c */
-int32_t ctdb_control_get_event_script_status(struct ctdb_context *ctdb,
- uint32_t call_type,
- TDB_DATA *outdata);
+int ctdb_start_eventd(struct ctdb_context *ctdb);
+void ctdb_stop_eventd(struct ctdb_context *ctdb);
int ctdb_event_script_callback(struct ctdb_context *ctdb,
TALLOC_CTX *mem_ctx,
@@ -1030,11 +1004,4 @@
int ctdb_event_script(struct ctdb_context *ctdb,
enum ctdb_event call);
-int32_t ctdb_run_eventscripts(struct ctdb_context *ctdb,
- struct ctdb_req_control_old *c,
- TDB_DATA data, bool *async_reply);
-
-int32_t ctdb_control_enable_script(struct ctdb_context *ctdb, TDB_DATA indata);
-int32_t ctdb_control_disable_script(struct ctdb_context *ctdb, TDB_DATA indata);
-
#endif
diff -Nru samba-4.5.8+dfsg/ctdb/include/ctdb_protocol.h samba-4.6.5+dfsg/ctdb/include/ctdb_protocol.h
--- samba-4.5.8+dfsg/ctdb/include/ctdb_protocol.h 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/include/ctdb_protocol.h 2017-01-11 07:55:14.000000000 +0000
@@ -266,8 +266,6 @@
};
-#define CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE 0x00010000
-
struct ctdb_public_ip_info_old {
struct ctdb_public_ip ip;
uint32_t active_idx;
diff -Nru samba-4.5.8+dfsg/ctdb/Makefile samba-4.6.5+dfsg/ctdb/Makefile
--- samba-4.5.8+dfsg/ctdb/Makefile 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/Makefile 2017-02-28 20:04:57.000000000 +0000
@@ -27,6 +27,10 @@
@touch .tmplock
@WAFLOCK=.tmplock $(WAF) show_version
+manpages:
+ touch .tmplock
+ WAFLOCK=.tmplock $(WAF) manpages
+
dist:
touch .tmplock
WAFLOCK=.tmplock $(WAF) dist
diff -Nru samba-4.5.8+dfsg/ctdb/packaging/RPM/ctdb.spec.in samba-4.6.5+dfsg/ctdb/packaging/RPM/ctdb.spec.in
--- samba-4.5.8+dfsg/ctdb/packaging/RPM/ctdb.spec.in 2016-12-05 08:18:44.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/packaging/RPM/ctdb.spec.in 2017-01-11 07:55:14.000000000 +0000
@@ -38,7 +38,7 @@
# Required minimum library versions when building with system libraries
%define libtalloc_version 2.0.8
-%define libtdb_version 1.2.11
+%define libtdb_version 1.3.11
%define libtevent_version 0.9.16
%if ! %with_included_talloc
@@ -213,10 +213,12 @@
%{_bindir}/ctdb_diagnostics
%{_bindir}/onnode
%dir %{_libexecdir}/ctdb
+%{_libexecdir}/ctdb/ctdb_eventd
%{_libexecdir}/ctdb/ctdb_lock_helper
-%{_libexecdir}/ctdb/ctdb_event_helper
%{_libexecdir}/ctdb/ctdb_recovery_helper
+%{_libexecdir}/ctdb/ctdb_takeover_helper
%{_libexecdir}/ctdb/ctdb_mutex_fcntl_helper
+%{_libexecdir}/ctdb/ctdb_event
%{_libexecdir}/ctdb/ctdb_natgw
%{_libexecdir}/ctdb/ctdb_lvs
%{_libexecdir}/ctdb/ctdb_killtcp
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_api.h samba-4.6.5+dfsg/ctdb/protocol/protocol_api.h
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_api.h 2017-01-17 19:55:44.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_api.h 2017-01-11 07:55:14.000000000 +0000
@@ -204,10 +204,10 @@
void ctdb_req_control_get_debug(struct ctdb_req_control *request);
int ctdb_reply_control_get_debug(struct ctdb_reply_control *reply,
- uint32_t *debug_level);
+ int *debug_level);
void ctdb_req_control_set_debug(struct ctdb_req_control *request,
- uint32_t debug_level);
+ int debug_level);
int ctdb_reply_control_set_debug(struct ctdb_reply_control *reply);
void ctdb_req_control_get_dbmap(struct ctdb_req_control *request);
@@ -397,10 +397,6 @@
struct ctdb_addr_info *addr_info);
int ctdb_reply_control_del_public_ip(struct ctdb_reply_control *reply);
-void ctdb_req_control_run_eventscripts(struct ctdb_req_control *request,
- const char *event_str);
-int ctdb_reply_control_run_eventscripts(struct ctdb_reply_control *reply);
-
void ctdb_req_control_get_capabilities(struct ctdb_req_control *request);
int ctdb_reply_control_get_capabilities(struct ctdb_reply_control *reply,
uint32_t *caps);
@@ -416,7 +412,8 @@
struct ctdb_public_ip *pubip);
int ctdb_reply_control_takeover_ip(struct ctdb_reply_control *reply);
-void ctdb_req_control_get_public_ips(struct ctdb_req_control *request);
+void ctdb_req_control_get_public_ips(struct ctdb_req_control *request,
+ bool available_only);
int ctdb_reply_control_get_public_ips(struct ctdb_reply_control *reply,
TALLOC_CTX *mem_ctx,
struct ctdb_public_ip_list **pubip_list);
@@ -426,12 +423,6 @@
TALLOC_CTX *mem_ctx,
struct ctdb_node_map **nodemap);
-void ctdb_req_control_get_event_script_status(struct ctdb_req_control *request,
- uint32_t event);
-int ctdb_reply_control_get_event_script_status(struct ctdb_reply_control *reply,
- TALLOC_CTX *mem_ctx,
- struct ctdb_script_list **script_list);
-
void ctdb_req_control_traverse_kill(struct ctdb_req_control *request,
struct ctdb_traverse_start *traverse);
int ctdb_reply_control_traverse_kill(struct ctdb_reply_control *reply);
@@ -459,14 +450,6 @@
uint32_t recmaster_role);
int ctdb_reply_control_set_recmasterrole(struct ctdb_reply_control *reply);
-void ctdb_req_control_enable_script(struct ctdb_req_control *request,
- const char *script);
-int ctdb_reply_control_enable_script(struct ctdb_reply_control *reply);
-
-void ctdb_req_control_disable_script(struct ctdb_req_control *request,
- const char *script);
-int ctdb_reply_control_disable_script(struct ctdb_reply_control *reply);
-
void ctdb_req_control_set_ban_state(struct ctdb_req_control *request,
struct ctdb_ban_state *ban_state);
int ctdb_reply_control_set_ban_state(struct ctdb_reply_control *reply);
@@ -644,6 +627,28 @@
TALLOC_CTX *mem_ctx,
struct ctdb_req_message_data *c);
+/* From protocol/protocol_event.c */
+
+void ctdb_event_header_fill(struct ctdb_event_header *h, uint32_t reqid);
+
+size_t ctdb_event_request_len(struct ctdb_event_request *in);
+
+int ctdb_event_request_push(struct ctdb_event_request *in,
+ uint8_t *buf, size_t *buflen);
+
+int ctdb_event_request_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *out);
+
+size_t ctdb_event_reply_len(struct ctdb_event_reply *in);
+
+int ctdb_event_reply_push(struct ctdb_event_reply *in,
+ uint8_t *buf, size_t *buflen);
+
+int ctdb_event_reply_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply *out);
+
/* From protocol/protocol_packet.c */
int ctdb_allocate_pkt(TALLOC_CTX *mem_ctx, size_t datalen,
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_client.c samba-4.6.5+dfsg/ctdb/protocol/protocol_client.c
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_client.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_client.c 2017-01-11 07:55:14.000000000 +0000
@@ -222,14 +222,14 @@
}
int ctdb_reply_control_get_debug(struct ctdb_reply_control *reply,
- uint32_t *loglevel)
+ int *loglevel)
{
if (reply->rdata.opcode != CTDB_CONTROL_GET_DEBUG) {
return EPROTO;
}
if (reply->status == 0) {
- *loglevel = reply->rdata.data.loglevel;
+ *loglevel = (int)reply->rdata.data.loglevel;
}
return reply->status;
}
@@ -237,7 +237,7 @@
/* CTDB_CONTROL_SET_DEBUG */
void ctdb_req_control_set_debug(struct ctdb_req_control *request,
- uint32_t loglevel)
+ int loglevel)
{
request->opcode = CTDB_CONTROL_SET_DEBUG;
request->pad = 0;
@@ -246,7 +246,7 @@
request->flags = 0;
request->rdata.opcode = CTDB_CONTROL_SET_DEBUG;
- request->rdata.data.loglevel = loglevel;
+ request->rdata.data.loglevel = (uint32_t)loglevel;
}
int ctdb_reply_control_set_debug(struct ctdb_reply_control *reply)
@@ -1272,27 +1272,6 @@
return ctdb_reply_control_generic(reply, CTDB_CONTROL_DEL_PUBLIC_IP);
}
-/* CTDB_CONTROL_RUN_EVENTSCRIPTS */
-
-void ctdb_req_control_run_eventscripts(struct ctdb_req_control *request,
- const char *event_str)
-{
- request->opcode = CTDB_CONTROL_RUN_EVENTSCRIPTS;
- request->pad = 0;
- request->srvid = 0;
- request->client_id = 0;
- request->flags = 0;
-
- request->rdata.opcode = CTDB_CONTROL_RUN_EVENTSCRIPTS;
- request->rdata.data.event_str = event_str;
-}
-
-int ctdb_reply_control_run_eventscripts(struct ctdb_reply_control *reply)
-{
- return ctdb_reply_control_generic(reply,
- CTDB_CONTROL_RUN_EVENTSCRIPTS);
-}
-
/* CTDB_CONTROL_GET_CAPABILITIES */
void ctdb_req_control_get_capabilities(struct ctdb_req_control *request)
@@ -1379,7 +1358,8 @@
/* CTDB_CONTROL_GET_PUBLIC_IPS */
-void ctdb_req_control_get_public_ips(struct ctdb_req_control *request)
+void ctdb_req_control_get_public_ips(struct ctdb_req_control *request,
+ bool available_only)
{
request->opcode = CTDB_CONTROL_GET_PUBLIC_IPS;
request->pad = 0;
@@ -1388,6 +1368,9 @@
request->flags = 0;
request->rdata.opcode = CTDB_CONTROL_GET_PUBLIC_IPS;
+ if (available_only) {
+ request->flags = CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE;
+ }
}
int ctdb_reply_control_get_public_ips(struct ctdb_reply_control *reply,
@@ -1432,35 +1415,6 @@
return reply->status;
}
-/* CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS */
-
-void ctdb_req_control_get_event_script_status(struct ctdb_req_control *request,
- uint32_t event)
-{
- request->opcode = CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS;
- request->pad = 0;
- request->srvid = 0;
- request->client_id = 0;
- request->flags = 0;
-
- request->rdata.opcode = CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS;
- request->rdata.data.event = event;
-}
-
-int ctdb_reply_control_get_event_script_status(struct ctdb_reply_control *reply,
- TALLOC_CTX *mem_ctx,
- struct ctdb_script_list **slist)
-{
- if (reply->rdata.opcode != CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS) {
- return EPROTO;
- }
-
- if (reply->status == 0) {
- *slist = talloc_steal(mem_ctx, reply->rdata.data.script_list);
- }
- return reply->status;
-}
-
/* CTDB_CONTROL_TRAVERSE_KILL */
void ctdb_req_control_traverse_kill(struct ctdb_req_control *request,
@@ -1607,46 +1561,6 @@
CTDB_CONTROL_SET_RECMASTERROLE);
}
-/* CTDB_CONTROL_ENABLE_SCRIPT */
-
-void ctdb_req_control_enable_script(struct ctdb_req_control *request,
- const char *script)
-{
- request->opcode = CTDB_CONTROL_ENABLE_SCRIPT;
- request->pad = 0;
- request->srvid = 0;
- request->client_id = 0;
- request->flags = 0;
-
- request->rdata.opcode = CTDB_CONTROL_ENABLE_SCRIPT;
- request->rdata.data.script = script;
-}
-
-int ctdb_reply_control_enable_script(struct ctdb_reply_control *reply)
-{
- return ctdb_reply_control_generic(reply, CTDB_CONTROL_ENABLE_SCRIPT);
-}
-
-/* CTDB_CONTROL_DISABLE_SCRIPT */
-
-void ctdb_req_control_disable_script(struct ctdb_req_control *request,
- const char *script)
-{
- request->opcode = CTDB_CONTROL_DISABLE_SCRIPT;
- request->pad = 0;
- request->srvid = 0;
- request->client_id = 0;
- request->flags = 0;
-
- request->rdata.opcode = CTDB_CONTROL_DISABLE_SCRIPT;
- request->rdata.data.script = script;
-}
-
-int ctdb_reply_control_disable_script(struct ctdb_reply_control *reply)
-{
- return ctdb_reply_control_generic(reply, CTDB_CONTROL_DISABLE_SCRIPT);
-}
-
/* CTDB_CONTROL_SET_BAN_STATE */
void ctdb_req_control_set_ban_state(struct ctdb_req_control *request,
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_control.c samba-4.6.5+dfsg/ctdb/protocol/protocol_control.c
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_control.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_control.c 2017-01-11 07:55:14.000000000 +0000
@@ -253,10 +253,6 @@
len = ctdb_addr_info_len(cd->data.addr_info);
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- len = ctdb_string_len(cd->data.event_str);
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
break;
@@ -277,10 +273,6 @@
case CTDB_CONTROL_GET_NODEMAP:
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- len = ctdb_uint32_len(cd->data.event);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
len = ctdb_traverse_start_len(cd->data.traverse_start);
break;
@@ -306,14 +298,6 @@
len = ctdb_uint32_len(cd->data.role);
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- len = ctdb_string_len(cd->data.script);
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- len = ctdb_string_len(cd->data.script);
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
len = ctdb_ban_state_len(cd->data.ban_state);
break;
@@ -574,10 +558,6 @@
ctdb_addr_info_push(cd->data.addr_info, buf);
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- ctdb_string_push(cd->data.event_str, buf);
- break;
-
case CTDB_CONTROL_RELEASE_IP:
ctdb_public_ip_push(cd->data.pubip, buf);
break;
@@ -586,10 +566,6 @@
ctdb_public_ip_push(cd->data.pubip, buf);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- ctdb_uint32_push(cd->data.event, buf);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
ctdb_traverse_start_push(cd->data.traverse_start, buf);
break;
@@ -606,14 +582,6 @@
ctdb_uint32_push(cd->data.role, buf);
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- ctdb_string_push(cd->data.script, buf);
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- ctdb_string_push(cd->data.script, buf);
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
ctdb_ban_state_push(cd->data.ban_state, buf);
break;
@@ -887,11 +855,6 @@
&cd->data.addr_info);
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- ret = ctdb_string_pull(buf, buflen, mem_ctx,
- &cd->data.event_str);
- break;
-
case CTDB_CONTROL_RELEASE_IP:
ret = ctdb_public_ip_pull(buf, buflen, mem_ctx,
&cd->data.pubip);
@@ -902,11 +865,6 @@
&cd->data.pubip);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- ret = ctdb_uint32_pull(buf, buflen, mem_ctx,
- &cd->data.event);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
ret = ctdb_traverse_start_pull(buf, buflen, mem_ctx,
&cd->data.traverse_start);
@@ -927,16 +885,6 @@
&cd->data.role);
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- ret = ctdb_string_pull(buf, buflen, mem_ctx,
- &cd->data.script);
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- ret = ctdb_string_pull(buf, buflen, mem_ctx,
- &cd->data.script);
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
ret = ctdb_ban_state_pull(buf, buflen, mem_ctx,
&cd->data.ban_state);
@@ -1269,9 +1217,6 @@
case CTDB_CONTROL_DEL_PUBLIC_IP:
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
len = ctdb_uint32_len(cd->data.caps);
break;
@@ -1293,10 +1238,6 @@
len = ctdb_node_map_len(cd->data.nodemap);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- len = ctdb_script_list_len(cd->data.script_list);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
break;
@@ -1319,12 +1260,6 @@
case CTDB_CONTROL_SET_RECMASTERROLE:
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
break;
@@ -1539,10 +1474,6 @@
ctdb_node_map_push(cd->data.nodemap, buf);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- ctdb_script_list_push(cd->data.script_list, buf);
- break;
-
case CTDB_CONTROL_GET_RECLOCK_FILE:
ctdb_string_push(cd->data.reclock_file, buf);
break;
@@ -1716,11 +1647,6 @@
&cd->data.nodemap);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- ret = ctdb_script_list_pull(buf, buflen, mem_ctx,
- &cd->data.script_list);
- break;
-
case CTDB_CONTROL_GET_RECLOCK_FILE:
ret = ctdb_string_pull(buf, buflen, mem_ctx,
&cd->data.reclock_file);
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_event.c samba-4.6.5+dfsg/ctdb/protocol/protocol_event.c
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_event.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_event.c 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,848 @@
+/*
+ CTDB eventd protocol marshalling
+
+ Copyright (C) Amitay Isaacs 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+#include "system/network.h"
+
+#include
+
+#include "protocol.h"
+#include "protocol_private.h"
+#include "protocol_api.h"
+
+static size_t ctdb_event_len(enum ctdb_event in)
+{
+ return ctdb_uint32_len((uint32_t)in);
+}
+
+static void ctdb_event_push(enum ctdb_event in, uint8_t *buf)
+{
+ ctdb_uint32_push((uint32_t)in, buf);
+}
+
+static int ctdb_event_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx, enum ctdb_event *out)
+{
+ uint32_t uint32_value;
+ enum ctdb_event value;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value);
+ if (ret != 0) {
+ return ret;
+ }
+
+ switch (uint32_value) {
+ case 0:
+ value = CTDB_EVENT_INIT;
+ break;
+
+ case 1:
+ value = CTDB_EVENT_SETUP;
+ break;
+
+ case 2:
+ value = CTDB_EVENT_STARTUP;
+ break;
+
+ case 3:
+ value = CTDB_EVENT_START_RECOVERY;
+ break;
+
+ case 4:
+ value = CTDB_EVENT_RECOVERED;
+ break;
+
+ case 5:
+ value = CTDB_EVENT_TAKE_IP;
+ break;
+
+ case 6:
+ value = CTDB_EVENT_RELEASE_IP;
+ break;
+
+ case 7:
+ value = CTDB_EVENT_STOPPED;
+ break;
+
+ case 8:
+ value = CTDB_EVENT_MONITOR;
+ break;
+
+ case 9:
+ value = CTDB_EVENT_STATUS;
+ break;
+
+ case 10:
+ value = CTDB_EVENT_SHUTDOWN;
+ break;
+
+ case 11:
+ value = CTDB_EVENT_RELOAD;
+ break;
+
+ case 12:
+ value = CTDB_EVENT_UPDATE_IP;
+ break;
+
+ case 13:
+ value = CTDB_EVENT_IPREALLOCATED;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ *out = value;
+ return 0;
+}
+
+static size_t ctdb_event_command_len(enum ctdb_event_command in)
+{
+ return ctdb_uint32_len((uint32_t)in);
+}
+
+static void ctdb_event_command_push(enum ctdb_event_command in, uint8_t *buf)
+{
+ ctdb_uint32_push((uint32_t)in, buf);
+}
+
+static int ctdb_event_command_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ enum ctdb_event_command *out)
+{
+ uint32_t uint32_value;
+ enum ctdb_event_command value;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value);
+ if (ret != 0) {
+ return ret;
+ }
+
+ switch (uint32_value) {
+ case 1:
+ value = CTDB_EVENT_COMMAND_RUN;
+ break;
+
+ case 2:
+ value = CTDB_EVENT_COMMAND_STATUS;
+ break;
+
+ case 3:
+ value = CTDB_EVENT_COMMAND_SCRIPT_LIST;
+ break;
+
+ case 4:
+ value = CTDB_EVENT_COMMAND_SCRIPT_ENABLE;
+ break;
+
+ case 5:
+ value = CTDB_EVENT_COMMAND_SCRIPT_DISABLE;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ *out = value;
+ return 0;
+}
+
+static size_t ctdb_event_status_state_len(enum ctdb_event_status_state in)
+{
+ return ctdb_uint32_len((uint32_t)in);
+}
+
+static void ctdb_event_status_state_push(enum ctdb_event_status_state in,
+ uint8_t *buf)
+{
+ ctdb_uint32_push((uint32_t)in, buf);
+}
+
+static int ctdb_event_status_state_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ enum ctdb_event_status_state *out)
+{
+ uint32_t uint32_value;
+ enum ctdb_event_status_state value;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &uint32_value);
+ if (ret != 0) {
+ return ret;
+ }
+
+ switch (uint32_value) {
+ case 1:
+ value = CTDB_EVENT_LAST_RUN;
+ break;
+
+ case 2:
+ value = CTDB_EVENT_LAST_PASS;
+ break;
+
+ case 3:
+ value = CTDB_EVENT_LAST_FAIL;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ *out = value;
+ return 0;
+}
+
+static size_t ctdb_event_request_run_len(struct ctdb_event_request_run *in)
+{
+ return ctdb_event_len(in->event) +
+ ctdb_uint32_len(in->timeout) +
+ ctdb_stringn_len(in->arg_str);
+}
+
+static void ctdb_event_request_run_push(struct ctdb_event_request_run *in,
+ uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_event_push(in->event, buf);
+ offset += ctdb_event_len(in->event);
+
+ ctdb_uint32_push(in->timeout, buf+offset);
+ offset += ctdb_uint32_len(in->timeout);
+
+ ctdb_stringn_push(in->arg_str, buf+offset);
+}
+
+static int ctdb_event_request_run_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_run **out)
+{
+ struct ctdb_event_request_run *rdata;
+ size_t offset = 0;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_request_run);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_pull(buf, buflen, rdata, &rdata->event);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += ctdb_event_len(rdata->event);
+
+ ret = ctdb_uint32_pull(buf+offset, buflen-offset,
+ rdata, &rdata->timeout);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += ctdb_uint32_len(rdata->timeout);
+
+ ret = ctdb_stringn_pull(buf+offset, buflen-offset,
+ rdata, &rdata->arg_str);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ *out = rdata;
+ return 0;
+
+fail:
+ talloc_free(rdata);
+ return ret;
+}
+
+static size_t ctdb_event_request_status_len(
+ struct ctdb_event_request_status *in)
+{
+ return ctdb_event_len(in->event) +
+ ctdb_event_status_state_len(in->state);
+}
+
+static void ctdb_event_request_status_push(
+ struct ctdb_event_request_status *in,
+ uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_event_push(in->event, buf);
+ offset += ctdb_event_len(in->event);
+
+ ctdb_event_status_state_push(in->state, buf+offset);
+}
+
+static int ctdb_event_request_status_pull(
+ uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_status **out)
+{
+ struct ctdb_event_request_status *rdata;
+ size_t offset = 0;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_request_status);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_pull(buf, buflen, rdata, &rdata->event);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+ offset += ctdb_event_len(rdata->event);
+
+ ret = ctdb_event_status_state_pull(buf+offset, buflen-offset,
+ rdata, &rdata->state);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+
+ *out = rdata;
+ return 0;
+}
+
+static size_t ctdb_event_request_script_enable_len(
+ struct ctdb_event_request_script_enable *in)
+{
+ return ctdb_stringn_len(in->script_name);
+}
+
+static void ctdb_event_request_script_enable_push(
+ struct ctdb_event_request_script_enable *in,
+ uint8_t *buf)
+{
+ ctdb_stringn_push(in->script_name, buf);
+}
+
+static int ctdb_event_request_script_enable_pull(
+ uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script_enable **out)
+{
+ struct ctdb_event_request_script_enable *rdata;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_request_script_enable);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_stringn_pull(buf, buflen, rdata, &rdata->script_name);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+
+ *out = rdata;
+ return 0;
+}
+
+static size_t ctdb_event_request_script_disable_len(
+ struct ctdb_event_request_script_disable *in)
+{
+ return ctdb_stringn_len(in->script_name);
+}
+
+static void ctdb_event_request_script_disable_push(
+ struct ctdb_event_request_script_disable *in,
+ uint8_t *buf)
+{
+ ctdb_stringn_push(in->script_name, buf);
+}
+
+static int ctdb_event_request_script_disable_pull(
+ uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script_disable **out)
+{
+ struct ctdb_event_request_script_disable *rdata;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_request_script_disable);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_stringn_pull(buf, buflen, rdata, &rdata->script_name);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+
+ *out = rdata;
+ return 0;
+}
+
+static size_t ctdb_event_request_data_len(struct ctdb_event_request_data *in)
+{
+ size_t len = 0;
+
+ len += ctdb_event_command_len(in->command);
+
+ switch(in->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ len += ctdb_event_request_run_len(in->data.run);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ len += ctdb_event_request_status_len(in->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ len += ctdb_event_request_script_enable_len(
+ in->data.script_enable);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ len += ctdb_event_request_script_disable_len(
+ in->data.script_disable);
+ break;
+ }
+
+ return len;
+}
+
+static void ctdb_event_request_data_push(struct ctdb_event_request_data *in,
+ uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_event_command_push(in->command, buf);
+ offset += ctdb_event_command_len(in->command);
+
+ switch (in->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ ctdb_event_request_run_push(in->data.run, buf+offset);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ ctdb_event_request_status_push(in->data.status, buf+offset);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ ctdb_event_request_script_enable_push(
+ in->data.script_enable,
+ buf+offset);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ ctdb_event_request_script_disable_push(
+ in->data.script_disable,
+ buf+offset);
+ break;
+ }
+}
+
+static int ctdb_event_request_data_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_data *out)
+{
+ size_t offset = 0;
+ int ret;
+
+ ret = ctdb_event_command_pull(buf, buflen, mem_ctx, &out->command);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_event_command_len(out->command);
+
+ switch (out->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ ret = ctdb_event_request_run_pull(buf+offset, buflen-offset,
+ mem_ctx, &out->data.run);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ ret = ctdb_event_request_status_pull(
+ buf+offset, buflen-offset,
+ mem_ctx, &out->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ ret = 0;
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ ret = ctdb_event_request_script_enable_pull(
+ buf+offset, buflen-offset,
+ mem_ctx,
+ &out->data.script_enable);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ ret = ctdb_event_request_script_disable_pull(
+ buf+offset, buflen-offset,
+ mem_ctx,
+ &out->data.script_disable);
+ break;
+ }
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static size_t ctdb_event_reply_status_len(struct ctdb_event_reply_status *in)
+{
+ return ctdb_int32_len(in->status) +
+ ctdb_script_list_len(in->script_list);
+}
+
+static void ctdb_event_reply_status_push(struct ctdb_event_reply_status *in,
+ uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_int32_push(in->status, buf);
+ offset += ctdb_int32_len(in->status);
+
+ ctdb_script_list_push(in->script_list, buf+offset);
+}
+
+static int ctdb_event_reply_status_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status **out)
+{
+ struct ctdb_event_reply_status *rdata;
+ size_t offset = 0;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_reply_status);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_int32_pull(buf, buflen, rdata, &rdata->status);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+ offset += ctdb_int32_len(rdata->status);
+
+ ret = ctdb_script_list_pull(buf+offset, buflen-offset,
+ rdata, &rdata->script_list);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+
+ *out = rdata;
+ return 0;
+}
+
+static size_t ctdb_event_reply_script_list_len(
+ struct ctdb_event_reply_script_list *in)
+{
+ return ctdb_script_list_len(in->script_list);
+}
+
+static void ctdb_event_reply_script_list_push(
+ struct ctdb_event_reply_script_list *in,
+ uint8_t *buf)
+{
+ ctdb_script_list_push(in->script_list, buf);
+}
+
+static int ctdb_event_reply_script_list_pull(
+ uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_script_list **out)
+{
+ struct ctdb_event_reply_script_list *rdata;
+ int ret;
+
+ rdata = talloc(mem_ctx, struct ctdb_event_reply_script_list);
+ if (rdata == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_script_list_pull(buf, buflen, rdata, &rdata->script_list);
+ if (ret != 0) {
+ talloc_free(rdata);
+ return ret;
+ }
+
+ *out = rdata;
+ return 0;
+}
+
+static size_t ctdb_event_reply_data_len(struct ctdb_event_reply_data *in)
+{
+ size_t len = 0;
+
+ len += ctdb_event_command_len(in->command);
+ len += ctdb_int32_len(in->result);
+
+ switch (in->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ len += ctdb_event_reply_status_len(in->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ len += ctdb_event_reply_script_list_len(in->data.script_list);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ break;
+ }
+
+ return len;
+}
+
+static void ctdb_event_reply_data_push(struct ctdb_event_reply_data *in,
+ uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_event_command_push(in->command, buf);
+ offset += ctdb_event_command_len(in->command);
+
+ ctdb_int32_push(in->result, buf+offset);
+ offset += ctdb_int32_len(in->result);
+
+ switch (in->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ ctdb_event_reply_status_push(in->data.status, buf+offset);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ ctdb_event_reply_script_list_push(in->data.script_list,
+ buf+offset);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ break;
+ }
+}
+
+static int ctdb_event_reply_data_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_data *out)
+{
+ size_t offset = 0;
+ int ret;
+
+ ret = ctdb_event_command_pull(buf, buflen, mem_ctx, &out->command);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_event_command_len(out->command);
+
+ ret = ctdb_int32_pull(buf+offset, buflen-offset,
+ mem_ctx, &out->result);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_int32_len(out->result);
+
+ switch (out->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ ret = ctdb_event_reply_status_pull(
+ buf+offset, buflen-offset,
+ mem_ctx, &out->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ ret = ctdb_event_reply_script_list_pull(
+ buf+offset, buflen-offset,
+ mem_ctx, &out->data.script_list);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ break;
+ }
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static size_t ctdb_event_header_len(struct ctdb_event_header *in)
+{
+ return ctdb_uint32_len(in->length) + ctdb_uint32_len(in->reqid);
+}
+
+static void ctdb_event_header_push(struct ctdb_event_header *in, uint8_t *buf)
+{
+ size_t offset = 0;
+
+ ctdb_uint32_push(in->length, buf);
+ offset += ctdb_uint32_len(in->length);
+
+ ctdb_uint32_push(in->reqid, buf+offset);
+}
+
+static int ctdb_event_header_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_header *out)
+{
+ size_t offset = 0;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, mem_ctx, &out->length);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_uint32_len(out->length);
+
+ ret = ctdb_uint32_pull(buf+offset, buflen-offset,
+ mem_ctx, &out->reqid);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+void ctdb_event_header_fill(struct ctdb_event_header *h, uint32_t reqid)
+{
+ h->length = ctdb_event_header_len(h);
+ h->reqid = reqid;
+}
+
+size_t ctdb_event_request_len(struct ctdb_event_request *in)
+{
+ return ctdb_event_header_len(&in->header) +
+ ctdb_event_request_data_len(&in->rdata);
+}
+
+int ctdb_event_request_push(struct ctdb_event_request *in,
+ uint8_t *buf, size_t *buflen)
+{
+ size_t len, offset = 0;
+
+ len = ctdb_event_request_len(in);
+ if (*buflen < len) {
+ *buflen = len;
+ return EMSGSIZE;
+ }
+
+ in->header.length = *buflen;
+
+ ctdb_event_header_push(&in->header, buf);
+ offset += ctdb_event_header_len(&in->header);
+
+ ctdb_event_request_data_push(&in->rdata, buf+offset);
+
+ return 0;
+}
+
+int ctdb_event_request_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *out)
+{
+ size_t offset = 0;
+ int ret;
+
+ ret = ctdb_event_header_pull(buf, buflen, mem_ctx, &out->header);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_event_header_len(&out->header);
+
+ ret = ctdb_event_request_data_pull(buf+offset, buflen-offset,
+ mem_ctx, &out->rdata);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+size_t ctdb_event_reply_len(struct ctdb_event_reply *in)
+{
+ return ctdb_event_header_len(&in->header) +
+ ctdb_event_reply_data_len(&in->rdata);
+}
+
+int ctdb_event_reply_push(struct ctdb_event_reply *in,
+ uint8_t *buf, size_t *buflen)
+{
+ size_t len, offset = 0;
+
+ len = ctdb_event_reply_len(in);
+ if (*buflen < len) {
+ *buflen = len;
+ return EMSGSIZE;
+ }
+
+ in->header.length = *buflen;
+
+ ctdb_event_header_push(&in->header, buf);
+ offset += ctdb_event_header_len(&in->header);
+
+ ctdb_event_reply_data_push(&in->rdata, buf+offset);
+
+ return 0;
+}
+
+int ctdb_event_reply_pull(uint8_t *buf, size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply *out)
+{
+ size_t offset = 0;
+ int ret;
+
+ ret = ctdb_event_header_pull(buf, buflen, mem_ctx, &out->header);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += ctdb_event_header_len(&out->header);
+
+ ret = ctdb_event_reply_data_pull(buf+offset, buflen-offset,
+ mem_ctx, &out->rdata);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol.h samba-4.6.5+dfsg/ctdb/protocol/protocol.h
--- samba-4.5.8+dfsg/ctdb/protocol/protocol.h 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol.h 2017-01-11 07:55:14.000000000 +0000
@@ -295,7 +295,7 @@
CTDB_CONTROL_DISABLE_MONITOR = 76,
CTDB_CONTROL_ADD_PUBLIC_IP = 77,
CTDB_CONTROL_DEL_PUBLIC_IP = 78,
- CTDB_CONTROL_RUN_EVENTSCRIPTS = 79,
+ CTDB_CONTROL_RUN_EVENTSCRIPTS = 79, /* obsolete */
CTDB_CONTROL_GET_CAPABILITIES = 80,
CTDB_CONTROL_START_PERSISTENT_UPDATE = 81, /* obsolete */
CTDB_CONTROL_CANCEL_PERSISTENT_UPDATE= 82, /* obsolete */
@@ -309,7 +309,7 @@
CTDB_CONTROL_GET_PUBLIC_IPS = 90,
CTDB_CONTROL_GET_NODEMAP = 91,
/* missing */
- CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS = 96,
+ CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS = 96, /* obsolete */
CTDB_CONTROL_TRAVERSE_KILL = 97,
CTDB_CONTROL_RECD_RECLOCK_LATENCY = 98,
CTDB_CONTROL_GET_RECLOCK_FILE = 99,
@@ -319,8 +319,8 @@
CTDB_CONTROL_SET_NATGWSTATE = 103, /* obsolete */
CTDB_CONTROL_SET_LMASTERROLE = 104,
CTDB_CONTROL_SET_RECMASTERROLE = 105,
- CTDB_CONTROL_ENABLE_SCRIPT = 107,
- CTDB_CONTROL_DISABLE_SCRIPT = 108,
+ CTDB_CONTROL_ENABLE_SCRIPT = 107, /* obsolete */
+ CTDB_CONTROL_DISABLE_SCRIPT = 108, /* obsolete */
CTDB_CONTROL_SET_BAN_STATE = 109,
CTDB_CONTROL_GET_BAN_STATE = 110,
CTDB_CONTROL_SET_DB_PRIORITY = 111, /* obsolete */
@@ -631,6 +631,7 @@
uint32_t lock_processes_per_db;
uint32_t rec_buffer_size_limit;
uint32_t queue_buffer_size;
+ uint32_t ip_alloc_algorithm;
};
struct ctdb_tickle_list {
@@ -856,12 +857,10 @@
struct ctdb_client_id *cid;
struct ctdb_addr_info *addr_info;
struct ctdb_transdb *transdb;
- const char *event_str;
struct ctdb_public_ip *pubip;
enum ctdb_event event;
double reclock_latency;
uint32_t role;
- const char *script;
struct ctdb_ban_state *ban_state;
struct ctdb_notify_data *notify;
uint64_t srvid;
@@ -894,7 +893,6 @@
uint32_t caps;
struct ctdb_public_ip_list *pubip_list;
struct ctdb_node_map *nodemap;
- struct ctdb_script_list *script_list;
const char *reclock_file;
struct ctdb_ban_state *ban_state;
uint64_t seqnum;
@@ -916,6 +914,8 @@
uint32_t client_id;
#define CTDB_CTRL_FLAG_NOREPLY 1
#define CTDB_CTRL_FLAG_OPCODE_SPECIFIC 0xFFFF0000
+/* Ugly overloading of this field... */
+#define CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE 0x00010000
uint32_t flags;
struct ctdb_req_control_data rdata;
};
@@ -1002,4 +1002,84 @@
struct ctdb_g_lock *lock;
};
+/*
+ * Eventd protocol
+ */
+
+enum ctdb_event_command {
+ CTDB_EVENT_COMMAND_RUN = 1,
+ CTDB_EVENT_COMMAND_STATUS = 2,
+ CTDB_EVENT_COMMAND_SCRIPT_LIST = 3,
+ CTDB_EVENT_COMMAND_SCRIPT_ENABLE = 4,
+ CTDB_EVENT_COMMAND_SCRIPT_DISABLE = 5,
+};
+
+enum ctdb_event_status_state {
+ CTDB_EVENT_LAST_RUN = 1,
+ CTDB_EVENT_LAST_PASS = 2,
+ CTDB_EVENT_LAST_FAIL = 3,
+};
+
+struct ctdb_event_request_run {
+ enum ctdb_event event;
+ uint32_t timeout;
+ const char *arg_str;
+};
+
+struct ctdb_event_request_status {
+ enum ctdb_event event;
+ enum ctdb_event_status_state state;
+};
+
+struct ctdb_event_request_script_enable {
+ const char *script_name;
+};
+
+struct ctdb_event_request_script_disable {
+ const char *script_name;
+};
+
+struct ctdb_event_request_data {
+ enum ctdb_event_command command;
+ union {
+ struct ctdb_event_request_run *run;
+ struct ctdb_event_request_status *status;
+ struct ctdb_event_request_script_enable *script_enable;
+ struct ctdb_event_request_script_disable *script_disable;
+ } data;
+};
+
+struct ctdb_event_reply_status {
+ int status;
+ struct ctdb_script_list *script_list;
+};
+
+struct ctdb_event_reply_script_list {
+ struct ctdb_script_list *script_list;
+};
+
+struct ctdb_event_reply_data {
+ enum ctdb_event_command command;
+ int32_t result;
+ union {
+ struct ctdb_event_reply_status *status;
+ struct ctdb_event_reply_script_list *script_list;
+ } data;
+};
+
+struct ctdb_event_header {
+ uint32_t length;
+ uint32_t reqid;
+};
+
+struct ctdb_event_request {
+ struct ctdb_event_header header;
+ struct ctdb_event_request_data rdata;
+};
+
+struct ctdb_event_reply {
+ struct ctdb_event_header header;
+ struct ctdb_event_reply_data rdata;
+};
+
#endif /* __CTDB_PROTOCOL_H__ */
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_private.h samba-4.6.5+dfsg/ctdb/protocol/protocol_private.h
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_private.h 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_private.h 2017-01-11 07:55:14.000000000 +0000
@@ -22,6 +22,11 @@
#include "protocol.h"
+size_t ctdb_int32_len(int32_t val);
+void ctdb_int32_push(int32_t val, uint8_t *buf);
+int ctdb_int32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
+ int32_t *out);
+
size_t ctdb_uint32_len(uint32_t val);
void ctdb_uint32_push(uint32_t val, uint8_t *buf);
int ctdb_uint32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
diff -Nru samba-4.5.8+dfsg/ctdb/protocol/protocol_types.c samba-4.6.5+dfsg/ctdb/protocol/protocol_types.c
--- samba-4.5.8+dfsg/ctdb/protocol/protocol_types.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/protocol/protocol_types.c 2017-01-11 07:55:14.000000000 +0000
@@ -27,6 +27,27 @@
#include "protocol_private.h"
#include "protocol_api.h"
+size_t ctdb_int32_len(int32_t val)
+{
+ return sizeof(int32_t);
+}
+
+void ctdb_int32_push(int32_t val, uint8_t *buf)
+{
+ memcpy(buf, &val, sizeof(int32_t));
+}
+
+int ctdb_int32_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
+ int32_t *out)
+{
+ if (buflen < sizeof(int32_t)) {
+ return EMSGSIZE;
+ }
+
+ *out = *(int32_t *)buf;
+ return 0;
+}
+
size_t ctdb_uint32_len(uint32_t val)
{
return sizeof(uint32_t);
@@ -231,19 +252,15 @@
size_t ctdb_stringn_len(const char *str)
{
- return sizeof(uint32_t) + strlen(str) + 1;
+ return sizeof(uint32_t) + ctdb_string_len(str);
}
void ctdb_stringn_push(const char *str, uint8_t *buf)
{
struct stringn_wire *wire = (struct stringn_wire *)buf;
- if (str == NULL) {
- wire->length = 0;
- } else {
- wire->length = strlen(str) + 1;
- memcpy(wire->str, str, wire->length);
- }
+ wire->length = ctdb_string_len(str);
+ ctdb_string_push(str, wire->str);
}
int ctdb_stringn_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx,
@@ -265,6 +282,11 @@
return EMSGSIZE;
}
+ if (wire->length == 0) {
+ *out = NULL;
+ return 0;
+ }
+
str = talloc_strndup(mem_ctx, (char *)wire->str, wire->length);
if (str == NULL) {
return ENOMEM;
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_call.c samba-4.6.5+dfsg/ctdb/server/ctdb_call.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_call.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_call.c 2017-06-06 07:47:19.000000000 +0000
@@ -30,6 +30,7 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
@@ -1561,6 +1562,7 @@
struct revokechild_deferred_call {
+ struct revokechild_deferred_call *prev, *next;
struct ctdb_context *ctdb;
struct ctdb_req_header *hdr;
deferred_requeue_fn fn;
@@ -1576,48 +1578,31 @@
int fd[2];
pid_t child;
TDB_DATA key;
-};
-
-struct revokechild_requeue_handle {
- struct ctdb_context *ctdb;
- struct ctdb_req_header *hdr;
- deferred_requeue_fn fn;
- void *ctx;
+ struct revokechild_deferred_call *deferred_call_list;
};
static void deferred_call_requeue(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval t, void *private_data)
{
- struct revokechild_requeue_handle *requeue_handle = talloc_get_type(private_data, struct revokechild_requeue_handle);
+ struct revokechild_deferred_call *dlist = talloc_get_type_abort(
+ private_data, struct revokechild_deferred_call);
- requeue_handle->fn(requeue_handle->ctx, requeue_handle->hdr);
- talloc_free(requeue_handle);
-}
+ while (dlist != NULL) {
+ struct revokechild_deferred_call *dcall = dlist;
-static int deferred_call_destructor(struct revokechild_deferred_call *deferred_call)
-{
- struct ctdb_context *ctdb = deferred_call->ctdb;
- struct revokechild_requeue_handle *requeue_handle = talloc(ctdb, struct revokechild_requeue_handle);
- struct ctdb_req_call_old *c = (struct ctdb_req_call_old *)deferred_call->hdr;
-
- requeue_handle->ctdb = ctdb;
- requeue_handle->hdr = deferred_call->hdr;
- requeue_handle->fn = deferred_call->fn;
- requeue_handle->ctx = deferred_call->ctx;
- talloc_steal(requeue_handle, requeue_handle->hdr);
-
- /* when revoking, any READONLY requests have 1 second grace to let read/write finish first */
- tevent_add_timer(ctdb->ev, requeue_handle,
- timeval_current_ofs(c->flags & CTDB_WANT_READONLY ? 1 : 0, 0),
- deferred_call_requeue, requeue_handle);
-
- return 0;
+ DLIST_REMOVE(dlist, dcall);
+ dcall->fn(dcall->ctx, dcall->hdr);
+ talloc_free(dcall);
+ }
}
static int revokechild_destructor(struct revokechild_handle *rc)
{
+ struct revokechild_deferred_call *now_list = NULL;
+ struct revokechild_deferred_call *delay_list = NULL;
+
if (rc->fde != NULL) {
talloc_free(rc->fde);
}
@@ -1631,6 +1616,48 @@
ctdb_kill(rc->ctdb, rc->child, SIGKILL);
DLIST_REMOVE(rc->ctdb_db->revokechild_active, rc);
+
+ while (rc->deferred_call_list != NULL) {
+ struct revokechild_deferred_call *dcall;
+
+ dcall = rc->deferred_call_list;
+ DLIST_REMOVE(rc->deferred_call_list, dcall);
+
+ /* If revoke is successful, then first process all the calls
+ * that need write access, and delay readonly requests by 1
+ * second grace.
+ *
+ * If revoke is unsuccessful, most likely because of node
+ * failure, delay all the pending requests, so database can
+ * be recovered.
+ */
+
+ if (rc->status == 0) {
+ struct ctdb_req_call_old *c;
+
+ c = (struct ctdb_req_call_old *)dcall->hdr;
+ if (c->flags & CTDB_WANT_READONLY) {
+ DLIST_ADD(delay_list, dcall);
+ } else {
+ DLIST_ADD(now_list, dcall);
+ }
+ } else {
+ DLIST_ADD(delay_list, dcall);
+ }
+ }
+
+ if (now_list != NULL) {
+ tevent_add_timer(rc->ctdb->ev, rc->ctdb_db,
+ tevent_timeval_current_ofs(0, 0),
+ deferred_call_requeue, now_list);
+ }
+
+ if (delay_list != NULL) {
+ tevent_add_timer(rc->ctdb->ev, rc->ctdb_db,
+ tevent_timeval_current_ofs(1, 0),
+ deferred_call_requeue, delay_list);
+ }
+
return 0;
}
@@ -1852,10 +1879,9 @@
if (rc->child == 0) {
char c = 0;
close(rc->fd[0]);
- debug_extra = talloc_asprintf(NULL, "revokechild-%s:", ctdb_db->db_name);
prctl_set_comment("ctdb_revokechild");
- if (switch_from_server_to_client(ctdb, "revokechild-%s", ctdb_db->db_name) != 0) {
+ if (switch_from_server_to_client(ctdb) != 0) {
DEBUG(DEBUG_ERR,("Failed to switch from server to client for revokechild process\n"));
c = 1;
goto child_finished;
@@ -1909,19 +1935,18 @@
return -1;
}
- deferred_call = talloc(rc, struct revokechild_deferred_call);
+ deferred_call = talloc(ctdb_db, struct revokechild_deferred_call);
if (deferred_call == NULL) {
DEBUG(DEBUG_ERR,("Failed to allocate deferred call structure for revoking record\n"));
return -1;
}
deferred_call->ctdb = ctdb;
- deferred_call->hdr = hdr;
+ deferred_call->hdr = talloc_steal(deferred_call, hdr);
deferred_call->fn = fn;
deferred_call->ctx = call_context;
- talloc_set_destructor(deferred_call, deferred_call_destructor);
- talloc_steal(deferred_call, hdr);
+ DLIST_ADD(rc->deferred_call_list, deferred_call);
return 0;
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_cluster_mutex.c samba-4.6.5+dfsg/ctdb/server/ctdb_cluster_mutex.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_cluster_mutex.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_cluster_mutex.c 2017-01-11 07:55:14.000000000 +0000
@@ -28,12 +28,12 @@
#include "lib/util/time.h"
#include "lib/util/strv.h"
#include "lib/util/strv_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/blocking.h"
#include "ctdb_private.h"
#include "common/common.h"
#include "common/logging.h"
-#include "common/system.h"
#include "ctdb_cluster_mutex.h"
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_control.c samba-4.6.5+dfsg/ctdb/server/ctdb_control.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_control.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_control.c 2017-01-11 07:55:14.000000000 +0000
@@ -332,9 +332,9 @@
CHECK_CONTROL_DATA_SIZE(0);
ctdb_enable_monitoring(ctdb);
return 0;
-
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- return ctdb_run_eventscripts(ctdb, c, indata, async_reply);
+
+ case CTDB_CONTROL_RUN_EVENTSCRIPTS:
+ return control_not_implemented("RUN_EVENTSCRIPTS", NULL);
case CTDB_CONTROL_DISABLE_MONITOR:
CHECK_CONTROL_DATA_SIZE(0);
@@ -496,8 +496,7 @@
return ctdb_control_recd_ping(ctdb);
case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t));
- return ctdb_control_get_event_script_status(ctdb, *(uint32_t *)indata.dptr, outdata);
+ return control_not_implemented("GET_EVENT_SCRIPT_STATUS", NULL);
case CTDB_CONTROL_RECD_RECLOCK_LATENCY:
CHECK_CONTROL_DATA_SIZE(sizeof(double));
@@ -551,10 +550,10 @@
}
case CTDB_CONTROL_ENABLE_SCRIPT:
- return ctdb_control_enable_script(ctdb, indata);
+ return control_not_implemented("ENABLE_SCRIPT", NULL);
case CTDB_CONTROL_DISABLE_SCRIPT:
- return ctdb_control_disable_script(ctdb, indata);
+ return control_not_implemented("DISABLE_SCRIPT", NULL);
case CTDB_CONTROL_SET_BAN_STATE:
CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_ban_state));
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_daemon.c samba-4.6.5+dfsg/ctdb/server/ctdb_daemon.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_daemon.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_daemon.c 2017-01-11 07:55:14.000000000 +0000
@@ -58,11 +58,11 @@
static void daemon_incoming_packet(void *, struct ctdb_req_header *);
+static pid_t __ctdbd_pid;
+
static void print_exit_message(void)
{
- if (debug_extra != NULL && debug_extra[0] != '\0') {
- DEBUG(DEBUG_NOTICE,("CTDB %s shutting down\n", debug_extra));
- } else {
+ if (getpid() == __ctdbd_pid) {
DEBUG(DEBUG_NOTICE,("CTDB daemon shutting down\n"));
/* Wait a second to allow pending log messages to be flushed */
@@ -1104,6 +1104,16 @@
static struct timeval tevent_before_wait_ts;
static struct timeval tevent_after_wait_ts;
+static void ctdb_tevent_trace_init(void)
+{
+ struct timeval now;
+
+ now = timeval_current();
+
+ tevent_before_wait_ts = now;
+ tevent_after_wait_ts = now;
+}
+
static void ctdb_tevent_trace(enum tevent_trace_point tp,
void *private_data)
{
@@ -1120,25 +1130,21 @@
switch (tp) {
case TEVENT_TRACE_BEFORE_WAIT:
- if (!timeval_is_zero(&tevent_after_wait_ts)) {
- diff = timeval_until(&tevent_after_wait_ts, &now);
- if (diff.tv_sec > 3) {
- DEBUG(DEBUG_ERR,
- ("Handling event took %ld seconds!\n",
- (long)diff.tv_sec));
- }
+ diff = timeval_until(&tevent_after_wait_ts, &now);
+ if (diff.tv_sec > 3) {
+ DEBUG(DEBUG_ERR,
+ ("Handling event took %ld seconds!\n",
+ (long)diff.tv_sec));
}
tevent_before_wait_ts = now;
break;
case TEVENT_TRACE_AFTER_WAIT:
- if (!timeval_is_zero(&tevent_before_wait_ts)) {
- diff = timeval_until(&tevent_before_wait_ts, &now);
- if (diff.tv_sec > 3) {
- DEBUG(DEBUG_CRIT,
- ("No event for %ld seconds!\n",
- (long)diff.tv_sec));
- }
+ diff = timeval_until(&tevent_before_wait_ts, &now);
+ if (diff.tv_sec > 3) {
+ DEBUG(DEBUG_ERR,
+ ("No event for %ld seconds!\n",
+ (long)diff.tv_sec));
}
tevent_after_wait_ts = now;
break;
@@ -1259,6 +1265,7 @@
* This must be the first exit handler to run (so the last to
* be registered.
*/
+ __ctdbd_pid = getpid();
atexit(print_exit_message);
if (ctdb->do_setsched) {
@@ -1275,12 +1282,8 @@
exit(1);
}
tevent_loop_allow_nesting(ctdb->ev);
+ ctdb_tevent_trace_init();
tevent_set_trace_callback(ctdb->ev, ctdb_tevent_trace, ctdb);
- ret = ctdb_init_tevent_logging(ctdb);
- if (ret != 0) {
- DEBUG(DEBUG_ALERT,("Failed to initialize TEVENT logging\n"));
- exit(1);
- }
/* set up a handler to pick up sigchld */
if (ctdb_init_sigchld(ctdb) == NULL) {
@@ -1288,7 +1291,9 @@
exit(1);
}
- ctdb_set_child_logging(ctdb);
+ if (do_fork) {
+ ctdb_set_child_logging(ctdb);
+ }
TALLOC_FREE(ctdb->srv);
if (srvid_init(ctdb, &ctdb->srv) != 0) {
@@ -1302,6 +1307,11 @@
/* force initial recovery for election */
ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE;
+ if (ctdb_start_eventd(ctdb) != 0) {
+ DEBUG(DEBUG_ERR, ("Failed to start event daemon\n"));
+ exit(1);
+ }
+
ctdb_set_runstate(ctdb, CTDB_RUNSTATE_INIT);
ret = ctdb_event_script(ctdb, CTDB_EVENT_INIT);
if (ret != 0) {
@@ -1834,6 +1844,7 @@
ctdb_stop_monitoring(ctdb);
ctdb_release_all_ips(ctdb);
ctdb_event_script(ctdb, CTDB_EVENT_SHUTDOWN);
+ ctdb_stop_eventd(ctdb);
if (ctdb->methods != NULL && ctdb->methods->shutdown != NULL) {
ctdb->methods->shutdown(ctdb);
}
@@ -1848,15 +1859,9 @@
* process must be created using ctdb_fork() and not fork() -
* ctdb_fork() does some necessary housekeeping.
*/
-int switch_from_server_to_client(struct ctdb_context *ctdb, const char *fmt, ...)
+int switch_from_server_to_client(struct ctdb_context *ctdb)
{
int ret;
- va_list ap;
-
- /* Add extra information so we can identify this in the logs */
- va_start(ap, fmt);
- debug_extra = talloc_strdup_append(talloc_vasprintf(NULL, fmt, ap), ":");
- va_end(ap);
/* get a new event context */
ctdb->ev = tevent_context_init(ctdb);
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdbd.c samba-4.6.5+dfsg/ctdb/server/ctdbd.c
--- samba-4.5.8+dfsg/ctdb/server/ctdbd.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdbd.c 2017-01-11 07:55:14.000000000 +0000
@@ -36,11 +36,12 @@
#include "common/reqid.h"
#include "common/system.h"
-#include "common/cmdline.h"
#include "common/common.h"
#include "common/logging.h"
static struct {
+ const char *socket;
+ const char *debuglevel;
const char *nlist;
const char *transport;
const char *myaddress;
@@ -62,7 +63,10 @@
int script_log_level;
int no_publicipcheck;
int max_persistent_check_errors;
+ int torture;
} options = {
+ .socket = CTDB_RUNDIR "/ctdbd.socket",
+ .debuglevel = "NOTICE",
.nlist = NULL,
.public_address_list = NULL,
.transport = "tcp",
@@ -116,7 +120,8 @@
struct poptOption popt_options[] = {
POPT_AUTOHELP
- POPT_CTDB_CMDLINE
+ { "socket", 0, POPT_ARG_STRING, &options.socket, 0, "local socket name", "filename" },
+ { "debug", 'd', POPT_ARG_STRING, &options.debuglevel, 0, "debug level", NULL },
{ "interactive", 'i', POPT_ARG_NONE, &interactive, 0, "don't fork", NULL },
{ "public-addresses", 0, POPT_ARG_STRING, &options.public_address_list, 0, "public address list file", "filename" },
{ "public-interface", 0, POPT_ARG_STRING, &options.public_interface, 0, "public interface", "interface"},
@@ -143,11 +148,11 @@
&options.max_persistent_check_errors, 0,
"max allowed persistent check errors (default 0)", NULL },
{ "sloppy-start", 0, POPT_ARG_NONE, &fast_start, 0, "Do not perform full recovery on start", NULL },
+ { "torture", 0, POPT_ARG_NONE, &options.torture, 0, "enable nastiness in library", NULL },
POPT_TABLEEND
};
int opt, ret;
const char **extra_argv;
- int extra_argc = 0;
poptContext pc;
struct tevent_context *ev;
@@ -162,11 +167,14 @@
}
}
- /* setup the remaining options for the main program to use */
+ /* If there are extra arguments then exit with usage message */
extra_argv = poptGetArgs(pc);
if (extra_argv) {
extra_argv++;
- while (extra_argv[extra_argc]) extra_argc++;
+ if (extra_argv[0]) {
+ poptPrintHelp(pc, stdout, 0);
+ exit(1);
+ }
}
talloc_enable_null_tracking();
@@ -175,21 +183,44 @@
ev = tevent_context_init(NULL);
if (ev == NULL) {
- DEBUG(DEBUG_ALERT,("tevent_context_init() failed\n"));
+ fprintf(stderr, "tevent_context_init() failed\n");
exit(1);
}
tevent_loop_allow_nesting(ev);
- ctdb = ctdb_cmdline_init(ev);
+ ctdb = ctdb_init(ev);
+ if (ctdb == NULL) {
+ fprintf(stderr, "Failed to init ctdb\n");
+ exit(1);
+ }
- ctdb->start_as_disabled = options.start_as_disabled;
- ctdb->start_as_stopped = options.start_as_stopped;
+ if (options.torture == 1) {
+ ctdb_set_flags(ctdb, CTDB_FLAG_TORTURE);
+ }
- script_log_level = options.script_log_level;
+ /* Log to stderr when running as interactive */
+ if (interactive) {
+ options.logging = "file:";
+ }
- if (!ctdb_logging_init(ctdb, options.logging)) {
+ /* Initialize logging and set the debug level */
+ if (!ctdb_logging_init(ctdb, options.logging, options.debuglevel)) {
exit(1);
}
+ setenv("CTDB_LOGGING", options.logging, 1);
+ setenv("CTDB_DEBUGLEVEL", debug_level_to_string(DEBUGLEVEL), 1);
+
+ setenv("CTDB_SOCKET", options.socket, 1);
+ ret = ctdb_set_socketname(ctdb, options.socket);
+ if (ret == -1) {
+ DEBUG(DEBUG_ERR, ("ctdb_set_socketname() failed\n"));
+ exit(1);
+ }
+
+ ctdb->start_as_disabled = options.start_as_disabled;
+ ctdb->start_as_stopped = options.start_as_stopped;
+
+ script_log_level = options.script_log_level;
DEBUG(DEBUG_NOTICE,("CTDB starting on node\n"));
@@ -208,7 +239,7 @@
TALLOC_FREE(ctdb->idr);
ret = reqid_init(ctdb, 0, &ctdb->idr);;
if (ret != 0) {
- DEBUG(DEBUG_ALERT, ("reqid_init failed (%s)\n", strerror(ret)));
+ DEBUG(DEBUG_ERR, ("reqid_init failed (%s)\n", strerror(ret)));
exit(1);
}
@@ -216,7 +247,8 @@
ret = ctdb_set_transport(ctdb, options.transport);
if (ret == -1) {
- DEBUG(DEBUG_ALERT,("ctdb_set_transport failed - %s\n", ctdb_errstr(ctdb)));
+ DEBUG(DEBUG_ERR,("ctdb_set_transport failed - %s\n",
+ ctdb_errstr(ctdb)));
exit(1);
}
@@ -224,7 +256,8 @@
if (options.myaddress) {
ret = ctdb_set_address(ctdb, options.myaddress);
if (ret == -1) {
- DEBUG(DEBUG_ALERT,("ctdb_set_address failed - %s\n", ctdb_errstr(ctdb)));
+ DEBUG(DEBUG_ERR,("ctdb_set_address failed - %s\n",
+ ctdb_errstr(ctdb)));
exit(1);
}
}
@@ -258,7 +291,7 @@
ctdb->nodes_file =
talloc_asprintf(ctdb, "%s/nodes", getenv("CTDB_BASE"));
if (ctdb->nodes_file == NULL) {
- DEBUG(DEBUG_ALERT,(__location__ " Out of memory\n"));
+ DEBUG(DEBUG_ERR,(__location__ " Out of memory\n"));
exit(1);
}
}
@@ -284,7 +317,7 @@
ctdb->event_script_dir = talloc_asprintf(ctdb, "%s/events.d",
getenv("CTDB_BASE"));
if (ctdb->event_script_dir == NULL) {
- DEBUG(DEBUG_ALERT,(__location__ " Out of memory\n"));
+ DEBUG(DEBUG_ERR,(__location__ " Out of memory\n"));
exit(1);
}
}
@@ -292,7 +325,7 @@
if (options.notification_script != NULL) {
ret = ctdb_set_notification_script(ctdb, options.notification_script);
if (ret == -1) {
- DEBUG(DEBUG_ALERT,("Unable to setup notification script\n"));
+ DEBUG(DEBUG_ERR,("Unable to setup notification script\n"));
exit(1);
}
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_eventd.c samba-4.6.5+dfsg/ctdb/server/ctdb_eventd.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_eventd.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_eventd.c 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,1802 @@
+/*
+ Event script running daemon
+
+ Copyright (C) Amitay Isaacs 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/dir.h"
+#include "system/wait.h"
+#include "system/locale.h"
+
+#include
+#include
+#include
+
+#include "lib/util/debug.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/blocking.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/dlinklist.h"
+#include "lib/async_req/async_sock.h"
+
+#include "protocol/protocol_api.h"
+
+#include "common/comm.h"
+#include "common/logging.h"
+#include "common/run_proc.h"
+#include "common/sock_daemon.h"
+
+struct pending_event {
+ struct pending_event *prev, *next;
+
+ struct tevent_req *req;
+};
+
+struct eventd_client {
+ struct eventd_client *prev, *next;
+
+ struct sock_client_context *client_ctx;
+ struct pending_event *pending_list;
+};
+
+struct eventd_context {
+ const char *script_dir;
+ const char *debug_script;
+ struct run_proc_context *run_ctx;
+ struct tevent_queue *queue;
+
+ /* current state */
+ bool running;
+ enum ctdb_event event;
+ struct tevent_req *req;
+
+ /* result of last execution */
+ int result_run;
+ int result_fail;
+ struct ctdb_script_list *status_run[CTDB_EVENT_MAX];
+ struct ctdb_script_list *status_pass[CTDB_EVENT_MAX];
+ struct ctdb_script_list *status_fail[CTDB_EVENT_MAX];
+
+ struct eventd_client *client_list;
+};
+
+/*
+ * Global state manipulation functions
+ */
+
+static int eventd_context_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *script_dir,
+ const char *debug_script,
+ struct eventd_context **result)
+{
+ struct eventd_context *ectx;
+ int ret;
+
+ ectx = talloc_zero(mem_ctx, struct eventd_context);
+ if (ectx == NULL) {
+ return ENOMEM;
+ }
+
+ ectx->script_dir = talloc_strdup(ectx, script_dir);
+ if (ectx->script_dir == NULL) {
+ talloc_free(ectx);
+ return ENOMEM;
+ }
+
+ if (debug_script != NULL) {
+ ectx->debug_script = talloc_strdup(ectx, debug_script);
+ if (ectx->debug_script == NULL) {
+ talloc_free(ectx);
+ return ENOMEM;
+ }
+ }
+
+ ret = run_proc_init(ectx, ev, &ectx->run_ctx);
+ if (ret != 0) {
+ talloc_free(ectx);
+ return ret;
+ }
+
+ ectx->queue = tevent_queue_create(ectx, "run event queue");
+ if (ectx->queue == NULL) {
+ talloc_free(ectx);
+ return ENOMEM;
+ }
+
+ ectx->running = false;
+ ectx->event = CTDB_EVENT_INIT;
+
+ *result = ectx;
+ return 0;
+}
+
+static const char *eventd_script_dir(struct eventd_context *ectx)
+{
+ return ectx->script_dir;
+}
+
+static const char *eventd_debug_script(struct eventd_context *ectx)
+{
+ return ectx->debug_script;
+}
+
+static struct tevent_queue *eventd_queue(struct eventd_context *ectx)
+{
+ return ectx->queue;
+}
+
+static void eventd_start_running(struct eventd_context *ectx,
+ enum ctdb_event event,
+ struct tevent_req *req)
+{
+ ectx->running = true;
+ ectx->event = event;
+ ectx->req = req;
+}
+
+static void eventd_stop_running(struct eventd_context *ectx)
+{
+ ectx->running = false;
+ ectx->req = NULL;
+}
+
+static void eventd_cancel_running(struct eventd_context *ectx)
+{
+ if (ectx->req != NULL) {
+ tevent_req_error(ectx->req, ECANCELED);
+ }
+
+ eventd_stop_running(ectx);
+}
+
+static bool eventd_is_running(struct eventd_context *ectx,
+ enum ctdb_event *event)
+{
+ if (event != NULL && ectx->running) {
+ *event = ectx->event;
+ }
+
+ return ectx->running;
+}
+
+static struct ctdb_script_list *script_list_copy(TALLOC_CTX *mem_ctx,
+ struct ctdb_script_list *s)
+{
+ struct ctdb_script_list *s2;
+
+ s2 = talloc_zero(mem_ctx, struct ctdb_script_list);
+ if (s2 == NULL) {
+ return NULL;
+ }
+
+ s2->num_scripts = s->num_scripts;
+ s2->script = talloc_memdup(s2, s->script,
+ s->num_scripts * sizeof(struct ctdb_script));
+ if (s2->script == NULL) {
+ talloc_free(s2);
+ return NULL;
+ }
+
+ return s2;
+}
+
+static void eventd_set_result(struct eventd_context *ectx,
+ enum ctdb_event event,
+ struct ctdb_script_list *script_list,
+ int result)
+{
+ struct ctdb_script_list *s;
+
+ /* Avoid negative values, they represent -errno */
+ result = (result < 0) ? -result : result;
+
+ ectx->result_run = result;
+ if (script_list == NULL) {
+ return;
+ }
+
+ TALLOC_FREE(ectx->status_run[event]);
+ ectx->status_run[event] = talloc_steal(ectx, script_list);
+
+ s = script_list_copy(ectx, script_list);
+ if (s == NULL) {
+ return;
+ }
+
+ if (result == 0) {
+ TALLOC_FREE(ectx->status_pass[event]);
+ ectx->status_pass[event] = s;
+ } else {
+ TALLOC_FREE(ectx->status_fail[event]);
+ ectx->status_fail[event] = s;
+ ectx->result_fail = result;
+ }
+}
+
+static int eventd_get_result(struct eventd_context *ectx,
+ enum ctdb_event event,
+ enum ctdb_event_status_state state,
+ struct ctdb_script_list **out)
+{
+ struct ctdb_script_list *s = NULL;
+ int result = 0;
+
+ switch (state) {
+ case CTDB_EVENT_LAST_RUN:
+ s = ectx->status_run[event];
+ result = ectx->result_run;
+ break;
+
+ case CTDB_EVENT_LAST_PASS:
+ s = ectx->status_pass[event];
+ result = 0;
+ break;
+
+ case CTDB_EVENT_LAST_FAIL:
+ s = ectx->status_fail[event];
+ result = ectx->result_fail;
+ break;
+ }
+
+ *out = s;
+ return result;
+}
+
+/*
+ * Run debug script to dianose hung scripts
+ */
+
+static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
+ enum ctdb_event event, pid_t pid, const char ***out)
+{
+ const char **argv;
+
+ argv = talloc_array(mem_ctx, const char *, 4);
+ if (argv == NULL) {
+ return ENOMEM;
+ }
+
+ argv[0] = path;
+ argv[1] = talloc_asprintf(argv, "%d", pid);
+ argv[2] = ctdb_event_to_string(event);
+ if (argv[1] == NULL) {
+ talloc_free(argv);
+ return ENOMEM;
+ }
+ argv[3] = NULL;
+
+ *out = argv;
+ return 0;
+}
+
+static void debug_log(int loglevel, char *output, const char *log_prefix)
+{
+ char *line;
+
+ line = strtok(output, "\n");
+ while (line != NULL) {
+ DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
+ line = strtok(NULL, "\n");
+ }
+}
+
+struct run_debug_state {
+ pid_t pid;
+};
+
+static void run_debug_done(struct tevent_req *subreq);
+
+static struct tevent_req *run_debug_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ enum ctdb_event event, pid_t pid)
+{
+ struct tevent_req *req, *subreq;
+ struct run_debug_state *state;
+ const char **argv;
+ const char *debug_script;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->pid = pid;
+
+ debug_script = eventd_debug_script(ectx);
+ if (debug_script == NULL) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (pid == -1) {
+ D_DEBUG("Script terminated, nothing to debug\n");
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = debug_args(state, debug_script, event, pid, &argv);
+ if (ret != 0) {
+ D_ERR("debug_args() failed\n");
+ tevent_req_error(req, ret);
+ return tevent_req_post(req, ev);
+ }
+
+ D_DEBUG("Running debug %s with args \"%s %s\"\n",
+ debug_script, argv[1], argv[2]);
+
+ subreq = run_proc_send(state, ev, ectx->run_ctx, debug_script, argv,
+ tevent_timeval_zero());
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, run_debug_done, req);
+
+ talloc_free(argv);
+ return req;
+}
+
+static void run_debug_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct run_debug_state *state = tevent_req_data(
+ req, struct run_debug_state);
+ char *output;
+ int ret;
+ bool status;
+
+ status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("Running debug failed, ret=%d\n", ret);
+ }
+
+ /* Log output */
+ if (output != NULL) {
+ debug_log(DEBUG_ERR, output, "event_debug");
+ talloc_free(output);
+ }
+
+ kill(-state->pid, SIGTERM);
+ tevent_req_done(req);
+}
+
+static bool run_debug_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Utility functions for running a single event
+ */
+
+static int script_filter(const struct dirent *de)
+{
+ size_t namelen = strlen(de->d_name);
+ char *ptr;
+
+ /* Ignore . and .. */
+ if (namelen < 3) {
+ return 0;
+ }
+
+ /* Skip filenames with ~ */
+ ptr = strchr(de->d_name, '~');
+ if (ptr != NULL) {
+ return 0;
+ }
+
+ /* Filename should start with [0-9][0-9]. */
+ if ((! isdigit(de->d_name[0])) ||
+ (! isdigit(de->d_name[1])) ||
+ (de->d_name[2] != '.')) {
+ return 0;
+ }
+
+ /* Ignore file names longer than MAX_SCRIPT_NAME */
+ if (namelen > MAX_SCRIPT_NAME) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int get_script_list(TALLOC_CTX *mem_ctx,
+ const char *script_dir,
+ struct ctdb_script_list **out)
+{
+ struct dirent **namelist = NULL;
+ struct ctdb_script_list *script_list;
+ int count, ret;
+ int i;
+
+ script_list = talloc_zero(mem_ctx, struct ctdb_script_list);
+ if (script_list == NULL) {
+ return ENOMEM;
+ }
+
+ count = scandir(script_dir, &namelist, script_filter, alphasort);
+ if (count == -1) {
+ ret = errno;
+ if (ret == ENOENT) {
+ D_WARNING("event script dir %s removed\n", script_dir);
+ } else {
+ D_WARNING("scandir() failed on %s, ret=%d\n",
+ script_dir, ret);
+ }
+ *out = script_list;
+ ret = 0;
+ goto done;
+ }
+
+ if (count == 0) {
+ *out = script_list;
+ ret = 0;
+ goto done;
+ }
+
+ script_list->num_scripts = count;
+ script_list->script = talloc_zero_array(script_list,
+ struct ctdb_script,
+ count);
+ if (script_list->script == NULL) {
+ ret = ENOMEM;
+ talloc_free(script_list);
+ goto done;
+ }
+
+ for (i=0; iscript[i];
+ size_t len;
+
+ len = strlcpy(s->name, namelist[i]->d_name, sizeof(s->name));
+ if (len >= sizeof(s->name)) {
+ ret = EIO;
+ talloc_free(script_list);
+ goto done;
+ }
+ }
+
+ *out = script_list;
+ ret = 0;
+
+done:
+ if (namelist != NULL && count != -1) {
+ for (i=0; id_name, script_name) == 0) {
+
+ /* check for valid script names */
+ ret = script_filter(de);
+ if (ret == 0) {
+ closedir(dirp);
+ return EINVAL;
+ }
+
+ found = true;
+ break;
+ }
+ }
+ closedir(dirp);
+
+ if (! found) {
+ return ENOENT;
+ }
+
+ filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name);
+ if (filename == NULL) {
+ return ENOMEM;
+ }
+
+ ret = stat(filename, &st);
+ if (ret != 0) {
+ ret = errno;
+ goto done;
+ }
+
+ if (enable) {
+ new_mode = st.st_mode | S_IXUSR;
+ } else {
+ new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ }
+
+ ret = chmod(filename, new_mode);
+ if (ret != 0) {
+ ret = errno;
+ goto done;
+ }
+
+done:
+ talloc_free(filename);
+ return ret;
+}
+
+static int script_args(TALLOC_CTX *mem_ctx, enum ctdb_event event,
+ const char *arg_str, const char ***out)
+{
+ const char **argv;
+ int argc;
+
+ argv = talloc_array(mem_ctx, const char *, 7);
+ if (argv == NULL) {
+ return ENOMEM;
+ }
+
+ argv[0] = NULL; /* script name */
+ argv[1] = ctdb_event_to_string(event);
+ argc = 2;
+
+ if (arg_str != NULL) {
+ char *str, *t, *tok;
+
+ str = talloc_strdup(argv, arg_str);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+
+ t = str;
+ while ((tok = strtok(t, " ")) != NULL) {
+ argv[argc] = talloc_strdup(argv, tok);
+ if (argv[argc] == NULL) {
+ talloc_free(argv);
+ return ENOMEM;
+ }
+ argc += 1;
+ if (argc >= 7) {
+ talloc_free(argv);
+ return EINVAL;
+ }
+ t = NULL;
+ }
+
+ talloc_free(str);
+ }
+
+ argv[argc] = NULL;
+ argc += 1;
+
+ *out = argv;
+ return 0;
+}
+
+/*
+ * Run a single event
+ */
+
+struct run_event_state {
+ struct tevent_context *ev;
+ struct eventd_context *ectx;
+ struct timeval timeout;
+ enum ctdb_event event;
+
+ struct ctdb_script_list *script_list;
+ const char **argv;
+ int index;
+ int status;
+};
+
+static struct tevent_req *run_event_run_script(struct tevent_req *req);
+static void run_event_next_script(struct tevent_req *subreq);
+static void run_event_debug(struct tevent_req *req, pid_t pid);
+static void run_event_debug_done(struct tevent_req *subreq);
+
+static struct tevent_req *run_event_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ enum ctdb_event event,
+ const char *arg_str,
+ uint32_t timeout)
+{
+ struct tevent_req *req, *subreq;
+ struct run_event_state *state;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct run_event_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->ectx = ectx;
+ state->event = event;
+
+ ret = get_script_list(state, eventd_script_dir(ectx),
+ &state->script_list);
+ if (ret != 0) {
+ D_ERR("get_script_list() failed, ret=%d\n", ret);
+ tevent_req_error(req, ret);
+ return tevent_req_post(req, ev);
+ }
+
+ /* No scripts */
+ if (state->script_list->num_scripts == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = script_args(state, event, arg_str, &state->argv);
+ if (ret != 0) {
+ D_ERR("script_args() failed, ret=%d\n", ret);
+ tevent_req_error(req, ret);
+ return tevent_req_post(req, ev);
+ }
+
+ if (timeout > 0) {
+ state->timeout = tevent_timeval_current_ofs(timeout, 0);
+ }
+ state->index = 0;
+ eventd_start_running(ectx, event, req);
+
+ subreq = run_event_run_script(req);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, run_event_next_script, req);
+
+ return req;
+}
+
+static struct tevent_req *run_event_run_script(struct tevent_req *req)
+{
+ struct run_event_state *state = tevent_req_data(
+ req, struct run_event_state);
+ struct ctdb_script *script;
+ struct tevent_req *subreq;
+ char *path;
+
+ script = &state->script_list->script[state->index];
+
+ path = talloc_asprintf(state, "%s/%s",
+ eventd_script_dir(state->ectx), script->name);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ state->argv[0] = script->name;
+ script->start = tevent_timeval_current();
+
+ D_DEBUG("Running %s with args \"%s %s\"\n",
+ path, state->argv[0], state->argv[1]);
+
+ subreq = run_proc_send(state, state->ev, state->ectx->run_ctx,
+ path, state->argv, state->timeout);
+
+ talloc_free(path);
+
+ return subreq;
+}
+
+static void run_event_next_script(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct run_event_state *state = tevent_req_data(
+ req, struct run_event_state);
+ struct ctdb_script *script;
+ char *output;
+ struct run_proc_result result;
+ pid_t pid;
+ int ret;
+ bool status;
+
+ script = &state->script_list->script[state->index];
+ script->finished = tevent_timeval_current();
+
+ status = run_proc_recv(subreq, &ret, &result, &pid, state, &output);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
+ script->name, result.sig, result.err, result.status);
+
+ if (output != NULL) {
+ debug_log(DEBUG_ERR, output, script->name);
+ }
+
+ if (result.sig > 0) {
+ script->status = -EINTR;
+ } else if (result.err > 0) {
+ if (result.err == EACCES) {
+ /* Map EACCESS to ENOEXEC */
+ script->status = -ENOEXEC;
+ } else {
+ script->status = -result.err;
+ }
+ } else {
+ script->status = result.status;
+ }
+
+ if (script->status != 0 && output != NULL) {
+ size_t n;
+
+ n = strlcpy(script->output, output, MAX_SCRIPT_OUTPUT);
+ if (n >= MAX_SCRIPT_OUTPUT) {
+ script->output[MAX_SCRIPT_OUTPUT] = '\0';
+ }
+ }
+
+ /* If a script fails, stop running */
+ if (script->status != 0 && script->status != -ENOEXEC) {
+ state->status = script->status;
+ eventd_stop_running(state->ectx);
+ state->script_list->num_scripts = state->index + 1;
+ eventd_set_result(state->ectx, state->event,
+ state->script_list, state->status);
+
+ if (state->status == -ETIME && pid != -1) {
+ run_event_debug(req, pid);
+ }
+
+ D_ERR("%s event %s\n", ctdb_event_to_string(state->event),
+ (state->status == -ETIME) ? "timed out" : "failed");
+
+ tevent_req_done(req);
+ return;
+ }
+
+ state->index += 1;
+
+ /* All scripts executed */
+ if (state->index >= state->script_list->num_scripts) {
+ eventd_stop_running(state->ectx);
+ eventd_set_result(state->ectx, state->event,
+ state->script_list, state->status);
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = run_event_run_script(req);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, run_event_next_script, req);
+}
+
+static void run_event_debug(struct tevent_req *req, pid_t pid)
+{
+ struct run_event_state *state = tevent_req_data(
+ req, struct run_event_state);
+ struct tevent_req *subreq;
+
+ /* Debug script is run with ectx as the memory context */
+ subreq = run_debug_send(state->ectx, state->ev, state->ectx,
+ state->event, pid);
+ if (subreq == NULL) {
+ /* If run debug fails, it's not an error */
+ D_NOTICE("Failed to run event debug\n");
+ return;
+ }
+ tevent_req_set_callback(subreq, run_event_debug_done, NULL);
+}
+
+static void run_event_debug_done(struct tevent_req *subreq)
+{
+ int ret = 0;
+ bool status;
+
+ status = run_debug_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_NOTICE("run_debug() failed, ret=%d\n", ret);
+ }
+}
+
+static bool run_event_recv(struct tevent_req *req, int *perr, int *status)
+{
+ struct run_event_state *state = tevent_req_data(
+ req, struct run_event_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (ret == ECANCELED) {
+ if (status != NULL) {
+ *status = -ECANCELED;
+ }
+ return true;
+ }
+
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (status != NULL) {
+ *status = state->status;
+ }
+ return true;
+}
+
+/*
+ * Process RUN command
+ */
+
+struct command_run_state {
+ struct tevent_context *ev;
+ struct eventd_context *ectx;
+ struct eventd_client *client;
+
+ enum ctdb_event event;
+ uint32_t timeout;
+ const char *arg_str;
+ struct ctdb_event_reply *reply;
+};
+
+static void command_run_trigger(struct tevent_req *req, void *private_data);
+static void command_run_done(struct tevent_req *subreq);
+
+static struct tevent_req *command_run_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ struct eventd_client *client,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req;
+ struct command_run_state *state;
+ struct pending_event *pending;
+ enum ctdb_event running_event;
+ bool running, status;
+
+ req = tevent_req_create(mem_ctx, &state, struct command_run_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->ectx = ectx;
+ state->client = client;
+
+ state->event = request->rdata.data.run->event;
+ state->timeout = request->rdata.data.run->timeout;
+ state->arg_str = talloc_steal(state, request->rdata.data.run->arg_str);
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.command = request->rdata.command;
+
+ /*
+ * If monitor event is running,
+ * Cancel the running monitor event and run new event
+ *
+ * If any other event is running,
+ * If new event is monitor, cancel that event
+ * Else add new event to the queue
+ */
+
+ running = eventd_is_running(ectx, &running_event);
+ if (running) {
+ if (running_event == CTDB_EVENT_MONITOR) {
+ eventd_cancel_running(ectx);
+ } else if (state->event == CTDB_EVENT_MONITOR) {
+ state->reply->rdata.result = -ECANCELED;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ pending = talloc_zero(state, struct pending_event);
+ if (tevent_req_nomem(pending, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ pending->req = req;
+ DLIST_ADD(client->pending_list, pending);
+
+ status = tevent_queue_add(eventd_queue(ectx), ev, req,
+ command_run_trigger, pending);
+ if (! status) {
+ tevent_req_error(req, ENOMEM);
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void command_run_trigger(struct tevent_req *req, void *private_data)
+{
+ struct pending_event *pending = talloc_get_type_abort(
+ private_data, struct pending_event);
+ struct command_run_state *state = tevent_req_data(
+ req, struct command_run_state);
+ struct tevent_req *subreq;
+
+ DLIST_REMOVE(state->client->pending_list, pending);
+
+ if (pending->req != req) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ talloc_free(pending);
+
+ D_DEBUG("Running event %s with args \"%s\"\n",
+ ctdb_event_to_string(state->event), state->arg_str);
+
+ subreq = run_event_send(state, state->ev, state->ectx,
+ state->event, state->arg_str, state->timeout);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, command_run_done, req);
+}
+
+static void command_run_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct command_run_state *state = tevent_req_data(
+ req, struct command_run_state);
+ int ret, result;
+ bool status;
+
+ status = run_event_recv(subreq, &ret, &result);
+ if (! status) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->reply->rdata.result = result;
+ tevent_req_done(req);
+}
+
+static bool command_run_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct command_run_state *state = tevent_req_data(
+ req, struct command_run_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (reply != NULL) {
+ *reply = talloc_steal(mem_ctx, state->reply);
+ }
+ return true;
+}
+
+/*
+ * Process STATUS command
+ */
+
+struct command_status_state {
+ struct ctdb_event_reply *reply;
+};
+
+static struct tevent_req *command_status_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ struct eventd_client *client,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req;
+ struct command_status_state *state;
+ enum ctdb_event event;
+ enum ctdb_event_status_state estate;
+
+ req = tevent_req_create(mem_ctx, &state, struct command_status_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ event = request->rdata.data.status->event;
+ estate = request->rdata.data.status->state;
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.data.status =
+ talloc(state->reply, struct ctdb_event_reply_status);
+ if (tevent_req_nomem(state->reply->rdata.data.status, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.command = request->rdata.command;
+ state->reply->rdata.result = 0;
+ state->reply->rdata.data.status->status =
+ eventd_get_result(ectx, event, estate,
+ &state->reply->rdata.data.status->script_list);
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static bool command_status_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct command_status_state *state = tevent_req_data(
+ req, struct command_status_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (reply != NULL) {
+ *reply = talloc_steal(mem_ctx, state->reply);
+ }
+ return true;
+}
+
+/*
+ * Process SCRIPT_LIST command
+ */
+
+struct command_script_list_state {
+ struct ctdb_event_reply *reply;
+};
+
+static struct tevent_req *command_script_list_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ struct eventd_client *client,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req;
+ struct command_script_list_state *state;
+ struct ctdb_script_list *script_list;
+ int ret, i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct command_script_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.data.script_list =
+ talloc(state->reply, struct ctdb_event_reply_script_list);
+ if (tevent_req_nomem(state->reply->rdata.data.script_list, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.command = request->rdata.command;
+
+ ret = get_script_list(state, eventd_script_dir(ectx), &script_list);
+ if (ret != 0) {
+ state->reply->rdata.result = -ret;
+ state->reply->rdata.data.script_list->script_list = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; inum_scripts; i++) {
+ struct ctdb_script *script = &script_list->script[i];
+ struct stat st;
+ char *path = NULL;
+
+ path = talloc_asprintf(state, "%s/%s",
+ eventd_script_dir(ectx), script->name);
+ if (tevent_req_nomem(path, req)) {
+ continue;
+ }
+
+ ret = stat(path, &st);
+ if (ret != 0) {
+ TALLOC_FREE(path);
+ continue;
+ }
+
+ if (! (st.st_mode & S_IXUSR)) {
+ script->status = -ENOEXEC;
+ }
+
+ TALLOC_FREE(path);
+ }
+
+ state->reply->rdata.data.script_list->script_list =
+ talloc_steal(state->reply, script_list);
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static bool command_script_list_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct command_script_list_state *state = tevent_req_data(
+ req, struct command_script_list_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (reply != NULL) {
+ *reply = talloc_steal(mem_ctx, state->reply);
+ }
+ return true;
+}
+
+/*
+ * Process SCRIPT_ENABLE command
+ */
+
+struct command_script_enable_state {
+ struct ctdb_event_reply *reply;
+};
+
+static struct tevent_req *command_script_enable_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ struct eventd_client *client,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req;
+ struct command_script_enable_state *state;
+ const char *script_name;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct command_script_enable_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ script_name = request->rdata.data.script_enable->script_name;
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.command = request->rdata.command;
+
+ ret = script_chmod(state, eventd_script_dir(ectx), script_name, true);
+ state->reply->rdata.result = -ret;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static bool command_script_enable_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct command_script_enable_state *state = tevent_req_data(
+ req, struct command_script_enable_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (reply != NULL) {
+ *reply = talloc_steal(mem_ctx, state->reply);
+ }
+ return true;
+}
+
+/*
+ * Process SCRIPT_DISABLE command
+ */
+
+struct command_script_disable_state {
+ struct ctdb_event_reply *reply;
+};
+
+static struct tevent_req *command_script_disable_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct eventd_context *ectx,
+ struct eventd_client *client,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req;
+ struct command_script_disable_state *state;
+ const char *script_name;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct command_script_disable_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ script_name = request->rdata.data.script_disable->script_name;
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->rdata.command = request->rdata.command;
+
+ ret = script_chmod(state, eventd_script_dir(ectx), script_name, false);
+ state->reply->rdata.result = -ret;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static bool command_script_disable_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct command_script_disable_state *state = tevent_req_data(
+ req, struct command_script_disable_state);
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ if (reply != NULL) {
+ *reply = talloc_steal(mem_ctx, state->reply);
+ }
+ return true;
+}
+
+/*
+ * Process clients
+ */
+
+static struct eventd_client *client_find(struct eventd_context *ectx,
+ struct sock_client_context *client_ctx)
+{
+ struct eventd_client *client;
+
+ for (client = ectx->client_list;
+ client != NULL;
+ client = client->next) {
+ if (client->client_ctx == client_ctx) {
+ return client;
+ }
+ }
+
+ return NULL;
+}
+
+static bool client_connect(struct sock_client_context *client_ctx,
+ void *private_data)
+{
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
+ struct eventd_client *client;
+
+ client = talloc_zero(ectx, struct eventd_client);
+ if (client == NULL) {
+ return false;
+ }
+
+ client->client_ctx = client_ctx;
+
+ DLIST_ADD(ectx->client_list, client);
+ return true;
+}
+
+static void client_disconnect(struct sock_client_context *client_ctx,
+ void *private_data)
+{
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
+ struct eventd_client *client;
+ struct pending_event *pe;
+
+ client = client_find(ectx, client_ctx);
+ if (client == NULL) {
+ return;
+ }
+
+ /* Get rid of pending events */
+ while ((pe = client->pending_list) != NULL) {
+ DLIST_REMOVE(client->pending_list, pe);
+ talloc_free(pe->req);
+ }
+}
+
+struct client_process_state {
+ struct tevent_context *ev;
+
+ struct eventd_client *client;
+ struct ctdb_event_request request;
+};
+
+static void client_run_done(struct tevent_req *subreq);
+static void client_status_done(struct tevent_req *subreq);
+static void client_script_list_done(struct tevent_req *subreq);
+static void client_script_enable_done(struct tevent_req *subreq);
+static void client_script_disable_done(struct tevent_req *subreq);
+static void client_process_reply(struct tevent_req *req,
+ struct ctdb_event_reply *reply);
+static void client_process_reply_done(struct tevent_req *subreq);
+
+static struct tevent_req *client_process_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sock_client_context *client_ctx,
+ uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
+ struct tevent_req *req, *subreq;
+ struct client_process_state *state;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct client_process_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ state->client = client_find(ectx, client_ctx);
+ if (state->client == NULL) {
+ tevent_req_error(req, EIO);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = ctdb_event_request_pull(buf, buflen, state, &state->request);
+ if (ret != 0) {
+ tevent_req_error(req, EPROTO);
+ return tevent_req_post(req, ev);
+ }
+
+ switch (state->request.rdata.command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ subreq = command_run_send(state, ev, ectx, state->client,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, client_run_done, req);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ subreq = command_status_send(state, ev, ectx, state->client,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, client_status_done, req);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ subreq = command_script_list_send(state, ev, ectx,
+ state->client,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, client_script_list_done, req);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ subreq = command_script_enable_send(state, ev, ectx,
+ state->client,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, client_script_enable_done,
+ req);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ subreq = command_script_disable_send(state, ev, ectx,
+ state->client,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, client_script_disable_done,
+ req);
+ break;
+ }
+
+ return req;
+}
+
+static void client_run_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct ctdb_event_reply *reply = NULL;
+ int ret = 0;
+ bool status;
+
+ status = command_run_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("COMMAND_RUN failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ client_process_reply(req, reply);
+ talloc_free(reply);
+}
+
+static void client_status_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct ctdb_event_reply *reply = NULL;
+ int ret = 0;
+ bool status;
+
+ status = command_status_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("COMMAND_STATUS failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ client_process_reply(req, reply);
+ talloc_free(reply);
+}
+
+static void client_script_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct ctdb_event_reply *reply = NULL;
+ int ret = 0;
+ bool status;
+
+ status = command_script_list_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("COMMAND_SCRIPT_LIST failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ client_process_reply(req, reply);
+ talloc_free(reply);
+}
+
+static void client_script_enable_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct ctdb_event_reply *reply = NULL;
+ int ret = 0;
+ bool status;
+
+ status = command_script_enable_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("COMMAND_SCRIPT_ENABLE failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ client_process_reply(req, reply);
+ talloc_free(reply);
+}
+
+static void client_script_disable_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct ctdb_event_reply *reply = NULL;
+ int ret = 0;
+ bool status;
+
+ status = command_script_disable_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("COMMAND_SCRIPT_DISABLE failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ client_process_reply(req, reply);
+ talloc_free(reply);
+}
+
+static void client_process_reply(struct tevent_req *req,
+ struct ctdb_event_reply *reply)
+{
+ struct client_process_state *state = tevent_req_data(
+ req, struct client_process_state);
+ struct tevent_req *subreq;
+ uint8_t *buf;
+ size_t buflen;
+ int ret;
+
+ ctdb_event_header_fill(&reply->header, state->request.header.reqid);
+
+ buflen = ctdb_event_reply_len(reply);
+ buf = talloc_zero_size(state, buflen);
+ if (tevent_req_nomem(buf, req)) {
+ return;
+ }
+
+ ret = ctdb_event_reply_push(reply, buf, &buflen);
+ if (ret != 0) {
+ talloc_free(buf);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sock_socket_write_send(state, state->ev,
+ state->client->client_ctx,
+ buf, buflen);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, client_process_reply_done, req);
+}
+
+static void client_process_reply_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+ bool status;
+
+ status = sock_socket_write_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("Sending reply failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool client_process_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Event daemon
+ */
+
+static void eventd_shutdown(void *private_data)
+{
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
+ struct eventd_client *client;
+
+ while ((client = ectx->client_list) != NULL) {
+ DLIST_REMOVE(ectx->client_list, client);
+ talloc_free(client);
+ }
+}
+
+static struct {
+ const char *debug_script;
+ const char *script_dir;
+ const char *logging;
+ const char *debug_level;
+ const char *pidfile;
+ const char *socket;
+ int pid;
+} options = {
+ .debug_level = "ERR",
+};
+
+struct poptOption cmdline_options[] = {
+ POPT_AUTOHELP
+ { "debug_script", 'D', POPT_ARG_STRING, &options.debug_script, 0,
+ "debug script", "FILE" },
+ { "pid", 'P', POPT_ARG_INT, &options.pid, 0,
+ "pid to wait for", "PID" },
+ { "event_script_dir", 'e', POPT_ARG_STRING, &options.script_dir, 0,
+ "event script dir", "DIRECTORY" },
+ { "logging", 'l', POPT_ARG_STRING, &options.logging, 0,
+ "logging specification" },
+ { "debug", 'd', POPT_ARG_STRING, &options.debug_level, 0,
+ "debug level" },
+ { "pidfile", 'p', POPT_ARG_STRING, &options.pidfile, 0,
+ "eventd pid file", "FILE" },
+ { "socket", 's', POPT_ARG_STRING, &options.socket, 0,
+ "eventd socket path", "FILE" },
+ POPT_TABLEEND
+};
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct eventd_context *ectx;
+ struct sock_daemon_context *sockd;
+ struct sock_daemon_funcs daemon_funcs;
+ struct sock_socket_funcs socket_funcs;
+ struct stat statbuf;
+ int opt, ret;
+
+ /* Set default options */
+ options.pid = -1;
+
+ pc = poptGetContext(argv[0], argc, argv, cmdline_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ fprintf(stderr, "Invalid options %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ exit(1);
+ }
+
+ if (options.socket == NULL) {
+ fprintf(stderr, "Please specify eventd socket (--socket)\n");
+ exit(1);
+ }
+
+ if (options.script_dir == NULL) {
+ fprintf(stderr,
+ "Please specify script dir (--event_script_dir)\n");
+ exit(1);
+ }
+
+ if (options.logging == NULL) {
+ fprintf(stderr,
+ "Please specify logging (--logging)\n");
+ exit(1);
+ }
+
+ ret = stat(options.script_dir, &statbuf);
+ if (ret != 0) {
+ ret = errno;
+ fprintf(stderr, "Error reading script_dir %s, ret=%d\n",
+ options.script_dir, ret);
+ exit(1);
+ }
+ if (! S_ISDIR(statbuf.st_mode)) {
+ fprintf(stderr, "script_dir %s is not a directory\n",
+ options.script_dir);
+ exit(1);
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ exit(1);
+ }
+
+ ev = tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ ret = 1;
+ goto fail;
+ }
+
+ ret = eventd_context_init(mem_ctx, ev, options.script_dir,
+ options.debug_script, &ectx);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ daemon_funcs = (struct sock_daemon_funcs) {
+ .shutdown = eventd_shutdown,
+ };
+
+ ret = sock_daemon_setup(mem_ctx, "ctdb-eventd", options.logging,
+ options.debug_level, options.pidfile,
+ &daemon_funcs, ectx, &sockd);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ socket_funcs = (struct sock_socket_funcs) {
+ .connect = client_connect,
+ .disconnect = client_disconnect,
+ .read_send = client_process_send,
+ .read_recv = client_process_recv,
+ };
+
+ ret = sock_daemon_add_unix(sockd, options.socket, &socket_funcs, ectx);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ ret = sock_daemon_run(ev, sockd, options.pid);
+ if (ret == EINTR) {
+ ret = 0;
+ }
+
+fail:
+ talloc_free(mem_ctx);
+ (void)poptFreeContext(pc);
+ exit(ret);
+}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_event_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_event_helper.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_event_helper.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_event_helper.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,158 +0,0 @@
-/*
- ctdb event script helper
-
- Copyright (C) Amitay Isaacs 2013
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see .
-*/
-
-#include "replace.h"
-#include "system/filesys.h"
-#include "system/network.h"
-#include "system/wait.h"
-
-#include
-
-#include "lib/util/blocking.h"
-
-#include "ctdb_private.h"
-
-#include "common/system.h"
-
-static char *progname = NULL;
-
-
-/* CTDB sends SIGTERM, when process must die */
-static void sigterm(int sig)
-{
- pid_t pid;
-
- /* all the child processes are running in the same process group */
- pid = getpgrp();
- if (pid == -1) {
- kill(-getpid(), SIGKILL);
- } else {
- kill(-pid, SIGKILL);
- }
- _exit(0);
-}
-
-static int check_executable(const char *path)
-{
- struct stat st;
-
- if (stat(path, &st) != 0) {
- fprintf(stderr, "Failed to access '%s' - %s\n",
- path, strerror(errno));
- return errno;
- }
-
- if (!(st.st_mode & S_IXUSR)) {
- return ENOEXEC;
- }
-
- return 0;
-}
-
-static void usage(void)
-{
- fprintf(stderr, "\n");
- fprintf(stderr, "Usage: %s []\n",
- progname);
-}
-
-int main(int argc, char *argv[])
-{
- int log_fd, write_fd;
- pid_t pid;
- int status, output, ret;
-
- progname = argv[0];
-
- if (argc < 5) {
- usage();
- exit(1);
- }
-
- reset_scheduler();
-
- log_fd = atoi(argv[1]);
- write_fd = atoi(argv[2]);
-
- set_close_on_exec(write_fd);
-
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- dup2(log_fd, STDOUT_FILENO);
- dup2(log_fd, STDERR_FILENO);
- close(log_fd);
-
- if (setpgid(0, 0) != 0) {
- fprintf(stderr, "Failed to create process group for event script - %s\n",
- strerror(errno));
- exit(1);
- }
-
- signal(SIGTERM, sigterm);
-
- pid = fork();
- if (pid < 0) {
- int save_errno = errno;
- fprintf(stderr, "Failed to fork - %s\n", strerror(errno));
- sys_write(write_fd, &save_errno, sizeof(save_errno));
- exit(1);
- }
-
- if (pid == 0) {
- ret = check_executable(argv[3]);
- if (ret != 0) {
- _exit(ret);
- }
- ret = execv(argv[3], &argv[3]);
- if (ret != 0) {
- int save_errno = errno;
- fprintf(stderr, "Error executing '%s' - %s\n",
- argv[3], strerror(save_errno));
- }
- /* This should never happen */
- _exit(ENOEXEC);
- }
-
- ret = waitpid(pid, &status, 0);
- if (ret == -1) {
- output = -errno;
- fprintf(stderr, "waitpid() failed - %s\n", strerror(errno));
- sys_write(write_fd, &output, sizeof(output));
- exit(1);
- }
- if (WIFEXITED(status)) {
- output = WEXITSTATUS(status);
- /* Only errors should be returned as -ve values */
- if (output == ENOENT || output == ENOEXEC) {
- output = -output;
- }
- sys_write(write_fd, &output, sizeof(output));
- exit(0);
- }
- if (WIFSIGNALED(status)) {
- output = -EINTR;
- fprintf(stderr, "Process terminated with signal - %d\n",
- WTERMSIG(status));
- sys_write(write_fd, &output, sizeof(output));
- exit(0);
- }
-
- fprintf(stderr, "waitpid() status=%d\n", status);
- exit(1);
-}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_fork.c samba-4.6.5+dfsg/ctdb/server/ctdb_fork.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_fork.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_fork.c 2017-01-11 07:55:14.000000000 +0000
@@ -25,6 +25,7 @@
#include
#include "lib/util/debug.h"
+#include "lib/util/time.h"
#include "ctdb_private.h"
#include "ctdb_client.h"
@@ -34,20 +35,6 @@
#include "common/common.h"
#include "common/logging.h"
-void ctdb_set_child_info(TALLOC_CTX *mem_ctx, const char *child_name_fmt, ...)
-{
- if (child_name_fmt != NULL) {
- va_list ap;
- char *t;
-
- va_start(ap, child_name_fmt);
- t = talloc_vasprintf(mem_ctx, child_name_fmt, ap);
- debug_extra = talloc_asprintf(mem_ctx, "%s:", t);
- talloc_free(t);
- va_end(ap);
- }
-}
-
void ctdb_track_child(struct ctdb_context *ctdb, pid_t pid)
{
char *process;
@@ -68,6 +55,10 @@
pid_t ctdb_fork(struct ctdb_context *ctdb)
{
pid_t pid;
+ struct timeval before;
+ double delta_t;
+
+ before = timeval_current();
pid = fork();
if (pid == -1) {
@@ -76,8 +67,6 @@
return -1;
}
if (pid == 0) {
- ctdb_set_child_info(ctdb, NULL);
-
/* Close the Unix Domain socket and the TCP socket.
* This ensures that none of the child processes will
* look like the main daemon when it is not running.
@@ -104,6 +93,57 @@
return 0;
}
+ delta_t = timeval_elapsed(&before);
+ if (delta_t > 3.0) {
+ DEBUG(DEBUG_WARNING, ("fork() took %lf seconds\n", delta_t));
+ }
+
+ ctdb_track_child(ctdb, pid);
+ return pid;
+}
+
+/*
+ * vfork + exec
+ */
+pid_t ctdb_vfork_exec(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
+ const char *helper, int helper_argc,
+ const char **helper_argv)
+{
+ pid_t pid;
+ struct timeval before;
+ double delta_t;
+ char **argv;
+ int i;
+
+ argv = talloc_array(mem_ctx, char *, helper_argc + 1);
+ if (argv == NULL) {
+ DEBUG(DEBUG_ERR, ("Memory allocation error\n"));
+ return -1;
+ }
+
+ argv[0] = discard_const(helper);
+ for (i=0; i 3.0) {
+ DEBUG(DEBUG_WARNING, ("vfork() took %lf seconds\n", delta_t));
+ }
+
ctdb_track_child(ctdb, pid);
return pid;
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_lock.c samba-4.6.5+dfsg/ctdb/server/ctdb_lock.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_lock.c 2017-01-30 09:56:26.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_lock.c 2017-01-26 12:19:08.000000000 +0000
@@ -28,10 +28,10 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "ctdb_private.h"
-#include "common/system.h"
#include "common/common.h"
#include "common/logging.h"
@@ -392,8 +392,10 @@
void *private_data)
{
static char debug_locks[PATH_MAX+1] = "";
+ static struct timeval last_debug_time;
struct lock_context *lock_ctx;
struct ctdb_context *ctdb;
+ struct timeval now;
pid_t pid;
double elapsed_time;
int new_timer;
@@ -401,12 +403,6 @@
lock_ctx = talloc_get_type_abort(private_data, struct lock_context);
ctdb = lock_ctx->ctdb;
- /* If a node stopped/banned, don't spam the logs */
- if (ctdb->nodes[ctdb->pnn]->flags & NODE_FLAGS_INACTIVE) {
- lock_ctx->ttimer = NULL;
- return;
- }
-
elapsed_time = timeval_elapsed(&lock_ctx->start_time);
if (lock_ctx->ctdb_db) {
DEBUG(DEBUG_WARNING,
@@ -419,6 +415,19 @@
elapsed_time));
}
+ /* If a node stopped/banned, don't spam the logs */
+ if (ctdb->nodes[ctdb->pnn]->flags & NODE_FLAGS_INACTIVE) {
+ goto skip_lock_debug;
+ }
+
+ /* Restrict log debugging to once per second */
+ now = timeval_current();
+ if (last_debug_time.tv_sec == now.tv_sec) {
+ goto skip_lock_debug;
+ }
+
+ last_debug_time.tv_sec = now.tv_sec;
+
if (ctdb_set_helper("lock debugging helper",
debug_locks, sizeof(debug_locks),
"CTDB_DEBUG_LOCKS",
@@ -435,6 +444,8 @@
" Unable to setup lock debugging\n"));
}
+skip_lock_debug:
+
/* Back-off logging if lock is not obtained for a long time */
if (elapsed_time < 100.0) {
new_timer = 10;
@@ -650,9 +661,9 @@
return;
}
- if (!ctdb_vfork_with_logging(lock_ctx, ctdb, "lock_helper",
- prog, argc, (const char **)args,
- NULL, NULL, &lock_ctx->child)) {
+ lock_ctx->child = ctdb_vfork_exec(lock_ctx, ctdb, prog, argc,
+ (const char **)args);
+ if (lock_ctx->child == -1) {
DEBUG(DEBUG_ERR, ("Failed to create a child in ctdb_lock_schedule\n"));
close(lock_ctx->fd[0]);
close(lock_ctx->fd[1]);
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_lock_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_lock_helper.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_lock_helper.c 2017-01-30 09:56:26.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_lock_helper.c 2017-01-26 12:19:08.000000000 +0000
@@ -24,10 +24,12 @@
#include
#include
+#include
+#include "lib/util/sys_rw.h"
#include "lib/util/tevent_unix.h"
-#include "ctdb_private.h"
+#include "protocol/protocol.h"
#include "common/system.h"
@@ -55,8 +57,7 @@
realtime = set_scheduler();
if (! realtime) {
fprintf(stderr,
- "%s: Unable to set real-time scheduler priority\n",
- progname);
+ "locking: Unable to set real-time scheduler priority\n");
}
}
@@ -79,10 +80,8 @@
static void usage(void)
{
fprintf(stderr, "\n");
- fprintf(stderr, "Usage: %s RECORD \n",
- progname);
- fprintf(stderr, " %s DB \n",
- progname);
+ fprintf(stderr, "Usage: %s RECORD \n", progname);
+ fprintf(stderr, " %s DB \n", progname);
}
static uint8_t *hex_decode_talloc(TALLOC_CTX *mem_ctx,
@@ -121,16 +120,15 @@
state->tdb = tdb_open(dbpath, 0, tdb_flags, O_RDWR, 0600);
if (state->tdb == NULL) {
- fprintf(stderr, "%s: Error opening database %s\n",
- progname, dbpath);
+ fprintf(stderr, "locking: Error opening database %s\n", dbpath);
return 1;
}
set_priority();
if (tdb_chainlock(state->tdb, state->key) < 0) {
- fprintf(stderr, "%s: Error getting record lock (%s)\n",
- progname, tdb_errorstr(state->tdb));
+ fprintf(stderr, "locking: Error getting record lock (%s)\n",
+ tdb_errorstr(state->tdb));
return 1;
}
@@ -150,16 +148,15 @@
state->tdb = tdb_open(dbpath, 0, tdb_flags, O_RDWR, 0600);
if (state->tdb == NULL) {
- fprintf(stderr, "%s: Error opening database %s\n",
- progname, dbpath);
+ fprintf(stderr, "locking: Error opening database %s\n", dbpath);
return 1;
}
set_priority();
if (tdb_lockall(state->tdb) < 0) {
- fprintf(stderr, "%s: Error getting db lock (%s)\n",
- progname, tdb_errorstr(state->tdb));
+ fprintf(stderr, "locking: Error getting db lock (%s)\n",
+ tdb_errorstr(state->tdb));
return 1;
}
@@ -271,7 +268,7 @@
struct tevent_signal *se;
struct tevent_req *req;
struct lock_state state = { 0 };
- int write_fd, log_fd;
+ int write_fd;
char result = 0;
int ppid;
const char *lock_type;
@@ -281,21 +278,14 @@
progname = argv[0];
- if (argc < 5) {
+ if (argc < 4) {
usage();
exit(1);
}
- log_fd = atoi(argv[1]);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- dup2(log_fd, STDOUT_FILENO);
- dup2(log_fd, STDERR_FILENO);
- close(log_fd);
-
- ppid = atoi(argv[2]);
- write_fd = atoi(argv[3]);
- lock_type = argv[4];
+ ppid = atoi(argv[1]);
+ write_fd = atoi(argv[2]);
+ lock_type = argv[3];
ev = tevent_context_init(NULL);
if (ev == NULL) {
@@ -312,26 +302,27 @@
}
if (strcmp(lock_type, "RECORD") == 0) {
- if (argc != 8) {
- fprintf(stderr, "%s: Invalid number of arguments (%d)\n",
- progname, argc);
+ if (argc != 7) {
+ fprintf(stderr,
+ "locking: Invalid number of arguments (%d)\n",
+ argc);
usage();
exit(1);
}
- result = lock_record(argv[5], argv[6], argv[7], &state);
+ result = lock_record(argv[4], argv[5], argv[6], &state);
} else if (strcmp(lock_type, "DB") == 0) {
- if (argc != 7) {
+ if (argc != 6) {
fprintf(stderr,
"locking: Invalid number of arguments (%d)\n",
argc);
usage();
exit(1);
}
- result = lock_db(argv[5], argv[6], &state);
+ result = lock_db(argv[4], argv[5], &state);
} else {
- fprintf(stderr, "%s: Invalid lock-type '%s'\n", progname, lock_type);
+ fprintf(stderr, "locking: Invalid lock-type '%s'\n", lock_type);
usage();
exit(1);
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_logging.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging.c 2017-01-11 07:55:14.000000000 +0000
@@ -20,7 +20,6 @@
#include "replace.h"
#include "system/filesys.h"
#include "system/network.h"
-#include "system/syslog.h"
#include "system/time.h"
#include
@@ -29,22 +28,15 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/blocking.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/time.h"
#include "ctdb_private.h"
#include "ctdb_client.h"
-#include "common/system.h"
#include "common/common.h"
#include "common/logging.h"
-const char *debug_extra = "";
-
-struct ctdb_log_backend {
- struct ctdb_log_backend *prev, *next;
- const char *prefix;
- ctdb_log_setup_fn_t setup;
-};
-
struct ctdb_log_state {
const char *prefix;
int fd, pfd;
@@ -52,62 +44,28 @@
uint16_t buf_used;
void (*logfn)(const char *, uint16_t, void *);
void *logfn_private;
- struct ctdb_log_backend *backends;
};
/* Used by ctdb_set_child_logging() */
static struct ctdb_log_state *log_state;
-void ctdb_log_register_backend(const char *prefix, ctdb_log_setup_fn_t setup)
-{
- struct ctdb_log_backend *b;
-
- b = talloc_zero(log_state, struct ctdb_log_backend);
- if (b == NULL) {
- printf("Failed to register backend \"%s\" - no memory\n",
- prefix);
- return;
- }
-
- b->prefix = prefix;
- b->setup = setup;
-
- DLIST_ADD_END(log_state->backends, b);
-}
-
-
/* Initialise logging */
-bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging)
+bool ctdb_logging_init(TALLOC_CTX *mem_ctx, const char *logging,
+ const char *debug_level)
{
- struct ctdb_log_backend *b;
int ret;
log_state = talloc_zero(mem_ctx, struct ctdb_log_state);
if (log_state == NULL) {
- printf("talloc_zero failed\n");
- abort();
+ return false;
}
- ctdb_log_init_file();
- ctdb_log_init_syslog();
-
- for (b = log_state->backends; b != NULL; b = b->next) {
- size_t l = strlen(b->prefix);
- /* Exact match with prefix or prefix followed by ':' */
- if (strncmp(b->prefix, logging, l) == 0 &&
- (logging[l] == '\0' || logging[l] == ':')) {
- ret = b->setup(mem_ctx, logging, "ctdbd");
- if (ret == 0) {
- return true;
- }
- printf("Log init for \"%s\" failed with \"%s\"\n",
- logging, strerror(ret));
- return false;
- }
+ ret = logging_init(mem_ctx, logging, debug_level, "ctdbd");
+ if (ret != 0) {
+ return false;
}
- printf("Unable to find log backend for \"%s\"\n", logging);
- return false;
+ return true;
}
/* Note that do_debug always uses the global log state. */
@@ -200,6 +158,8 @@
struct tevent_fd *fde;
char **argv;
int i;
+ struct timeval before;
+ double delta_t;
log = talloc_zero(mem_ctx, struct ctdb_log_state);
CTDB_NO_MEMORY_NULL(ctdb, log);
@@ -230,6 +190,8 @@
argv[i+2] = discard_const(helper_argv[i]);
}
+ before = timeval_current();
+
*pid = vfork();
if (*pid == 0) {
execv(helper, argv);
@@ -243,6 +205,11 @@
goto free_log;
}
+ delta_t = timeval_elapsed(&before);
+ if (delta_t > 3.0) {
+ DEBUG(DEBUG_WARNING, ("vfork() took %lf seconds\n", delta_t));
+ }
+
ctdb_track_child(ctdb, *pid);
log->pfd = p[0];
@@ -269,11 +236,6 @@
int old_stdout, old_stderr;
struct tevent_fd *fde;
- if (log_state->fd == STDOUT_FILENO) {
- /* not needed for stdout logging */
- return 0;
- }
-
/* setup a pipe to catch IO from subprocesses */
if (pipe(p) != 0) {
DEBUG(DEBUG_ERR,(__location__ " Failed to setup for child logging pipe\n"));
@@ -320,48 +282,3 @@
return 0;
}
-
-
-/*
- * set up a log handler to catch logging from TEVENT
- */
-static void ctdb_tevent_logging(void *private_data,
- enum tevent_debug_level level,
- const char *fmt,
- va_list ap) PRINTF_ATTRIBUTE(3, 0);
-static void ctdb_tevent_logging(void *private_data,
- enum tevent_debug_level level,
- const char *fmt,
- va_list ap)
-{
- enum debug_level lvl = DEBUG_CRIT;
-
- switch (level) {
- case TEVENT_DEBUG_FATAL:
- lvl = DEBUG_CRIT;
- break;
- case TEVENT_DEBUG_ERROR:
- lvl = DEBUG_ERR;
- break;
- case TEVENT_DEBUG_WARNING:
- lvl = DEBUG_WARNING;
- break;
- case TEVENT_DEBUG_TRACE:
- lvl = DEBUG_DEBUG;
- break;
- }
-
- if (lvl <= DEBUGLEVEL) {
- dbgtext_va(fmt, ap);
- }
-}
-
-int ctdb_init_tevent_logging(struct ctdb_context *ctdb)
-{
- int ret;
-
- ret = tevent_set_debug(ctdb->ev,
- ctdb_tevent_logging,
- ctdb);
- return ret;
-}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging_file.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging_file.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_logging_file.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging_file.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,121 +0,0 @@
-/*
- ctdb logging code
-
- Copyright (C) Andrew Tridgell 2008
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see .
-*/
-
-#include "replace.h"
-#include "system/time.h"
-#include "system/filesys.h"
-#include "system/network.h"
-
-#include
-
-#include "lib/util/debug.h"
-#include "lib/util/time_basic.h"
-
-#include "ctdb_private.h"
-#include "ctdb_client.h"
-
-#include "common/system.h"
-
-#define CTDB_LOG_FILE_PREFIX "file"
-
-struct file_state {
- int fd;
-};
-
-/*
- log file logging function
- */
-static void ctdb_log_to_file(void *private_ptr, int dbglevel, const char *s)
-{
- struct file_state *state = talloc_get_type(
- private_ptr, struct file_state);
- struct timeval tv;
- struct timeval_buf tvbuf;
- char *s2 = NULL;
- int ret;
-
- GetTimeOfDay(&tv);
- timeval_str_buf(&tv, false, true, &tvbuf);
-
- ret = asprintf(&s2, "%s [%s%5u]: %s\n",
- tvbuf.buf,
- debug_extra, (unsigned)getpid(), s);
- if (ret == -1) {
- const char *errstr = "asprintf failed\n";
- sys_write(state->fd, errstr, strlen(errstr));
- return;
- }
- if (s2) {
- sys_write(state->fd, s2, strlen(s2));
- free(s2);
- }
-}
-
-static int file_state_destructor(struct file_state *state)
-{
- close(state->fd);
- state->fd = -1;
- return 0;
-}
-
-static int ctdb_log_setup_file(TALLOC_CTX *mem_ctx,
- const char *logging,
- const char *app_name)
-{
- struct file_state *state;
- const char *logfile;
- size_t l;
-
- l = strlen(CTDB_LOG_FILE_PREFIX);
- if (logging[l] != ':') {
- return EINVAL;
- }
- logfile = &logging[0] + l + 1;
-
- state = talloc_zero(mem_ctx, struct file_state);
- if (state == NULL) {
- return ENOMEM;
- }
-
- if (logfile == NULL || strcmp(logfile, "-") == 0) {
- int ret;
-
- state->fd = 1;
- /* also catch stderr of subcommands to stdout */
- ret = dup2(1, 2);
- if (ret == -1) {
- return errno;
- }
- } else {
- state->fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0666);
- if (state->fd == -1) {
- return errno;
- }
- }
-
- talloc_set_destructor(state, file_state_destructor);
- debug_set_callback(state, ctdb_log_to_file);
-
- return 0;
-}
-
-void ctdb_log_init_file(void)
-{
- ctdb_log_register_backend(CTDB_LOG_FILE_PREFIX, ctdb_log_setup_file);
-}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_logging_syslog.c samba-4.6.5+dfsg/ctdb/server/ctdb_logging_syslog.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_logging_syslog.c 2016-09-13 08:21:35.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_logging_syslog.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,328 +0,0 @@
-/*
- ctdb logging code - syslog backend
-
- Copyright (C) Andrew Tridgell 2008
- Copyright (C) Martin Schwenke 2014
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see .
-*/
-
-#include "replace.h"
-#include "system/network.h"
-#include "system/syslog.h"
-
-#include "lib/util/debug.h"
-#include "lib/util/blocking.h"
-#include "lib/util/time_basic.h"
-#include "lib/util/samba_util.h" /* get_myname */
-
-#include "ctdb_private.h"
-
-#include "common/logging.h"
-
-/* Linux and FreeBSD define this appropriately - try good old /dev/log
- * for anything that doesn't... */
-#ifndef _PATH_LOG
-#define _PATH_LOG "/dev/log"
-#endif
-
-#define CTDB_LOG_SYSLOG_PREFIX "syslog"
-#define CTDB_SYSLOG_FACILITY LOG_USER
-
-struct ctdb_syslog_sock_state {
- int fd;
- const char *app_name;
- const char *hostname;
- int (*format)(int dbglevel, struct ctdb_syslog_sock_state *state,
- const char *str, char *buf, int bsize);
-};
-
-/**********************************************************************/
-
-static int ctdb_debug_to_syslog_level(int dbglevel)
-{
- int level;
-
- switch (dbglevel) {
- case DEBUG_ERR:
- level = LOG_ERR;
- break;
- case DEBUG_WARNING:
- level = LOG_WARNING;
- break;
- case DEBUG_NOTICE:
- level = LOG_NOTICE;
- break;
- case DEBUG_INFO:
- level = LOG_INFO;
- break;
- default:
- level = LOG_DEBUG;
- break;
- }
-
- return level;
-}
-
-/**********************************************************************/
-
-/* Format messages as per RFC3164. */
-
-/* It appears that some syslog daemon implementations do not allow a
- * hostname when messages are sent via a Unix domain socket, so omit
- * it. Similarly, syslogd on FreeBSD does not understand the hostname
- * part of the header, even when logging via UDP. Note that most
- * implementations will log messages against "localhost" when logging
- * via UDP. A timestamp could be sent but rsyslogd on Linux limits
- * the timestamp logged to the precision that was received on
- * /dev/log. It seems sane to send degenerate RFC3164 messages
- * without a header at all, so that the daemon will generate high
- * resolution timestamps if configured. */
-static int format_rfc3164(int dbglevel, struct ctdb_syslog_sock_state *state,
- const char *str, char *buf, int bsize)
-{
- int pri;
- int len;
-
- pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel);
- len = snprintf(buf, bsize, "<%d>%s[%u]: %s%s",
- pri, state->app_name, getpid(), debug_extra, str);
- len = MIN(len, bsize - 1);
-
- return len;
-}
-
-/* Format messages as per RFC5424
- *
- * <165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1
- * myproc 8710 - - %% It's time to make the do-nuts.
- */
-static int format_rfc5424(int dbglevel, struct ctdb_syslog_sock_state *state,
- const char *str, char *buf, int bsize)
-{
- int pri;
- struct timeval tv;
- struct timeval_buf tvbuf;
- int len, s;
-
- /* Header */
- pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel);
- GetTimeOfDay(&tv);
- len = snprintf(buf, bsize,
- "<%d>1 %s %s %s %u - - ",
- pri, timeval_str_buf(&tv, true, true, &tvbuf),
- state->hostname, state->app_name, getpid());
- /* A truncated header is not useful... */
- if (len >= bsize) {
- return -1;
- }
-
- /* Message */
- s = snprintf(&buf[len], bsize - len, "%s %s", debug_extra, str);
- len = MIN(len + s, bsize - 1);
-
- return len;
-}
-
-/**********************************************************************/
-
-/* Non-blocking logging */
-
-static void ctdb_log_to_syslog_sock(void *private_ptr,
- int dbglevel, const char *str)
-{
- struct ctdb_syslog_sock_state *state = talloc_get_type(
- private_ptr, struct ctdb_syslog_sock_state);
-
- /* RFC3164 says: The total length of the packet MUST be 1024
- bytes or less. */
- char buf[1024];
- int n;
-
- n = state->format(dbglevel, state, str, buf, sizeof(buf));
- if (n == -1) {
- fprintf(stderr, "Failed to format syslog message %s\n", str);
- return;
- }
-
- /* Could extend this to count failures, which probably
- * indicate dropped messages due to EAGAIN or EWOULDBLOCK */
- (void)send(state->fd, buf, n, 0);
-}
-
-static int
-ctdb_syslog_sock_state_destructor(struct ctdb_syslog_sock_state *state)
-{
- if (state->fd != -1) {
- close(state->fd);
- state->fd = -1;
- }
- return 0;
-}
-
-static struct ctdb_syslog_sock_state *
-ctdb_log_setup_syslog_common(TALLOC_CTX *mem_ctx,
- const char *app_name)
-{
- struct ctdb_syslog_sock_state *state;
-
- state = talloc_zero(mem_ctx, struct ctdb_syslog_sock_state);
- if (state == NULL) {
- return NULL;
- }
- state->fd = -1;
- state->app_name = app_name;
- talloc_set_destructor(state, ctdb_syslog_sock_state_destructor);
-
- return state;
-}
-
-static int ctdb_log_setup_syslog_un(TALLOC_CTX *mem_ctx,
- const char *app_name)
-{
- struct ctdb_syslog_sock_state *state;
- struct sockaddr_un dest;
- int ret;
-
- state = ctdb_log_setup_syslog_common(mem_ctx, app_name);
- if (state == NULL) {
- return ENOMEM;
- }
-
- state->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
- if (state->fd == -1) {
- int save_errno = errno;
- talloc_free(state);
- return save_errno;
- }
-
- dest.sun_family = AF_UNIX;
- strncpy(dest.sun_path, _PATH_LOG, sizeof(dest.sun_path)-1);
- ret = connect(state->fd,
- (struct sockaddr *)&dest, sizeof(dest));
- if (ret == -1) {
- int save_errno = errno;
- talloc_free(state);
- return save_errno;
- }
-
- ret = set_blocking(state->fd, false);
- if (ret != 0) {
- int save_errno = errno;
- talloc_free(state);
- return save_errno;
- }
-
- state->hostname = NULL; /* Make this explicit */
- state->format = format_rfc3164;
-
- debug_set_callback(state, ctdb_log_to_syslog_sock);
-
- return 0;
-}
-
-static int ctdb_log_setup_syslog_udp(TALLOC_CTX *mem_ctx,
- const char *app_name,
- bool rfc5424)
-{
- struct ctdb_syslog_sock_state *state;
- struct sockaddr_in dest;
- int ret;
-
- state = ctdb_log_setup_syslog_common(mem_ctx, app_name);
- if (state == NULL) {
- return ENOMEM;
- }
-
- state->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (state->fd == -1) {
- int save_errno = errno;
- talloc_free(state);
- return save_errno;
- }
-
- dest.sin_family = AF_INET;
- dest.sin_port = htons(514);
- dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- ret = connect(state->fd,
- (struct sockaddr *)&dest, sizeof(dest));
- if (ret == -1) {
- int save_errno = errno;
- talloc_free(state);
- return save_errno;
- }
-
- state->hostname = get_myname(state);
- if (state->hostname == NULL) {
- /* Use a fallback instead of failing initialisation */
- state->hostname = "localhost";
- }
- if (rfc5424) {
- state->format = format_rfc5424;
- } else {
- state->format = format_rfc3164;
- }
-
- debug_set_callback(state, ctdb_log_to_syslog_sock);
-
- return 0;
-}
-
-/**********************************************************************/
-
-static void ctdb_log_to_syslog(void *private_ptr, int dbglevel, const char *s)
-{
- syslog(ctdb_debug_to_syslog_level(dbglevel),
- "%s%s", debug_extra, s);
-}
-
-static int ctdb_log_setup_syslog(TALLOC_CTX *mem_ctx,
- const char *logging,
- const char *app_name)
-{
- size_t l = strlen(CTDB_LOG_SYSLOG_PREFIX);
-
- if (logging[l] != '\0') {
- /* Handle non-blocking extensions here */
- const char *method;
-
- if (logging[l] != ':') {
- return EINVAL;
- }
- method = &logging[0] + l + 1;
- if (strcmp(method, "nonblocking") == 0) {
- ctdb_log_setup_syslog_un(mem_ctx, app_name);
- return 0;
- }
- if (strcmp(method, "udp") == 0) {
- ctdb_log_setup_syslog_udp(mem_ctx, app_name, false);
- return 0;
- }
- if (strcmp(method, "udp-rfc5424") == 0) {
- ctdb_log_setup_syslog_udp(mem_ctx, app_name, true);
- return 0;
- }
-
- return EINVAL;
- }
-
- debug_set_callback(NULL, ctdb_log_to_syslog);
- return 0;
-}
-
-void ctdb_log_init_syslog(void)
-{
- ctdb_log_register_backend(CTDB_LOG_SYSLOG_PREFIX,
- ctdb_log_setup_syslog);
-}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_ltdb_server.c samba-4.6.5+dfsg/ctdb/server/ctdb_ltdb_server.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_ltdb_server.c 2016-09-13 08:21:35.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_ltdb_server.c 2017-01-11 07:55:14.000000000 +0000
@@ -57,7 +57,8 @@
TDB_DATA data)
{
struct ctdb_context *ctdb = ctdb_db->ctdb;
- TDB_DATA rec;
+ TDB_DATA rec[2];
+ uint32_t hsize = sizeof(struct ctdb_ltdb_header);
int ret;
bool seqnum_suppressed = false;
bool keep = false;
@@ -66,14 +67,21 @@
uint32_t lmaster;
if (ctdb->flags & CTDB_FLAG_TORTURE) {
+ TDB_DATA old;
struct ctdb_ltdb_header *h2;
- rec = tdb_fetch(ctdb_db->ltdb->tdb, key);
- h2 = (struct ctdb_ltdb_header *)rec.dptr;
- if (rec.dptr && rec.dsize >= sizeof(h2) && h2->rsn > header->rsn) {
- DEBUG(DEBUG_CRIT,("RSN regression! %llu %llu\n",
- (unsigned long long)h2->rsn, (unsigned long long)header->rsn));
+
+ old = tdb_fetch(ctdb_db->ltdb->tdb, key);
+ h2 = (struct ctdb_ltdb_header *)old.dptr;
+ if (old.dptr != NULL &&
+ old.dsize >= hsize &&
+ h2->rsn > header->rsn) {
+ DEBUG(DEBUG_ERR,
+ ("RSN regression! %"PRIu64" %"PRIu64"\n",
+ h2->rsn, header->rsn));
+ }
+ if (old.dptr) {
+ free(old.dptr);
}
- if (rec.dptr) free(rec.dptr);
}
if (ctdb->vnn_map == NULL) {
@@ -178,12 +186,11 @@
*/
header->flags &= ~CTDB_REC_FLAG_AUTOMATIC;
- rec.dsize = sizeof(*header) + data.dsize;
- rec.dptr = talloc_size(ctdb, rec.dsize);
- CTDB_NO_MEMORY(ctdb, rec.dptr);
+ rec[0].dsize = hsize;
+ rec[0].dptr = (uint8_t *)header;
- memcpy(rec.dptr, header, sizeof(*header));
- memcpy(rec.dptr + sizeof(*header), data.dptr, data.dsize);
+ rec[1].dsize = data.dsize;
+ rec[1].dptr = data.dptr;
/* Databases with seqnum updates enabled only get their seqnum
changes when/if we modify the data */
@@ -191,14 +198,14 @@
TDB_DATA old;
old = tdb_fetch(ctdb_db->ltdb->tdb, key);
- if ( (old.dsize == rec.dsize)
- && !memcmp(old.dptr+sizeof(struct ctdb_ltdb_header),
- rec.dptr+sizeof(struct ctdb_ltdb_header),
- rec.dsize-sizeof(struct ctdb_ltdb_header)) ) {
+ if ((old.dsize == hsize + data.dsize) &&
+ memcmp(old.dptr + hsize, data.dptr, data.dsize) == 0) {
tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM);
seqnum_suppressed = true;
}
- if (old.dptr) free(old.dptr);
+ if (old.dptr != NULL) {
+ free(old.dptr);
+ }
}
DEBUG(DEBUG_DEBUG, (__location__ " db[%s]: %s record: hash[0x%08x]\n",
@@ -207,7 +214,7 @@
ctdb_hash(&key)));
if (keep) {
- ret = tdb_store(ctdb_db->ltdb->tdb, key, rec, TDB_REPLACE);
+ ret = tdb_storev(ctdb_db->ltdb->tdb, key, rec, 2, TDB_REPLACE);
} else {
ret = tdb_delete(ctdb_db->ltdb->tdb, key);
}
@@ -234,8 +241,6 @@
tdb_add_flags(ctdb_db->ltdb->tdb, TDB_SEQNUM);
}
- talloc_free(rec.dptr);
-
if (schedule_for_deletion) {
int ret2;
ret2 = ctdb_local_schedule_for_deletion(ctdb_db, header, key);
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_monitor.c samba-4.6.5+dfsg/ctdb/server/ctdb_monitor.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_monitor.c 2016-09-13 08:21:35.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_monitor.c 2017-01-11 07:55:14.000000000 +0000
@@ -106,7 +106,6 @@
int ret;
prctl_set_comment("ctdb_notification");
- debug_extra = talloc_asprintf(NULL, "notification-%s:", event);
ret = ctdb_run_notification_script_child(ctdb, event);
if (ret != 0) {
DEBUG(DEBUG_ERR,(__location__ " Notification script failed\n"));
@@ -529,11 +528,6 @@
ctdb_daemon_send_message(ctdb, ctdb->pnn,
CTDB_SRVID_SET_NODE_FLAGS, indata);
- /* if we have become banned, we should go into recovery mode */
- if ((node->flags & NODE_FLAGS_BANNED) && !(c->old_flags & NODE_FLAGS_BANNED) && (node->pnn == ctdb->pnn)) {
- ctdb_local_node_got_banned(ctdb);
- }
-
return 0;
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_mutex_fcntl_helper.c 2017-01-11 07:55:14.000000000 +0000
@@ -21,6 +21,8 @@
#include "system/filesys.h"
#include "system/network.h"
+#include "lib/util/sys_rw.h"
+
/* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */
#include "protocol/protocol.h"
#include "common/system.h"
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_recoverd.c samba-4.6.5+dfsg/ctdb/server/ctdb_recoverd.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_recoverd.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_recoverd.c 2017-01-11 07:55:14.000000000 +0000
@@ -32,13 +32,13 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
#include "ctdb_client.h"
#include "common/system.h"
-#include "common/cmdline.h"
#include "common/common.h"
#include "common/logging.h"
@@ -385,21 +385,13 @@
return 0;
}
-static void set_recmode_fail_callback(struct ctdb_context *ctdb, uint32_t node_pnn, int32_t res, TDB_DATA outdata, void *callback_data)
-{
- struct ctdb_recoverd *rec = talloc_get_type(callback_data, struct ctdb_recoverd);
-
- DEBUG(DEBUG_ERR,("Failed to freeze node %u during recovery. Set it as ban culprit for %d credits\n", node_pnn, rec->nodemap->num));
- ctdb_set_culprit_count(rec, node_pnn, rec->nodemap->num);
-}
-
/*
change recovery mode on all nodes
*/
static int set_recovery_mode(struct ctdb_context *ctdb,
struct ctdb_recoverd *rec,
struct ctdb_node_map_old *nodemap,
- uint32_t rec_mode, bool freeze)
+ uint32_t rec_mode)
{
TDB_DATA data;
uint32_t *nodes;
@@ -424,25 +416,6 @@
return -1;
}
- /* freeze all nodes */
- if (freeze && rec_mode == CTDB_RECOVERY_ACTIVE) {
- int i;
-
- for (i=1; i<=NUM_DB_PRIORITIES; i++) {
- if (ctdb_client_async_control(ctdb, CTDB_CONTROL_FREEZE,
- nodes, i,
- CONTROL_TIMEOUT(),
- false, tdb_null,
- NULL,
- set_recmode_fail_callback,
- rec) != 0) {
- DEBUG(DEBUG_ERR, (__location__ " Unable to freeze nodes. Recovery failed.\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- }
- }
-
talloc_free(tmp_ctx);
return 0;
}
@@ -1022,6 +995,153 @@
}
}
+struct helper_state {
+ int fd[2];
+ pid_t pid;
+ int result;
+ bool done;
+};
+
+static void helper_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct helper_state *state = talloc_get_type_abort(
+ private_data, struct helper_state);
+ int ret;
+
+ ret = sys_read(state->fd[0], &state->result, sizeof(state->result));
+ if (ret != sizeof(state->result)) {
+ state->result = EPIPE;
+ }
+
+ state->done = true;
+}
+
+static int helper_run(struct ctdb_recoverd *rec, TALLOC_CTX *mem_ctx,
+ const char *prog, const char *arg, const char *type)
+{
+ struct helper_state *state;
+ struct tevent_fd *fde;
+ const char **args;
+ int nargs, ret;
+
+ state = talloc_zero(mem_ctx, struct helper_state);
+ if (state == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
+ return -1;
+ }
+
+ state->pid = -1;
+
+ ret = pipe(state->fd);
+ if (ret != 0) {
+ DEBUG(DEBUG_ERR,
+ ("Failed to create pipe for %s helper\n", type));
+ goto fail;
+ }
+
+ set_close_on_exec(state->fd[0]);
+
+ nargs = 4;
+ args = talloc_array(state, const char *, nargs);
+ if (args == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
+ goto fail;
+ }
+
+ args[0] = talloc_asprintf(args, "%d", state->fd[1]);
+ if (args[0] == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
+ goto fail;
+ }
+ args[1] = rec->ctdb->daemon.name;
+ args[2] = arg;
+ args[3] = NULL;
+
+ if (args[2] == NULL) {
+ nargs = 3;
+ }
+
+ state->pid = ctdb_vfork_exec(state, rec->ctdb, prog, nargs, args);
+ if (state->pid == -1) {
+ DEBUG(DEBUG_ERR,
+ ("Failed to create child for %s helper\n", type));
+ goto fail;
+ }
+
+ close(state->fd[1]);
+ state->fd[1] = -1;
+
+ state->done = false;
+
+ fde = tevent_add_fd(rec->ctdb->ev, rec->ctdb, state->fd[0],
+ TEVENT_FD_READ, helper_handler, state);
+ if (fde == NULL) {
+ goto fail;
+ }
+ tevent_fd_set_auto_close(fde);
+
+ while (!state->done) {
+ tevent_loop_once(rec->ctdb->ev);
+ }
+
+ close(state->fd[0]);
+ state->fd[0] = -1;
+
+ if (state->result != 0) {
+ goto fail;
+ }
+
+ ctdb_kill(rec->ctdb, state->pid, SIGKILL);
+ talloc_free(state);
+ return 0;
+
+fail:
+ if (state->fd[0] != -1) {
+ close(state->fd[0]);
+ }
+ if (state->fd[1] != -1) {
+ close(state->fd[1]);
+ }
+ if (state->pid != -1) {
+ ctdb_kill(rec->ctdb, state->pid, SIGKILL);
+ }
+ talloc_free(state);
+ return -1;
+}
+
+
+static int ctdb_takeover(struct ctdb_recoverd *rec,
+ uint32_t *force_rebalance_nodes)
+{
+ static char prog[PATH_MAX+1] = "";
+ char *arg;
+ int i;
+
+ if (!ctdb_set_helper("takeover_helper", prog, sizeof(prog),
+ "CTDB_TAKEOVER_HELPER", CTDB_HELPER_BINDIR,
+ "ctdb_takeover_helper")) {
+ ctdb_die(rec->ctdb, "Unable to set takeover helper\n");
+ }
+
+ arg = NULL;
+ for (i = 0; i < talloc_array_length(force_rebalance_nodes); i++) {
+ uint32_t pnn = force_rebalance_nodes[i];
+ if (arg == NULL) {
+ arg = talloc_asprintf(rec, "%u", pnn);
+ } else {
+ arg = talloc_asprintf_append(arg, ",%u", pnn);
+ }
+ if (arg == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
+ return -1;
+ }
+ }
+
+ return helper_run(rec, rec, prog, arg, "takeover");
+}
+
static bool do_takeover_run(struct ctdb_recoverd *rec,
struct ctdb_node_map_old *nodemap)
{
@@ -1075,8 +1195,7 @@
}
}
- ret = ctdb_takeover_run(rec->ctdb, nodemap,
- rec->force_rebalance_nodes);
+ ret = ctdb_takeover(rec, rec->force_rebalance_nodes);
/* Reenable takeover runs and IP checks on other nodes */
dtr.timeout = 0;
@@ -1111,37 +1230,10 @@
return ok;
}
-struct recovery_helper_state {
- int fd[2];
- pid_t pid;
- int result;
- bool done;
-};
-
-static void ctdb_recovery_handler(struct tevent_context *ev,
- struct tevent_fd *fde,
- uint16_t flags, void *private_data)
-{
- struct recovery_helper_state *state = talloc_get_type_abort(
- private_data, struct recovery_helper_state);
- int ret;
-
- ret = sys_read(state->fd[0], &state->result, sizeof(state->result));
- if (ret != sizeof(state->result)) {
- state->result = EPIPE;
- }
-
- state->done = true;
-}
-
-
static int db_recovery_parallel(struct ctdb_recoverd *rec, TALLOC_CTX *mem_ctx)
{
static char prog[PATH_MAX+1] = "";
- const char **args;
- struct recovery_helper_state *state;
- struct tevent_fd *fde;
- int nargs, ret;
+ const char *arg;
if (!ctdb_set_helper("recovery_helper", prog, sizeof(prog),
"CTDB_RECOVERY_HELPER", CTDB_HELPER_BINDIR,
@@ -1149,88 +1241,15 @@
ctdb_die(rec->ctdb, "Unable to set recovery helper\n");
}
- state = talloc_zero(mem_ctx, struct recovery_helper_state);
- if (state == NULL) {
+ arg = talloc_asprintf(mem_ctx, "%u", new_generation());
+ if (arg == NULL) {
DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
return -1;
}
- state->pid = -1;
-
- ret = pipe(state->fd);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Failed to create pipe for recovery helper\n"));
- goto fail;
- }
-
- set_close_on_exec(state->fd[0]);
-
- nargs = 4;
- args = talloc_array(state, const char *, nargs);
- if (args == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
- goto fail;
- }
-
- args[0] = talloc_asprintf(args, "%d", state->fd[1]);
- args[1] = rec->ctdb->daemon.name;
- args[2] = talloc_asprintf(args, "%u", new_generation());
- args[3] = NULL;
-
- if (args[0] == NULL || args[2] == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " memory error\n"));
- goto fail;
- }
-
setenv("CTDB_DBDIR_STATE", rec->ctdb->db_directory_state, 1);
- if (!ctdb_vfork_with_logging(state, rec->ctdb, "recovery", prog, nargs,
- args, NULL, NULL, &state->pid)) {
- DEBUG(DEBUG_ERR,
- ("Failed to create child for recovery helper\n"));
- goto fail;
- }
-
- close(state->fd[1]);
- state->fd[1] = -1;
-
- state->done = false;
-
- fde = tevent_add_fd(rec->ctdb->ev, rec->ctdb, state->fd[0],
- TEVENT_FD_READ, ctdb_recovery_handler, state);
- if (fde == NULL) {
- goto fail;
- }
- tevent_fd_set_auto_close(fde);
-
- while (!state->done) {
- tevent_loop_once(rec->ctdb->ev);
- }
-
- close(state->fd[0]);
- state->fd[0] = -1;
-
- if (state->result != 0) {
- goto fail;
- }
-
- ctdb_kill(rec->ctdb, state->pid, SIGKILL);
- talloc_free(state);
- return 0;
-
-fail:
- if (state->fd[0] != -1) {
- close(state->fd[0]);
- }
- if (state->fd[1] != -1) {
- close(state->fd[1]);
- }
- if (state->pid != -1) {
- ctdb_kill(rec->ctdb, state->pid, SIGKILL);
- }
- talloc_free(state);
- return -1;
+ return helper_run(rec, mem_ctx, prog, arg, "recovery");
}
/*
@@ -1901,7 +1920,7 @@
DEBUG(DEBUG_INFO,(__location__ " Force an election\n"));
/* set all nodes to recovery mode to stop all internode traffic */
- ret = set_recovery_mode(ctdb, rec, nodemap, CTDB_RECOVERY_ACTIVE, false);
+ ret = set_recovery_mode(ctdb, rec, nodemap, CTDB_RECOVERY_ACTIVE);
if (ret != 0) {
DEBUG(DEBUG_ERR, (__location__ " Unable to set recovery mode to active on cluster\n"));
return;
@@ -2360,16 +2379,9 @@
} else {
if (ctdb_sys_have_ip(&ips->ips[j].addr)) {
DEBUG(DEBUG_ERR,
- ("IP %s incorrectly on an interface - releasing\n",
+ ("IP %s incorrectly on an interface\n",
ctdb_addr_to_str(&ips->ips[j].addr)));
- ret = ctdb_ctrl_release_ip(ctdb,
- CONTROL_TIMEOUT(),
- CTDB_CURRENT_NODE,
- &ips->ips[j]);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Failed to release IP address\n"));
- }
+ need_takeover_run = true;
}
}
}
@@ -2970,6 +2982,7 @@
struct ctdb_recoverd *rec = talloc_get_type_abort(
private_data, struct ctdb_recoverd);
+ DEBUG(DEBUG_ERR, ("Received SIGTERM, exiting\n"));
ctdb_recovery_unlock(rec);
exit(0);
}
@@ -3144,6 +3157,7 @@
int fd[2];
struct tevent_signal *se;
struct tevent_fd *fde;
+ int ret;
if (pipe(fd) != 0) {
return -1;
@@ -3170,8 +3184,13 @@
srandom(getpid() ^ time(NULL));
+ ret = logging_init(ctdb, NULL, NULL, "ctdb-recoverd");
+ if (ret != 0) {
+ return -1;
+ }
+
prctl_set_comment("ctdb_recovered");
- if (switch_from_server_to_client(ctdb, "recoverd") != 0) {
+ if (switch_from_server_to_client(ctdb) != 0) {
DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch recovery daemon into client mode. shutting down.\n"));
exit(1);
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_recovery_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_recovery_helper.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_recovery_helper.c 2016-12-05 08:18:44.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_recovery_helper.c 2017-01-11 07:55:14.000000000 +0000
@@ -27,6 +27,7 @@
#include
#include "lib/tdb_wrap/tdb_wrap.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/time.h"
#include "lib/util/tevent_unix.h"
@@ -34,41 +35,20 @@
#include "protocol/protocol_api.h"
#include "client/client.h"
+#include "common/logging.h"
+
static int recover_timeout = 30;
#define NUM_RETRIES 3
#define TIMEOUT() timeval_current_ofs(recover_timeout, 0)
-static void LOG(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
-
-static void LOG(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
+#define LOG(...) DEBUG(DEBUG_NOTICE, (__VA_ARGS__))
/*
* Utility functions
*/
-static ssize_t sys_write(int fd, const void *buf, size_t count)
-{
- ssize_t ret;
-
- do {
- ret = write(fd, buf, count);
-#if defined(EWOULDBLOCK)
- } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-#else
- } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
-#endif
- return ret;
-}
-
static bool generic_recv(struct tevent_req *req, int *perr)
{
int err;
@@ -1705,7 +1685,7 @@
return;
}
- if (state->persistent && state->tun_list->recover_pdb_by_seqnum != 0) {
+ if (state->persistent) {
subreq = collect_highseqnum_db_send(
state, state->ev, state->client,
state->pnn_list, state->count, state->caps,
@@ -1734,7 +1714,7 @@
int ret;
bool status;
- if (state->persistent && state->tun_list->recover_pdb_by_seqnum != 0) {
+ if (state->persistent) {
status = collect_highseqnum_db_recv(subreq, &ret);
} else {
status = collect_all_db_recv(subreq, &ret);
@@ -2733,7 +2713,7 @@
static void usage(const char *progname)
{
- fprintf(stderr, "\nUsage: %s \n",
+ fprintf(stderr, "\nUsage: %s \n",
progname);
}
@@ -2743,7 +2723,7 @@
*/
int main(int argc, char *argv[])
{
- int log_fd, write_fd;
+ int write_fd;
const char *sockpath;
TALLOC_CTX *mem_ctx;
struct tevent_context *ev;
@@ -2752,27 +2732,24 @@
struct tevent_req *req;
uint32_t generation;
- if (argc != 5) {
+ if (argc != 4) {
usage(argv[0]);
exit(1);
}
- log_fd = atoi(argv[1]);
- if (log_fd != STDOUT_FILENO && log_fd != STDERR_FILENO) {
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- dup2(log_fd, STDOUT_FILENO);
- dup2(log_fd, STDERR_FILENO);
- }
- close(log_fd);
-
- write_fd = atoi(argv[2]);
- sockpath = argv[3];
- generation = (uint32_t)strtoul(argv[4], NULL, 0);
+ write_fd = atoi(argv[1]);
+ sockpath = argv[2];
+ generation = (uint32_t)strtoul(argv[3], NULL, 0);
mem_ctx = talloc_new(NULL);
if (mem_ctx == NULL) {
- LOG("talloc_new() failed\n");
+ fprintf(stderr, "recovery: talloc_new() failed\n");
+ goto failed;
+ }
+
+ ret = logging_init(mem_ctx, NULL, NULL, "ctdb-recovery");
+ if (ret != 0) {
+ fprintf(stderr, "recovery: Unable to initialize logging\n");
goto failed;
}
@@ -2810,6 +2787,6 @@
return 0;
failed:
- talloc_free(mem_ctx);
+ TALLOC_FREE(mem_ctx);
return 1;
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_takeover.c samba-4.6.5+dfsg/ctdb/server/ctdb_takeover.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_takeover.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_takeover.c 2017-01-11 07:55:14.000000000 +0000
@@ -30,6 +30,7 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
@@ -55,12 +56,17 @@
uint32_t references;
};
+struct vnn_interface {
+ struct vnn_interface *prev, *next;
+ struct ctdb_interface *iface;
+};
+
/* state associated with a public ip address */
struct ctdb_vnn {
struct ctdb_vnn *prev, *next;
struct ctdb_interface *iface;
- const char **ifaces;
+ struct vnn_interface *ifaces;
ctdb_sock_addr public_address;
uint8_t public_netmask_bits;
@@ -88,51 +94,62 @@
bool delete_pending;
};
-static const char *ctdb_vnn_iface_string(const struct ctdb_vnn *vnn)
+static const char *iface_string(const struct ctdb_interface *iface)
{
- if (vnn->iface) {
- return vnn->iface->name;
- }
+ return (iface != NULL ? iface->name : "__none__");
+}
- return "__none__";
+static const char *ctdb_vnn_iface_string(const struct ctdb_vnn *vnn)
+{
+ return iface_string(vnn->iface);
}
-static int ctdb_add_local_iface(struct ctdb_context *ctdb, const char *iface)
+static struct ctdb_interface *ctdb_find_iface(struct ctdb_context *ctdb,
+ const char *iface);
+
+static struct ctdb_interface *
+ctdb_add_local_iface(struct ctdb_context *ctdb, const char *iface)
{
struct ctdb_interface *i;
if (strlen(iface) > CTDB_IFACE_SIZE) {
DEBUG(DEBUG_ERR, ("Interface name too long \"%s\"\n", iface));
- return -1;
+ return NULL;
}
/* Verify that we don't have an entry for this ip yet */
- for (i=ctdb->ifaces;i;i=i->next) {
- if (strcmp(i->name, iface) == 0) {
- return 0;
- }
+ i = ctdb_find_iface(ctdb, iface);
+ if (i != NULL) {
+ return i;
}
/* create a new structure for this interface */
i = talloc_zero(ctdb, struct ctdb_interface);
- CTDB_NO_MEMORY_FATAL(ctdb, i);
+ if (i == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+ return NULL;
+ }
i->name = talloc_strdup(i, iface);
- CTDB_NO_MEMORY(ctdb, i->name);
+ if (i->name == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+ talloc_free(i);
+ return NULL;
+ }
i->link_up = true;
DLIST_ADD(ctdb->ifaces, i);
- return 0;
+ return i;
}
-static bool vnn_has_interface_with_name(struct ctdb_vnn *vnn,
- const char *name)
+static bool vnn_has_interface(struct ctdb_vnn *vnn,
+ const struct ctdb_interface *iface)
{
- int n;
+ struct vnn_interface *i;
- for (n = 0; vnn->ifaces[n] != NULL; n++) {
- if (strcmp(name, vnn->ifaces[n]) == 0) {
+ for (i = vnn->ifaces; i != NULL; i = i->next) {
+ if (iface == i->iface) {
return true;
}
}
@@ -163,14 +180,14 @@
next = i->next;
/* Only consider interfaces named in the given VNN. */
- if (!vnn_has_interface_with_name(vnn, i->name)) {
+ if (!vnn_has_interface(vnn, i)) {
continue;
}
/* Search for a vnn with this interface. */
found = false;
for (tv=ctdb->vnn; tv; tv=tv->next) {
- if (vnn_has_interface_with_name(tv, i->name)) {
+ if (vnn_has_interface(tv, i)) {
found = true;
break;
}
@@ -202,16 +219,13 @@
static struct ctdb_interface *ctdb_vnn_best_iface(struct ctdb_context *ctdb,
struct ctdb_vnn *vnn)
{
- int i;
+ struct vnn_interface *i;
struct ctdb_interface *cur = NULL;
struct ctdb_interface *best = NULL;
- for (i=0; vnn->ifaces[i]; i++) {
+ for (i = vnn->ifaces; i != NULL; i = i->next) {
- cur = ctdb_find_iface(ctdb, vnn->ifaces[i]);
- if (cur == NULL) {
- continue;
- }
+ cur = i->iface;
if (!cur->link_up) {
continue;
@@ -284,7 +298,7 @@
static bool ctdb_vnn_available(struct ctdb_context *ctdb,
struct ctdb_vnn *vnn)
{
- int i;
+ struct vnn_interface *i;
/* Nodes that are not RUNNING can not host IPs */
if (ctdb->runstate != CTDB_RUNSTATE_RUNNING) {
@@ -299,15 +313,8 @@
return true;
}
- for (i=0; vnn->ifaces[i]; i++) {
- struct ctdb_interface *cur;
-
- cur = ctdb_find_iface(ctdb, vnn->ifaces[i]);
- if (cur == NULL) {
- continue;
- }
-
- if (cur->link_up) {
+ for (i = vnn->ifaces; i != NULL; i = i->next) {
+ if (i->iface->link_up) {
return true;
}
}
@@ -575,10 +582,11 @@
if (status == -ETIME) {
ctdb_ban_self(ctdb);
}
- DEBUG(DEBUG_ERR,(__location__ " Failed to move IP %s from interface %s to %s\n",
- ctdb_addr_to_str(&state->vnn->public_address),
- state->old->name,
- ctdb_vnn_iface_string(state->vnn)));
+ DEBUG(DEBUG_ERR,
+ ("Failed update of IP %s from interface %s to %s\n",
+ ctdb_addr_to_str(&state->vnn->public_address),
+ iface_string(state->old),
+ ctdb_vnn_iface_string(state->vnn)));
/*
* All we can do is reset the old interface
@@ -626,6 +634,7 @@
int ret;
struct ctdb_do_updateip_state *state;
struct ctdb_interface *old = vnn->iface;
+ const char *old_name = iface_string(old);
const char *new_name;
if (vnn->update_in_flight) {
@@ -639,16 +648,15 @@
ctdb_vnn_unassign_iface(ctdb, vnn);
ret = ctdb_vnn_assign_iface(ctdb, vnn);
if (ret != 0) {
- DEBUG(DEBUG_ERR,("update of IP %s/%u failed to "
- "assin a usable interface (old iface '%s')\n",
+ DEBUG(DEBUG_ERR,("Update of IP %s/%u failed to "
+ "assign a usable interface (old iface '%s')\n",
ctdb_addr_to_str(&vnn->public_address),
vnn->public_netmask_bits,
- old->name));
+ old_name));
return -1;
}
- new_name = ctdb_vnn_iface_string(vnn);
- if (old->name != NULL && new_name != NULL && !strcmp(old->name, new_name)) {
+ if (old == vnn->iface) {
/* A benign update from one interface onto itself.
* no need to run the eventscripts in this case, just return
* success.
@@ -667,11 +675,12 @@
vnn->update_in_flight = true;
talloc_set_destructor(state, ctdb_updateip_destructor);
+ new_name = ctdb_vnn_iface_string(vnn);
DEBUG(DEBUG_NOTICE,("Update of IP %s/%u from "
"interface %s to %s\n",
ctdb_addr_to_str(&vnn->public_address),
vnn->public_netmask_bits,
- old->name,
+ old_name,
new_name));
ret = ctdb_event_script_callback(ctdb,
@@ -680,14 +689,15 @@
state,
CTDB_EVENT_UPDATE_IP,
"%s %s %s %u",
- state->old->name,
+ old_name,
new_name,
ctdb_addr_to_str(&vnn->public_address),
vnn->public_netmask_bits);
if (ret != 0) {
- DEBUG(DEBUG_ERR,(__location__ " Failed update IP %s from interface %s to %s\n",
- ctdb_addr_to_str(&vnn->public_address),
- old->name, new_name));
+ DEBUG(DEBUG_ERR,
+ ("Failed update IP %s from interface %s to %s\n",
+ ctdb_addr_to_str(&vnn->public_address),
+ old_name, new_name));
talloc_free(state);
return -1;
}
@@ -759,19 +769,6 @@
return -1;
}
- if (vnn->iface == NULL && vnn->pnn == -1 && have_ip && best_iface != NULL) {
- DEBUG(DEBUG_ERR,("Taking over newly created ip\n"));
- have_ip = false;
- }
-
-
- if (vnn->iface == NULL && have_ip) {
- DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, "
- "but we have no interface assigned, has someone manually configured it? Ignore for now.\n",
- ctdb_addr_to_str(&vnn->public_address)));
- return 0;
- }
-
if (vnn->pnn != ctdb->pnn && have_ip && vnn->pnn != -1) {
DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, "
"and we have it on iface[%s], but it was assigned to node %d"
@@ -783,12 +780,16 @@
}
if (vnn->pnn == -1 && have_ip) {
- vnn->pnn = ctdb->pnn;
- DEBUG(DEBUG_CRIT,(__location__ " takeoverip of IP %s is known to the kernel, "
- "and we already have it on iface[%s], update local daemon\n",
- ctdb_addr_to_str(&vnn->public_address),
- ctdb_vnn_iface_string(vnn)));
- return 0;
+ /* This will cause connections to be reset and
+ * reestablished. However, this is a very unusual
+ * situation and doing this will completely repair the
+ * inconsistency in the VNN.
+ */
+ DEBUG(DEBUG_WARNING,
+ (__location__
+ " Doing updateip for IP %s already on an interface\n",
+ ctdb_addr_to_str(&vnn->public_address)));
+ do_updateip = true;
}
if (vnn->iface) {
@@ -932,7 +933,7 @@
struct release_ip_callback_state *state;
struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
struct ctdb_vnn *vnn;
- char *iface;
+ const char *iface;
/* update our vnn list */
vnn = find_public_ip_vnn(ctdb, &pip->addr);
@@ -989,7 +990,7 @@
return -1;
}
- iface = strdup(ctdb_vnn_iface_string(vnn));
+ iface = ctdb_vnn_iface_string(vnn);
DEBUG(DEBUG_NOTICE,("Release of IP %s/%u on interface %s node:%d\n",
ctdb_addr_to_str(&pip->addr),
@@ -1001,7 +1002,6 @@
if (state == NULL) {
ctdb_set_error(ctdb, "Out of memory at %s:%d",
__FILE__, __LINE__);
- free(iface);
return -1;
}
@@ -1010,7 +1010,6 @@
if (state->addr == NULL) {
ctdb_set_error(ctdb, "Out of memory at %s:%d",
__FILE__, __LINE__);
- free(iface);
talloc_free(state);
return -1;
}
@@ -1028,7 +1027,6 @@
iface,
ctdb_addr_to_str(&pip->addr),
vnn->public_netmask_bits);
- free(iface);
if (ret != 0) {
DEBUG(DEBUG_ERR,(__location__ " Failed to release IP %s on interface %s\n",
ctdb_addr_to_str(&pip->addr),
@@ -1049,66 +1047,66 @@
bool check_address)
{
struct ctdb_vnn *vnn;
- uint32_t num = 0;
char *tmp;
const char *iface;
- int i;
- int ret;
- tmp = strdup(ifaces);
- for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) {
- if (!ctdb_sys_check_iface_exists(iface)) {
- DEBUG(DEBUG_CRIT,("Interface %s does not exist. Can not add public-address : %s\n", iface, ctdb_addr_to_str(addr)));
- free(tmp);
- return -1;
- }
- }
- free(tmp);
-
- /* Verify that we don't have an entry for this ip yet */
- for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
+ /* Verify that we don't have an entry for this IP yet */
+ for (vnn = ctdb->vnn; vnn != NULL; vnn = vnn->next) {
if (ctdb_same_sockaddr(addr, &vnn->public_address)) {
- DEBUG(DEBUG_CRIT,("Same ip '%s' specified multiple times in the public address list \n",
- ctdb_addr_to_str(addr)));
+ DEBUG(DEBUG_ERR,
+ ("Duplicate public IP address '%s'\n",
+ ctdb_addr_to_str(addr)));
return -1;
- }
+ }
}
- /* create a new vnn structure for this ip address */
+ /* Create a new VNN structure for this IP address */
vnn = talloc_zero(ctdb, struct ctdb_vnn);
- CTDB_NO_MEMORY_FATAL(ctdb, vnn);
- vnn->ifaces = talloc_array(vnn, const char *, num + 2);
+ if (vnn == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+ return -1;
+ }
tmp = talloc_strdup(vnn, ifaces);
- CTDB_NO_MEMORY_FATAL(ctdb, tmp);
- for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) {
- vnn->ifaces = talloc_realloc(vnn, vnn->ifaces, const char *, num + 2);
- CTDB_NO_MEMORY_FATAL(ctdb, vnn->ifaces);
- vnn->ifaces[num] = talloc_strdup(vnn, iface);
- CTDB_NO_MEMORY_FATAL(ctdb, vnn->ifaces[num]);
- num++;
+ if (tmp == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+ talloc_free(vnn);
+ return -1;
}
- talloc_free(tmp);
- vnn->ifaces[num] = NULL;
- vnn->public_address = *addr;
- vnn->public_netmask_bits = mask;
- vnn->pnn = -1;
- if (check_address) {
- if (ctdb_sys_have_ip(addr)) {
- DEBUG(DEBUG_ERR,("We are already hosting public address '%s'. setting PNN to ourself:%d\n", ctdb_addr_to_str(addr), ctdb->pnn));
- vnn->pnn = ctdb->pnn;
+ for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) {
+ struct vnn_interface *vnn_iface;
+ struct ctdb_interface *i;
+ if (!ctdb_sys_check_iface_exists(iface)) {
+ DEBUG(DEBUG_ERR,
+ ("Unknown interface %s for public address %s\n",
+ iface, ctdb_addr_to_str(addr)));
+ talloc_free(vnn);
+ return -1;
}
- }
- for (i=0; vnn->ifaces[i]; i++) {
- ret = ctdb_add_local_iface(ctdb, vnn->ifaces[i]);
- if (ret != 0) {
- DEBUG(DEBUG_CRIT, (__location__ " failed to add iface[%s] "
- "for public_address[%s]\n",
- vnn->ifaces[i], ctdb_addr_to_str(addr)));
+ i = ctdb_add_local_iface(ctdb, iface);
+ if (i == NULL) {
+ DEBUG(DEBUG_ERR,
+ ("Failed to add interface '%s' "
+ "for public address %s\n",
+ iface, ctdb_addr_to_str(addr)));
talloc_free(vnn);
return -1;
}
+
+ vnn_iface = talloc_zero(vnn, struct vnn_interface);
+ if (vnn_iface == NULL) {
+ DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+ talloc_free(vnn);
+ return -1;
+ }
+
+ vnn_iface->iface = i;
+ DLIST_ADD_END(vnn->ifaces, vnn_iface);
}
+ talloc_free(tmp);
+ vnn->public_address = *addr;
+ vnn->public_netmask_bits = mask;
+ vnn->pnn = -1;
DLIST_ADD(ctdb->vnn, vnn);
@@ -1182,584 +1180,6 @@
return 0;
}
-static struct ctdb_public_ip_list *
-ctdb_fetch_remote_public_ips(struct ctdb_context *ctdb,
- TALLOC_CTX *mem_ctx,
- struct ctdb_node_map_old *nodemap,
- uint32_t public_ip_flags)
-{
- int j, ret;
- struct ctdb_public_ip_list_old *ip_list;
- struct ctdb_public_ip_list *public_ips;
-
- public_ips = talloc_zero_array(mem_ctx,
- struct ctdb_public_ip_list,
- nodemap->num);
- if (public_ips == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- return NULL;
- }
-
- for (j = 0; j < nodemap->num; j++) {
- if (nodemap->nodes[j].flags & NODE_FLAGS_INACTIVE) {
- continue;
- }
-
- /* Retrieve the list of public IPs from the
- * node. Flags says whether it is known or
- * available. */
- ret = ctdb_ctrl_get_public_ips_flags(
- ctdb, TAKEOVER_TIMEOUT(), j, public_ips,
- public_ip_flags, &ip_list);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Failed to read public IPs from node: %u\n", j));
- talloc_free(public_ips);
- return NULL;
- }
- public_ips[j].num = ip_list->num;
- if (ip_list->num == 0) {
- talloc_free(ip_list);
- continue;
- }
- public_ips[j].ip = talloc_zero_array(public_ips,
- struct ctdb_public_ip,
- ip_list->num);
- if (public_ips[j].ip == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- talloc_free(public_ips);
- return NULL;
- }
- memcpy(public_ips[j].ip, &ip_list->ips[0],
- sizeof(struct ctdb_public_ip) * ip_list->num);
- talloc_free(ip_list);
- }
-
- return public_ips;
-}
-
-struct get_tunable_callback_data {
- const char *tunable;
- uint32_t *out;
- bool fatal;
-};
-
-static void get_tunable_callback(struct ctdb_context *ctdb, uint32_t pnn,
- int32_t res, TDB_DATA outdata,
- void *callback)
-{
- struct get_tunable_callback_data *cd =
- (struct get_tunable_callback_data *)callback;
- int size;
-
- if (res != 0) {
- /* Already handled in fail callback */
- return;
- }
-
- if (outdata.dsize != sizeof(uint32_t)) {
- DEBUG(DEBUG_ERR,("Wrong size of returned data when reading \"%s\" tunable from node %d. Expected %d bytes but received %d bytes\n",
- cd->tunable, pnn, (int)sizeof(uint32_t),
- (int)outdata.dsize));
- cd->fatal = true;
- return;
- }
-
- size = talloc_array_length(cd->out);
- if (pnn >= size) {
- DEBUG(DEBUG_ERR,("Got %s reply from node %d but nodemap only has %d entries\n",
- cd->tunable, pnn, size));
- return;
- }
-
-
- cd->out[pnn] = *(uint32_t *)outdata.dptr;
-}
-
-static void get_tunable_fail_callback(struct ctdb_context *ctdb, uint32_t pnn,
- int32_t res, TDB_DATA outdata,
- void *callback)
-{
- struct get_tunable_callback_data *cd =
- (struct get_tunable_callback_data *)callback;
-
- switch (res) {
- case -ETIME:
- DEBUG(DEBUG_ERR,
- ("Timed out getting tunable \"%s\" from node %d\n",
- cd->tunable, pnn));
- cd->fatal = true;
- break;
- case -EINVAL:
- case -1:
- DEBUG(DEBUG_WARNING,
- ("Tunable \"%s\" not implemented on node %d\n",
- cd->tunable, pnn));
- break;
- default:
- DEBUG(DEBUG_ERR,
- ("Unexpected error getting tunable \"%s\" from node %d\n",
- cd->tunable, pnn));
- cd->fatal = true;
- }
-}
-
-static uint32_t *get_tunable_from_nodes(struct ctdb_context *ctdb,
- TALLOC_CTX *tmp_ctx,
- struct ctdb_node_map_old *nodemap,
- const char *tunable,
- uint32_t default_value)
-{
- TDB_DATA data;
- struct ctdb_control_get_tunable *t;
- uint32_t *nodes;
- uint32_t *tvals;
- struct get_tunable_callback_data callback_data;
- int i;
-
- tvals = talloc_array(tmp_ctx, uint32_t, nodemap->num);
- CTDB_NO_MEMORY_NULL(ctdb, tvals);
- for (i=0; inum; i++) {
- tvals[i] = default_value;
- }
-
- callback_data.out = tvals;
- callback_data.tunable = tunable;
- callback_data.fatal = false;
-
- data.dsize = offsetof(struct ctdb_control_get_tunable, name) + strlen(tunable) + 1;
- data.dptr = talloc_size(tmp_ctx, data.dsize);
- t = (struct ctdb_control_get_tunable *)data.dptr;
- t->length = strlen(tunable)+1;
- memcpy(t->name, tunable, t->length);
- nodes = list_of_connected_nodes(ctdb, nodemap, tmp_ctx, true);
- if (ctdb_client_async_control(ctdb, CTDB_CONTROL_GET_TUNABLE,
- nodes, 0, TAKEOVER_TIMEOUT(),
- false, data,
- get_tunable_callback,
- get_tunable_fail_callback,
- &callback_data) != 0) {
- if (callback_data.fatal) {
- talloc_free(tvals);
- tvals = NULL;
- }
- }
- talloc_free(nodes);
- talloc_free(data.dptr);
-
- return tvals;
-}
-
-static struct ctdb_node_map *
-ctdb_node_map_old_to_new(TALLOC_CTX *mem_ctx,
- const struct ctdb_node_map_old *old)
-{
- struct ctdb_node_map *new;
-
- new = talloc(mem_ctx, struct ctdb_node_map);
- if (new == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- return NULL;
- }
- new->num = old->num;
- new->node = talloc_zero_array(new,
- struct ctdb_node_and_flags, new->num);
- memcpy(new->node, &old->nodes[0],
- sizeof(struct ctdb_node_and_flags) * new->num);
-
- return new;
-}
-
-
-static bool set_ipflags(struct ctdb_context *ctdb,
- struct ipalloc_state *ipalloc_state,
- struct ctdb_node_map_old *nodemap)
-{
- uint32_t *tval_noiptakeover;
- uint32_t *tval_noiphostonalldisabled;
- struct ctdb_node_map *new;
-
- tval_noiptakeover = get_tunable_from_nodes(ctdb, ipalloc_state, nodemap,
- "NoIPTakeover", 0);
- if (tval_noiptakeover == NULL) {
- return false;
- }
-
- tval_noiphostonalldisabled =
- get_tunable_from_nodes(ctdb, ipalloc_state, nodemap,
- "NoIPHostOnAllDisabled", 0);
- if (tval_noiphostonalldisabled == NULL) {
- /* Caller frees tmp_ctx */
- return false;
- }
-
- new = ctdb_node_map_old_to_new(ipalloc_state, nodemap);
- if (new == NULL) {
- return false;
- }
-
- ipalloc_set_node_flags(ipalloc_state, new,
- tval_noiptakeover,
- tval_noiphostonalldisabled);
-
- talloc_free(tval_noiptakeover);
- talloc_free(tval_noiphostonalldisabled);
- talloc_free(new);
-
- return true;
-}
-
-static enum ipalloc_algorithm
-determine_algorithm(const struct ctdb_tunable_list *tunables)
-{
- if (1 == tunables->lcp2_public_ip_assignment) {
- return IPALLOC_LCP2;
- } else if (1 == tunables->deterministic_public_ips) {
- return IPALLOC_DETERMINISTIC;
- } else {
- return IPALLOC_NONDETERMINISTIC;
- }
-}
-
-struct takeover_callback_data {
- uint32_t num_nodes;
- unsigned int *fail_count;
-};
-
-static struct takeover_callback_data *
-takeover_callback_data_init(TALLOC_CTX *mem_ctx,
- uint32_t num_nodes)
-{
- static struct takeover_callback_data *takeover_data;
-
- takeover_data = talloc_zero(mem_ctx, struct takeover_callback_data);
- if (takeover_data == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- return NULL;
- }
-
- takeover_data->fail_count = talloc_zero_array(takeover_data,
- unsigned int, num_nodes);
- if (takeover_data->fail_count == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- talloc_free(takeover_data);
- return NULL;
- }
-
- takeover_data->num_nodes = num_nodes;
-
- return takeover_data;
-}
-
-static void takeover_run_fail_callback(struct ctdb_context *ctdb,
- uint32_t node_pnn, int32_t res,
- TDB_DATA outdata, void *callback_data)
-{
- struct takeover_callback_data *cd =
- talloc_get_type_abort(callback_data,
- struct takeover_callback_data);
-
- if (node_pnn >= cd->num_nodes) {
- DEBUG(DEBUG_ERR, (__location__ " invalid PNN %u\n", node_pnn));
- return;
- }
-
- if (cd->fail_count[node_pnn] == 0) {
- DEBUG(DEBUG_ERR,
- ("Node %u failed the takeover run\n", node_pnn));
- }
-
- cd->fail_count[node_pnn]++;
-}
-
-static void takeover_run_process_failures(struct ctdb_context *ctdb,
- struct takeover_callback_data *tcd)
-{
- unsigned int max_fails = 0;
- uint32_t max_pnn = -1;
- uint32_t i;
-
- for (i = 0; i < tcd->num_nodes; i++) {
- if (tcd->fail_count[i] > max_fails) {
- max_pnn = i;
- max_fails = tcd->fail_count[i];
- }
- }
-
- if (max_fails > 0) {
- int ret;
- TDB_DATA data;
-
- DEBUG(DEBUG_ERR,
- ("Sending banning credits to %u with fail count %u\n",
- max_pnn, max_fails));
-
- data.dptr = (uint8_t *)&max_pnn;
- data.dsize = sizeof(uint32_t);
- ret = ctdb_client_send_message(ctdb,
- CTDB_BROADCAST_CONNECTED,
- CTDB_SRVID_BANNING,
- data);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Failed to set banning credits for node %u\n",
- max_pnn));
- }
- }
-}
-
-/*
- * Recalculate the allocation of public IPs to nodes and have the
- * nodes host their allocated addresses.
- *
- * - Initialise IP allocation state. Pass:
- + algorithm to be used;
- + whether IP rebalancing ("failback") should be done (this uses a
- cluster-wide configuration variable and only the value form the
- master node is used); and
- * + list of nodes to force rebalance (internal structure, currently
- * no way to fetch, only used by LCP2 for nodes that have had new
- * IP addresses added).
- * - Set IP flags for IP allocation based on node map and tunables
- * NoIPTakeover/NoIPHostOnAllDisabled from all connected nodes
- * (tunable fetching done separately so values can be faked in unit
- * testing)
- * - Retrieve known and available IP addresses (done separately so
- * values can be faked in unit testing)
- * - Use ipalloc_set_public_ips() to set known and available IP
- addresses for allocation
- * - If cluster can't host IP addresses then early exit
- * - Run IP allocation algorithm
- * - Send RELEASE_IP to all nodes for IPs they should not host
- * - Send TAKE_IP to all nodes for IPs they should host
- * - Send IPREALLOCATED to all nodes (with backward compatibility hack)
- */
-int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map_old *nodemap,
- uint32_t *force_rebalance_nodes)
-{
- int i, ret;
- struct ctdb_public_ip ip;
- uint32_t *nodes;
- struct public_ip_list *all_ips, *tmp_ip;
- TDB_DATA data;
- struct timeval timeout;
- struct client_async_data *async_data;
- struct ctdb_client_control_state *state;
- TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
- struct ipalloc_state *ipalloc_state;
- struct ctdb_public_ip_list *known_ips, *available_ips;
- struct takeover_callback_data *takeover_data;
-
- /* Initialise fail callback data to be used with
- * takeover_run_fail_callback(). A failure in any of the
- * following steps will cause an early return, so this can be
- * reused for each of those steps without re-initialising. */
- takeover_data = takeover_callback_data_init(tmp_ctx,
- nodemap->num);
- if (takeover_data == NULL) {
- talloc_free(tmp_ctx);
- return -1;
- }
-
- /* Default timeout for early jump to IPREALLOCATED. See below
- * for explanation of 3 times... */
- timeout = timeval_current_ofs(3 * ctdb->tunable.takeover_timeout, 0);
-
- /*
- * ip failover is completely disabled, just send out the
- * ipreallocated event.
- */
- if (ctdb->tunable.disable_ip_failover != 0) {
- goto ipreallocated;
- }
-
- ipalloc_state = ipalloc_state_init(tmp_ctx, ctdb->num_nodes,
- determine_algorithm(&ctdb->tunable),
- (ctdb->tunable.no_ip_failback != 0),
- force_rebalance_nodes);
- if (ipalloc_state == NULL) {
- talloc_free(tmp_ctx);
- return -1;
- }
-
- if (!set_ipflags(ctdb, ipalloc_state, nodemap)) {
- DEBUG(DEBUG_ERR,
- ("Failed to set IP flags - aborting takeover run\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- /* Fetch known/available public IPs from each active node */
- /* Fetch lists of known public IPs from all nodes */
- known_ips = ctdb_fetch_remote_public_ips(ctdb, ipalloc_state,
- nodemap, 0);
- if (known_ips == NULL) {
- DEBUG(DEBUG_ERR, ("Failed to read known public IPs\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- available_ips = ctdb_fetch_remote_public_ips(
- ctdb, ipalloc_state, nodemap,
- CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE);
- if (available_ips == NULL) {
- DEBUG(DEBUG_ERR, ("Failed to read available public IPs\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- ipalloc_set_public_ips(ipalloc_state, known_ips, available_ips);
-
- if (! ipalloc_can_host_ips(ipalloc_state)) {
- DEBUG(DEBUG_WARNING,("No nodes available to host public IPs yet\n"));
- goto ipreallocated;
- }
-
- /* Do the IP reassignment calculations */
- all_ips = ipalloc(ipalloc_state);
- if (all_ips == NULL) {
- talloc_free(tmp_ctx);
- return -1;
- }
-
- /* Now tell all nodes to release any public IPs should not
- * host. This will be a NOOP on nodes that don't currently
- * hold the given IP.
- */
- async_data = talloc_zero(tmp_ctx, struct client_async_data);
- CTDB_NO_MEMORY_FATAL(ctdb, async_data);
-
- async_data->fail_callback = takeover_run_fail_callback;
- async_data->callback_data = takeover_data;
-
- ZERO_STRUCT(ip); /* Avoid valgrind warnings for union */
-
- /* Each of the following stages (RELEASE_IP, TAKEOVER_IP,
- * IPREALLOCATED) notionally has a timeout of TakeoverTimeout
- * seconds. However, RELEASE_IP can take longer due to TCP
- * connection killing, so sometimes needs more time.
- * Therefore, use a cumulative timeout of TakeoverTimeout * 3
- * seconds across all 3 stages. No explicit expiry checks are
- * needed before each stage because tevent is smart enough to
- * fire the timeouts even if they are in the past. Initialise
- * this here so it explicitly covers the stages we're
- * interested in but, in particular, not the time taken by the
- * ipalloc().
- */
- timeout = timeval_current_ofs(3 * ctdb->tunable.takeover_timeout, 0);
-
- /* Send a RELEASE_IP to all nodes that should not be hosting
- * each IP. For each IP, all but one of these will be
- * redundant. However, the redundant ones are used to tell
- * nodes which node should be hosting the IP so that commands
- * like "ctdb ip" can display a particular nodes idea of who
- * is hosting what. */
- for (i=0;inum;i++) {
- /* don't talk to unconnected nodes, but do talk to banned nodes */
- if (nodemap->nodes[i].flags & NODE_FLAGS_DISCONNECTED) {
- continue;
- }
-
- for (tmp_ip=all_ips;tmp_ip;tmp_ip=tmp_ip->next) {
- if (tmp_ip->pnn == nodemap->nodes[i].pnn) {
- /* This node should be serving this
- vnn so don't tell it to release the ip
- */
- continue;
- }
- ip.pnn = tmp_ip->pnn;
- ip.addr = tmp_ip->addr;
-
- data.dsize = sizeof(ip);
- data.dptr = (uint8_t *)&ip;
- state = ctdb_control_send(ctdb, nodemap->nodes[i].pnn,
- 0, CTDB_CONTROL_RELEASE_IP, 0,
- data, async_data,
- &timeout, NULL);
- if (state == NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Failed to call async control CTDB_CONTROL_RELEASE_IP to node %u\n", nodemap->nodes[i].pnn));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- ctdb_client_async_add(async_data, state);
- }
- }
- if (ctdb_client_async_wait(ctdb, async_data) != 0) {
- DEBUG(DEBUG_ERR,
- ("Async control CTDB_CONTROL_RELEASE_IP failed\n"));
- goto fail;
- }
- talloc_free(async_data);
-
-
- /* For each IP, send a TAKOVER_IP to the node that should be
- * hosting it. Many of these will often be redundant (since
- * the allocation won't have changed) but they can be useful
- * to recover from inconsistencies. */
- async_data = talloc_zero(tmp_ctx, struct client_async_data);
- CTDB_NO_MEMORY_FATAL(ctdb, async_data);
-
- async_data->fail_callback = takeover_run_fail_callback;
- async_data->callback_data = takeover_data;
-
- for (tmp_ip=all_ips;tmp_ip;tmp_ip=tmp_ip->next) {
- if (tmp_ip->pnn == -1) {
- /* this IP won't be taken over */
- continue;
- }
-
- ip.pnn = tmp_ip->pnn;
- ip.addr = tmp_ip->addr;
-
- data.dsize = sizeof(ip);
- data.dptr = (uint8_t *)&ip;
- state = ctdb_control_send(ctdb, tmp_ip->pnn,
- 0, CTDB_CONTROL_TAKEOVER_IP, 0,
- data, async_data, &timeout, NULL);
- if (state == NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Failed to call async control CTDB_CONTROL_TAKEOVER_IP to node %u\n", tmp_ip->pnn));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- ctdb_client_async_add(async_data, state);
- }
- if (ctdb_client_async_wait(ctdb, async_data) != 0) {
- DEBUG(DEBUG_ERR,
- ("Async control CTDB_CONTROL_TAKEOVER_IP failed\n"));
- goto fail;
- }
-
-ipreallocated:
- /*
- * Tell all nodes to run eventscripts to process the
- * "ipreallocated" event. This can do a lot of things,
- * including restarting services to reconfigure them if public
- * IPs have moved. Once upon a time this event only used to
- * update natgw.
- */
- nodes = list_of_connected_nodes(ctdb, nodemap, tmp_ctx, true);
- ret = ctdb_client_async_control(ctdb, CTDB_CONTROL_IPREALLOCATED,
- nodes, 0, timeout,
- false, tdb_null,
- NULL, takeover_run_fail_callback,
- takeover_data);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Async CTDB_CONTROL_IPREALLOCATED control failed\n"));
- goto fail;
- }
-
- talloc_free(tmp_ctx);
- return ret;
-
-fail:
- takeover_run_process_failures(ctdb, takeover_data);
- talloc_free(tmp_ctx);
- return -1;
-}
-
-
/*
destroy a ctdb_client_ip structure
*/
@@ -2254,6 +1674,7 @@
ctdb_sock_addr *addr;
struct ctdb_public_ip_info_old *info;
struct ctdb_vnn *vnn;
+ struct vnn_interface *iface;
addr = (ctdb_sock_addr *)indata.dptr;
@@ -2267,7 +1688,7 @@
/* count how many public ip structures we have */
num = 0;
- for (;vnn->ifaces[num];) {
+ for (iface = vnn->ifaces; iface != NULL; iface = iface->next) {
num++;
}
@@ -2280,15 +1701,11 @@
info->ip.pnn = vnn->pnn;
info->active_idx = 0xFFFFFFFF;
- for (i=0; vnn->ifaces[i]; i++) {
+ i = 0;
+ for (iface = vnn->ifaces; iface != NULL; iface = iface->next) {
struct ctdb_interface *cur;
- cur = ctdb_find_iface(ctdb, vnn->ifaces[i]);
- if (cur == NULL) {
- DEBUG(DEBUG_CRIT, (__location__ " internal error iface[%s] unknown\n",
- vnn->ifaces[i]));
- return -1;
- }
+ cur = iface->iface;
if (vnn->iface == cur) {
info->active_idx = i;
}
@@ -2297,6 +1714,8 @@
info->ifaces[i].name[sizeof(info->ifaces[i].name)-1] = '\0';
info->ifaces[i].link_state = cur->link_up;
info->ifaces[i].references = cur->references;
+
+ i++;
}
info->num = i;
len = offsetof(struct ctdb_public_ip_info_old, ifaces) +
@@ -2998,7 +2417,7 @@
struct ctdb_addr_info_old *pub;
const char *ifaces = NULL;
uint32_t len;
- int iface = 0;
+ struct vnn_interface *iface = NULL;
DEBUG(DEBUG_NOTICE,
("New IP %s configured, adding it\n",
@@ -3022,12 +2441,12 @@
first_add = false;
}
- ifaces = vnn->ifaces[0];
- iface = 1;
- while (vnn->ifaces[iface] != NULL) {
+ ifaces = vnn->ifaces->iface->name;
+ iface = vnn->ifaces->next;
+ while (iface != NULL) {
ifaces = talloc_asprintf(vnn, "%s,%s", ifaces,
- vnn->ifaces[iface]);
- iface++;
+ iface->iface->name);
+ iface = iface->next;
}
len = strlen(ifaces) + 1;
@@ -3114,10 +2533,9 @@
signed char res = 0;
close(h->fd[0]);
- debug_extra = talloc_asprintf(NULL, "reloadips:");
prctl_set_comment("ctdb_reloadips");
- if (switch_from_server_to_client(ctdb, "reloadips-child") != 0) {
+ if (switch_from_server_to_client(ctdb) != 0) {
DEBUG(DEBUG_CRIT,("ERROR: Failed to switch reloadips child into client mode\n"));
res = -1;
} else {
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_takeover_helper.c samba-4.6.5+dfsg/ctdb/server/ctdb_takeover_helper.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_takeover_helper.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_takeover_helper.c 2017-01-26 12:19:08.000000000 +0000
@@ -0,0 +1,1225 @@
+/*
+ CTDB IP takeover helper
+
+ Copyright (C) Martin Schwenke 2016
+
+ Based on ctdb_recovery_helper.c
+ Copyright (C) Amitay Isaacs 2015
+
+ and ctdb_takeover.c
+ Copyright (C) Ronnie Sahlberg 2007
+ Copyright (C) Andrew Tridgell 2007
+ Copyright (C) Martin Schwenke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+#include
+#include
+#include
+
+#include "lib/util/debug.h"
+#include "lib/util/strv.h"
+#include "lib/util/strv_util.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/time.h"
+#include "lib/util/tevent_unix.h"
+
+#include "protocol/protocol.h"
+#include "protocol/protocol_api.h"
+#include "client/client.h"
+
+#include "common/logging.h"
+
+#include "server/ipalloc.h"
+
+static int takeover_timeout = 9;
+
+#define TIMEOUT() timeval_current_ofs(takeover_timeout, 0)
+
+/*
+ * Utility functions
+ */
+
+static bool generic_recv(struct tevent_req *req, int *perr)
+{
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ if (perr != NULL) {
+ *perr = err;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static enum ipalloc_algorithm
+determine_algorithm(const struct ctdb_tunable_list *tunables)
+{
+ switch (tunables->ip_alloc_algorithm) {
+ case 0:
+ return IPALLOC_DETERMINISTIC;
+ case 1:
+ return IPALLOC_NONDETERMINISTIC;
+ case 2:
+ return IPALLOC_LCP2;
+ default:
+ return IPALLOC_LCP2;
+ };
+}
+
+/**********************************************************************/
+
+struct get_public_ips_state {
+ struct tevent_context *ev;
+ struct ctdb_client_context *client;
+ uint32_t *pnns;
+ int count, num_nodes;
+ struct ctdb_public_ip_list *ips;
+};
+
+static void get_public_ips_done(struct tevent_req *subreq);
+
+static struct tevent_req *get_public_ips_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_client_context *client,
+ uint32_t *pnns,
+ int count, int num_nodes,
+ bool available_only)
+{
+ struct tevent_req *req, *subreq;
+ struct get_public_ips_state *state;
+ struct ctdb_req_control request;
+
+ req = tevent_req_create(mem_ctx, &state, struct get_public_ips_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->pnns = pnns;
+ state->count = count;
+ state->num_nodes = num_nodes;
+ state->ips = NULL;
+
+ ctdb_req_control_get_public_ips(&request, available_only);
+ subreq = ctdb_client_control_multi_send(mem_ctx, ev, client,
+ state->pnns,
+ state->count,
+ TIMEOUT(), &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, get_public_ips_done, req);
+
+ return req;
+}
+
+static void get_public_ips_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct get_public_ips_state *state = tevent_req_data(
+ req, struct get_public_ips_state);
+ struct ctdb_reply_control **reply;
+ int *err_list;
+ int ret, i;
+ bool status;
+
+ status = ctdb_client_control_multi_recv(subreq, &ret, state, &err_list,
+ &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ int ret2;
+ uint32_t pnn;
+
+ ret2 = ctdb_client_control_multi_error(state->pnns,
+ state->count,
+ err_list, &pnn);
+ if (ret2 != 0) {
+ D_ERR("control GET_PUBLIC_IPS failed on "
+ "node %u, ret=%d\n", pnn, ret2);
+ } else {
+ D_ERR("control GET_PUBLIC_IPS failed, "
+ "ret=%d\n", ret);
+ }
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->ips = talloc_zero_array(state, struct ctdb_public_ip_list,
+ state->num_nodes);
+ if (tevent_req_nomem(state->ips, req)) {
+ return;
+ }
+
+ for (i = 0; i < state->count; i++) {
+ uint32_t pnn;
+ struct ctdb_public_ip_list *ips;
+
+ pnn = state->pnns[i];
+ ret = ctdb_reply_control_get_public_ips(reply[i], state->ips,
+ &ips);
+ if (ret != 0) {
+ D_ERR("control GET_PUBLIC_IPS failed on "
+ "node %u\n", pnn);
+ tevent_req_error(req, EIO);
+ return;
+ }
+ state->ips[pnn] = *ips;
+ }
+
+ talloc_free(reply);
+
+ tevent_req_done(req);
+}
+
+static bool get_public_ips_recv(struct tevent_req *req, int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_public_ip_list **ips)
+{
+ struct get_public_ips_state *state = tevent_req_data(
+ req, struct get_public_ips_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ if (perr != NULL) {
+ *perr = err;
+ }
+ return false;
+ }
+
+ *ips = talloc_steal(mem_ctx, state->ips);
+
+ return true;
+}
+
+/**********************************************************************/
+
+struct release_ip_state {
+ int num_sent;
+ int num_replies;
+ int num_fails;
+ int err_any;
+ uint32_t *ban_credits;
+};
+
+struct release_ip_one_state {
+ struct tevent_req *req;
+ uint32_t *pnns;
+ int count;
+ const char *ip_str;
+};
+
+static void release_ip_done(struct tevent_req *subreq);
+
+static struct tevent_req *release_ip_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_client_context *client,
+ uint32_t *pnns,
+ int count,
+ struct timeval timeout,
+ struct public_ip_list *all_ips,
+ uint32_t *ban_credits)
+{
+ struct tevent_req *req, *subreq;
+ struct release_ip_state *state;
+ struct ctdb_req_control request;
+ struct public_ip_list *tmp_ip;
+
+ req = tevent_req_create(mem_ctx, &state, struct release_ip_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->num_sent = 0;
+ state->num_replies = 0;
+ state->num_fails = 0;
+ state->ban_credits = ban_credits;
+
+ /* Send a RELEASE_IP to all nodes that should not be hosting
+ * each IP. For each IP, all but one of these will be
+ * redundant. However, the redundant ones are used to tell
+ * nodes which node should be hosting the IP so that commands
+ * like "ctdb ip" can display a particular nodes idea of who
+ * is hosting what. */
+ for (tmp_ip = all_ips; tmp_ip != NULL; tmp_ip = tmp_ip->next) {
+ struct release_ip_one_state *substate;
+ struct ctdb_public_ip ip;
+ int i;
+
+ substate = talloc_zero(state, struct release_ip_one_state);
+ if (tevent_req_nomem(substate, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ substate->pnns = talloc_zero_array(substate, uint32_t, count);
+ if (tevent_req_nomem(substate->pnns, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ substate->count = 0;
+ substate->req = req;
+
+ substate->ip_str = ctdb_sock_addr_to_string(substate,
+ &tmp_ip->addr);
+ if (tevent_req_nomem(substate->ip_str, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i = 0; i < count; i++) {
+ uint32_t pnn = pnns[i];
+ /* If pnn is not the node that should be
+ * hosting the IP then add it to the list of
+ * nodes that need to do a release. */
+ if (tmp_ip->pnn != pnn) {
+ substate->pnns[substate->count] = pnn;
+ substate->count++;
+ }
+ }
+
+ if (substate->count == 0) {
+ /* No releases to send for this address... */
+ TALLOC_FREE(substate);
+ continue;
+ }
+
+ ip.pnn = tmp_ip->pnn;
+ ip.addr = tmp_ip->addr;
+ ctdb_req_control_release_ip(&request, &ip);
+ subreq = ctdb_client_control_multi_send(state, ev, client,
+ substate->pnns,
+ substate->count,
+ timeout,/* cumulative */
+ &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, release_ip_done, substate);
+
+ state->num_sent++;
+ }
+
+ /* None sent, finished... */
+ if (state->num_sent == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void release_ip_done(struct tevent_req *subreq)
+{
+ struct release_ip_one_state *substate = tevent_req_callback_data(
+ subreq, struct release_ip_one_state);
+ struct tevent_req *req = substate->req;
+ struct release_ip_state *state = tevent_req_data(
+ req, struct release_ip_state);
+ int ret, i;
+ int *err_list;
+ bool status, found_errors;
+
+ status = ctdb_client_control_multi_recv(subreq, &ret, state,
+ &err_list, NULL);
+ TALLOC_FREE(subreq);
+
+ if (status) {
+ D_INFO("RELEASE_IP %s succeeded on %d nodes\n",
+ substate->ip_str, substate->count);
+ goto done;
+ }
+
+ /* Get some clear error messages out of err_list and count
+ * banning credits
+ */
+ found_errors = false;
+ for (i = 0; i < substate->count; i++) {
+ int err = err_list[i];
+ if (err != 0) {
+ uint32_t pnn = substate->pnns[i];
+
+ D_ERR("RELEASE_IP %s failed on node %u, "
+ "ret=%d\n", substate->ip_str, pnn, err);
+
+ state->ban_credits[pnn]++;
+ state->err_any = err;
+ found_errors = true;
+ }
+ }
+ if (! found_errors) {
+ D_ERR("RELEASE_IP %s internal error, ret=%d\n",
+ substate->ip_str, ret);
+ state->err_any = EIO;
+ }
+
+ state->num_fails++;
+
+done:
+ talloc_free(substate);
+
+ state->num_replies++;
+
+ if (state->num_replies < state->num_sent) {
+ /* Not all replies received, don't go further */
+ return;
+ }
+
+ if (state->num_fails > 0) {
+ tevent_req_error(req, state->err_any);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool release_ip_recv(struct tevent_req *req, int *perr)
+{
+ return generic_recv(req, perr);
+}
+
+/**********************************************************************/
+
+struct take_ip_state {
+ int num_sent;
+ int num_replies;
+ int num_fails;
+ int err_any;
+ uint32_t *ban_credits;
+};
+
+struct take_ip_one_state {
+ struct tevent_req *req;
+ uint32_t pnn;
+ const char *ip_str;
+};
+
+static void take_ip_done(struct tevent_req *subreq);
+
+static struct tevent_req *take_ip_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_client_context *client,
+ struct timeval timeout,
+ struct public_ip_list *all_ips,
+ uint32_t *ban_credits)
+{
+ struct tevent_req *req, *subreq;
+ struct take_ip_state *state;
+ struct ctdb_req_control request;
+ struct public_ip_list *tmp_ip;
+
+ req = tevent_req_create(mem_ctx, &state, struct take_ip_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->num_sent = 0;
+ state->num_replies = 0;
+ state->num_fails = 0;
+ state->ban_credits = ban_credits;
+
+ /* For each IP, send a TAKOVER_IP to the node that should be
+ * hosting it. Many of these will often be redundant (since
+ * the allocation won't have changed) but they can be useful
+ * to recover from inconsistencies. */
+ for (tmp_ip = all_ips; tmp_ip != NULL; tmp_ip = tmp_ip->next) {
+ struct take_ip_one_state *substate;
+ struct ctdb_public_ip ip;
+
+ if (tmp_ip->pnn == -1) {
+ /* IP will be unassigned */
+ continue;
+ }
+
+ substate = talloc_zero(state, struct take_ip_one_state);
+ if (tevent_req_nomem(substate, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ substate->req = req;
+ substate->pnn = tmp_ip->pnn;
+
+ substate->ip_str = ctdb_sock_addr_to_string(substate,
+ &tmp_ip->addr);
+ if (tevent_req_nomem(substate->ip_str, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ip.pnn = tmp_ip->pnn;
+ ip.addr = tmp_ip->addr;
+ ctdb_req_control_takeover_ip(&request, &ip);
+ subreq = ctdb_client_control_send(
+ state, ev, client, tmp_ip->pnn,
+ timeout, /* cumulative */
+ &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, take_ip_done, substate);
+
+ state->num_sent++;
+ }
+
+ /* None sent, finished... */
+ if (state->num_sent == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void take_ip_done(struct tevent_req *subreq)
+{
+ struct take_ip_one_state *substate = tevent_req_callback_data(
+ subreq, struct take_ip_one_state);
+ struct tevent_req *req = substate->req;
+ struct ctdb_reply_control *reply;
+ struct take_ip_state *state = tevent_req_data(
+ req, struct take_ip_state);
+ int ret = 0;
+ bool status;
+
+ status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ D_ERR("TAKEOVER_IP %s failed to node %u, ret=%d\n",
+ substate->ip_str, substate->pnn, ret);
+ goto fail;
+ }
+
+ ret = ctdb_reply_control_takeover_ip(reply);
+ if (ret != 0) {
+ D_ERR("TAKEOVER_IP %s failed on node %u, ret=%d\n",
+ substate->ip_str, substate->pnn, ret);
+ goto fail;
+ }
+
+ D_INFO("TAKEOVER_IP %s succeeded on node %u\n",
+ substate->ip_str, substate->pnn);
+ goto done;
+
+fail:
+ state->ban_credits[substate->pnn]++;
+ state->num_fails++;
+ state->err_any = ret;
+
+done:
+ talloc_free(substate);
+
+ state->num_replies++;
+
+ if (state->num_replies < state->num_sent) {
+ /* Not all replies received, don't go further */
+ return;
+ }
+
+ if (state->num_fails > 0) {
+ tevent_req_error(req, state->err_any);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool take_ip_recv(struct tevent_req *req, int *perr)
+{
+ return generic_recv(req, perr);
+}
+
+/**********************************************************************/
+
+struct ipreallocated_state {
+ uint32_t *pnns;
+ int count;
+ uint32_t *ban_credits;
+};
+
+static void ipreallocated_done(struct tevent_req *subreq);
+
+static struct tevent_req *ipreallocated_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_client_context *client,
+ uint32_t *pnns,
+ int count,
+ struct timeval timeout,
+ uint32_t *ban_credits)
+{
+ struct tevent_req *req, *subreq;
+ struct ipreallocated_state *state;
+ struct ctdb_req_control request;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipreallocated_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->pnns = pnns;
+ state->count = count;
+ state->ban_credits = ban_credits;
+
+ ctdb_req_control_ipreallocated(&request);
+ subreq = ctdb_client_control_multi_send(state, ev, client,
+ pnns, count,
+ timeout, /* cumulative */
+ &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ipreallocated_done, req);
+
+ return req;
+}
+
+static void ipreallocated_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ipreallocated_state *state = tevent_req_data(
+ req, struct ipreallocated_state);
+ int *err_list = NULL;
+ int ret, i;
+ bool status, found_errors;
+
+ status = ctdb_client_control_multi_recv(subreq, &ret, state,
+ &err_list, NULL);
+ TALLOC_FREE(subreq);
+
+ if (status) {
+ D_INFO("IPREALLOCATED succeeded on %d nodes\n", state->count);
+ tevent_req_done(req);
+ return;
+ }
+
+ /* Get some clear error messages out of err_list and count
+ * banning credits
+ */
+ found_errors = false;
+ for (i = 0; i < state->count; i++) {
+ int err = err_list[i];
+ if (err != 0) {
+ uint32_t pnn = state->pnns[i];
+
+ D_ERR("IPREALLOCATED failed on node %u, ret=%d\n",
+ pnn, err);
+
+ state->ban_credits[pnn]++;
+ found_errors = true;
+ }
+ }
+
+ if (! found_errors) {
+ D_ERR("IPREALLOCATED internal error, ret=%d\n", ret);
+ }
+
+ tevent_req_error(req, ret);
+}
+
+static bool ipreallocated_recv(struct tevent_req *req, int *perr)
+{
+ return generic_recv(req, perr);
+}
+
+/**********************************************************************/
+
+/*
+ * Recalculate the allocation of public IPs to nodes and have the
+ * nodes host their allocated addresses.
+ *
+ * - Get tunables
+ * - Get nodemap
+ * - Initialise IP allocation state. Pass:
+ * + algorithm to be used;
+ * + various tunables (NoIPTakeover, NoIPFailback, NoIPHostOnAllDisabled)
+ * + list of nodes to force rebalance (internal structure, currently
+ * no way to fetch, only used by LCP2 for nodes that have had new
+ * IP addresses added).
+ * - Set IP flags for IP allocation based on node map
+ * - Retrieve known and available IP addresses (done separately so
+ * values can be faked in unit testing)
+ * - Use ipalloc_set_public_ips() to set known and available IP
+ * addresses for allocation
+ * - If cluster can't host IP addresses then jump to IPREALLOCATED
+ * - Run IP allocation algorithm
+ * - Send RELEASE_IP to all nodes for IPs they should not host
+ * - Send TAKE_IP to all nodes for IPs they should host
+ * - Send IPREALLOCATED to all nodes
+ */
+
+struct takeover_state {
+ struct tevent_context *ev;
+ struct ctdb_client_context *client;
+ struct timeval timeout;
+ int num_nodes;
+ uint32_t *pnns_connected;
+ int num_connected;
+ uint32_t *pnns_active;
+ int num_active;
+ uint32_t destnode;
+ uint32_t *force_rebalance_nodes;
+ struct ctdb_tunable_list *tun_list;
+ struct ipalloc_state *ipalloc_state;
+ struct ctdb_public_ip_list *known_ips;
+ struct public_ip_list *all_ips;
+ uint32_t *ban_credits;
+};
+
+static void takeover_tunables_done(struct tevent_req *subreq);
+static void takeover_nodemap_done(struct tevent_req *subreq);
+static void takeover_known_ips_done(struct tevent_req *subreq);
+static void takeover_avail_ips_done(struct tevent_req *subreq);
+static void takeover_release_ip_done(struct tevent_req *subreq);
+static void takeover_take_ip_done(struct tevent_req *subreq);
+static void takeover_ipreallocated(struct tevent_req *req);
+static void takeover_ipreallocated_done(struct tevent_req *subreq);
+static void takeover_failed(struct tevent_req *subreq, int ret);
+static void takeover_failed_done(struct tevent_req *subreq);
+
+static struct tevent_req *takeover_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_client_context *client,
+ uint32_t *force_rebalance_nodes)
+{
+ struct tevent_req *req, *subreq;
+ struct takeover_state *state;
+ struct ctdb_req_control request;
+
+ req = tevent_req_create(mem_ctx, &state, struct takeover_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->client = client;
+ state->force_rebalance_nodes = force_rebalance_nodes;
+ state->destnode = ctdb_client_pnn(client);
+
+ ctdb_req_control_get_all_tunables(&request);
+ subreq = ctdb_client_control_send(state, state->ev, state->client,
+ state->destnode, TIMEOUT(),
+ &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, takeover_tunables_done, req);
+
+ return req;
+}
+
+static void takeover_tunables_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ struct ctdb_reply_control *reply;
+ struct ctdb_req_control request;
+ int ret;
+ bool status;
+
+ status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("control GET_ALL_TUNABLES failed, ret=%d\n", ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ctdb_reply_control_get_all_tunables(reply, state,
+ &state->tun_list);
+ if (ret != 0) {
+ D_ERR("control GET_ALL_TUNABLES failed, ret=%d\n", ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ talloc_free(reply);
+
+ takeover_timeout = state->tun_list->takeover_timeout;
+
+ ctdb_req_control_get_nodemap(&request);
+ subreq = ctdb_client_control_send(state, state->ev, state->client,
+ state->destnode, TIMEOUT(),
+ &request);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, takeover_nodemap_done, req);
+}
+
+static void takeover_nodemap_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ struct ctdb_reply_control *reply;
+ bool status;
+ int ret;
+ struct ctdb_node_map *nodemap;
+
+ status = ctdb_client_control_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("control GET_NODEMAP failed to node %u, ret=%d\n",
+ state->destnode, ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = ctdb_reply_control_get_nodemap(reply, state, &nodemap);
+ if (ret != 0) {
+ D_ERR("control GET_NODEMAP failed, ret=%d\n", ret);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->num_nodes = nodemap->num;
+
+ state->num_connected = list_of_connected_nodes(nodemap,
+ CTDB_UNKNOWN_PNN, state,
+ &state->pnns_connected);
+ if (state->num_connected <= 0) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ state->num_active = list_of_active_nodes(nodemap,
+ CTDB_UNKNOWN_PNN, state,
+ &state->pnns_active);
+ if (state->num_active <= 0) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /* Default timeout for early jump to IPREALLOCATED. See below
+ * for explanation of 3 times...
+ */
+ state->timeout = timeval_current_ofs(3 * takeover_timeout, 0);
+
+ state->ban_credits = talloc_zero_array(state, uint32_t,
+ state->num_nodes);
+ if (tevent_req_nomem(state->ban_credits, req)) {
+ return;
+ }
+
+ if (state->tun_list->disable_ip_failover != 0) {
+ /* IP failover is completely disabled so just send out
+ * ipreallocated event.
+ */
+ takeover_ipreallocated(req);
+ return;
+ }
+
+ state->ipalloc_state =
+ ipalloc_state_init(
+ state, state->num_nodes,
+ determine_algorithm(state->tun_list),
+ (state->tun_list->no_ip_takeover != 0),
+ (state->tun_list->no_ip_failback != 0),
+ (state->tun_list->no_ip_host_on_all_disabled != 0),
+ state->force_rebalance_nodes);
+ if (tevent_req_nomem(state->ipalloc_state, req)) {
+ return;
+ }
+
+ ipalloc_set_node_flags(state->ipalloc_state, nodemap);
+
+ subreq = get_public_ips_send(state, state->ev, state->client,
+ state->pnns_active, state->num_active,
+ state->num_nodes, false);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, takeover_known_ips_done, req);
+}
+
+static void takeover_known_ips_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ int ret;
+ bool status;
+
+ status = get_public_ips_recv(subreq, &ret, state, &state->known_ips);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ D_ERR("Failed to fetch known public IPs\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = get_public_ips_send(state, state->ev, state->client,
+ state->pnns_active, state->num_active,
+ state->num_nodes, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, takeover_avail_ips_done, req);
+}
+
+static void takeover_avail_ips_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ bool status;
+ int ret;
+ struct ctdb_public_ip_list *available_ips;
+
+ status = get_public_ips_recv(subreq, &ret, state, &available_ips);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ D_ERR("Failed to fetch available public IPs\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ipalloc_set_public_ips(state->ipalloc_state,
+ state->known_ips, available_ips);
+
+ if (! ipalloc_can_host_ips(state->ipalloc_state)) {
+ D_NOTICE("No nodes available to host public IPs yet\n");
+ takeover_ipreallocated(req);
+ return;
+ }
+
+ /* Do the IP reassignment calculations */
+ state->all_ips = ipalloc(state->ipalloc_state);
+ if (tevent_req_nomem(state->all_ips, req)) {
+ return;
+ }
+
+ /* Each of the following stages (RELEASE_IP, TAKEOVER_IP,
+ * IPREALLOCATED) notionally has a timeout of TakeoverTimeout
+ * seconds. However, RELEASE_IP can take longer due to TCP
+ * connection killing, so sometimes needs more time.
+ * Therefore, use a cumulative timeout of TakeoverTimeout * 3
+ * seconds across all 3 stages. No explicit expiry checks are
+ * needed before each stage because tevent is smart enough to
+ * fire the timeouts even if they are in the past. Initialise
+ * this here so it explicitly covers the stages we're
+ * interested in but, in particular, not the time taken by the
+ * ipalloc().
+ */
+ state->timeout = timeval_current_ofs(3 * takeover_timeout, 0);
+
+ subreq = release_ip_send(state, state->ev, state->client,
+ state->pnns_connected, state->num_connected,
+ state->timeout, state->all_ips,
+ state->ban_credits);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, takeover_release_ip_done, req);
+}
+
+static void takeover_release_ip_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ int ret;
+ bool status;
+
+ status = release_ip_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ takeover_failed(req, ret);
+ return;
+ }
+
+ /* All released, now for takeovers */
+
+ subreq = take_ip_send(state, state->ev, state->client,
+ state->timeout, state->all_ips,
+ state->ban_credits);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, takeover_take_ip_done, req);
+}
+
+static void takeover_take_ip_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret = 0;
+ bool status;
+
+ status = take_ip_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ takeover_failed(req, ret);
+ return;
+ }
+
+ takeover_ipreallocated(req);
+}
+
+static void takeover_ipreallocated(struct tevent_req *req)
+{
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ struct tevent_req *subreq;
+
+ subreq = ipreallocated_send(state, state->ev, state->client,
+ state->pnns_connected,
+ state->num_connected,
+ state->timeout,
+ state->ban_credits);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, takeover_ipreallocated_done, req);
+}
+
+static void takeover_ipreallocated_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+ bool status;
+
+ status = ipreallocated_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ takeover_failed(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+struct takeover_failed_state {
+ struct tevent_req *req;
+ int ret;
+};
+
+void takeover_failed(struct tevent_req *req, int ret)
+{
+ struct takeover_state *state = tevent_req_data(
+ req, struct takeover_state);
+ struct tevent_req *subreq;
+ uint32_t max_pnn = CTDB_UNKNOWN_PNN;
+ int max_credits = 0;
+ int pnn;
+
+ /* Check that bans are enabled */
+ if (state->tun_list->enable_bans == 0) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ for (pnn = 0; pnn < state->num_nodes; pnn++) {
+ if (state->ban_credits[pnn] > max_credits) {
+ max_pnn = pnn;
+ max_credits = state->ban_credits[pnn];
+ }
+ }
+
+ if (max_credits > 0) {
+ struct ctdb_req_message message;
+ struct takeover_failed_state *substate;
+
+ D_WARNING("Assigning banning credits to node %u\n", max_pnn);
+
+ substate = talloc_zero(state, struct takeover_failed_state);
+ if (tevent_req_nomem(substate, req)) {
+ return;
+ }
+ substate->req = req;
+ substate->ret = ret;
+
+ message.srvid = CTDB_SRVID_BANNING;
+ message.data.pnn = max_pnn;
+
+ subreq = ctdb_client_message_send(
+ state, state->ev, state->client,
+ ctdb_client_pnn(state->client),
+ &message);
+ if (subreq == NULL) {
+ D_ERR("failed to assign banning credits\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_set_callback(subreq, takeover_failed_done, substate);
+ } else {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void takeover_failed_done(struct tevent_req *subreq)
+{
+ struct takeover_failed_state *substate = tevent_req_callback_data(
+ subreq, struct takeover_failed_state);
+ struct tevent_req *req = substate->req;
+ int ret;
+ bool status;
+
+ status = ctdb_client_message_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ D_ERR("failed to assign banning credits, ret=%d\n", ret);
+ }
+
+ ret = substate->ret;
+ talloc_free(substate);
+ tevent_req_error(req, ret);
+}
+
+static void takeover_recv(struct tevent_req *req, int *perr)
+{
+ generic_recv(req, perr);
+}
+
+static uint32_t *parse_node_list(TALLOC_CTX *mem_ctx, const char* s)
+{
+ char *strv = NULL;
+ int num, i, ret;
+ char *t;
+ uint32_t *nodes;
+
+ ret = strv_split(mem_ctx, &strv, s, ",");
+ if (ret != 0) {
+ D_ERR("out of memory\n");
+ return NULL;
+ }
+
+ num = strv_count(strv);
+
+ nodes = talloc_array(mem_ctx, uint32_t, num);
+ if (nodes == NULL) {
+ D_ERR("out of memory\n");
+ return NULL;
+ }
+
+ t = NULL;
+ for (i = 0; i < num; i++) {
+ t = strv_next(strv, t);
+ nodes[i] = atoi(t);
+ }
+
+ return nodes;
+}
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+ "\nUsage: %s "
+ "[]\n",
+ progname);
+}
+
+/*
+ * Arguments - write fd, socket path
+ */
+int main(int argc, const char *argv[])
+{
+ int write_fd;
+ const char *sockpath;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct ctdb_client_context *client;
+ int ret;
+ struct tevent_req *req;
+ uint32_t *force_rebalance_nodes = NULL;
+
+ if (argc < 3 || argc > 4) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ write_fd = atoi(argv[1]);
+ sockpath = argv[2];
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ fprintf(stderr, "talloc_new() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (argc == 4) {
+ force_rebalance_nodes = parse_node_list(mem_ctx, argv[3]);
+ if (force_rebalance_nodes == NULL) {
+ usage(argv[0]);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = logging_init(mem_ctx, NULL, NULL, "ctdb-takeover");
+ if (ret != 0) {
+ fprintf(stderr,
+ "ctdb-takeover: Unable to initialize logging\n");
+ goto done;
+ }
+
+ ev = tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ D_ERR("tevent_context_init() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ctdb_client_init(mem_ctx, ev, sockpath, &client);
+ if (ret != 0) {
+ D_ERR("ctdb_client_init() failed, ret=%d\n", ret);
+ goto done;
+ }
+
+ req = takeover_send(mem_ctx, ev, client, force_rebalance_nodes);
+ if (req == NULL) {
+ D_ERR("takeover_send() failed\n");
+ ret = 1;
+ goto done;
+ }
+
+ if (! tevent_req_poll(req, ev)) {
+ D_ERR("tevent_req_poll() failed\n");
+ ret = 1;
+ goto done;
+ }
+
+ takeover_recv(req, &ret);
+ TALLOC_FREE(req);
+ if (ret != 0) {
+ D_ERR("takeover run failed, ret=%d\n", ret);
+ }
+
+done:
+ sys_write_v(write_fd, &ret, sizeof(ret));
+
+ talloc_free(mem_ctx);
+ return ret;
+}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_traverse.c samba-4.6.5+dfsg/ctdb/server/ctdb_traverse.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_traverse.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_traverse.c 2017-01-11 07:55:14.000000000 +0000
@@ -30,6 +30,7 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
@@ -219,8 +220,7 @@
close(h->fd[0]);
prctl_set_comment("ctdb_traverse");
- if (switch_from_server_to_client(ctdb, "traverse_local-%s:",
- ctdb_db->db_name) != 0) {
+ if (switch_from_server_to_client(ctdb) != 0) {
DEBUG(DEBUG_CRIT, ("Failed to switch traverse child into client mode\n"));
_exit(0);
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_update_record.c samba-4.6.5+dfsg/ctdb/server/ctdb_update_record.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_update_record.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_update_record.c 2017-01-11 07:55:14.000000000 +0000
@@ -28,6 +28,7 @@
#include "lib/tdb_wrap/tdb_wrap.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
@@ -267,7 +268,6 @@
close(result->fd[0]);
prctl_set_comment("ctdb_write_persistent");
- debug_extra = talloc_asprintf(NULL, "childwrite-%s:", ctdb_db->db_name);
ret = ctdb_persistent_store(state);
if (ret != 0) {
DEBUG(DEBUG_ERR, (__location__ " Failed to write persistent data\n"));
diff -Nru samba-4.5.8+dfsg/ctdb/server/ctdb_vacuum.c samba-4.6.5+dfsg/ctdb/server/ctdb_vacuum.c
--- samba-4.5.8+dfsg/ctdb/server/ctdb_vacuum.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ctdb_vacuum.c 2017-01-11 07:55:14.000000000 +0000
@@ -31,13 +31,13 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "lib/util/util_process.h"
#include "ctdb_private.h"
#include "ctdb_client.h"
#include "common/rb_tree.h"
-#include "common/system.h"
#include "common/common.h"
#include "common/logging.h"
@@ -1507,7 +1507,7 @@
DEBUG(DEBUG_INFO,("Vacuuming child process %d for db %s started\n", getpid(), ctdb_db->db_name));
prctl_set_comment("ctdb_vacuum");
- if (switch_from_server_to_client(ctdb, "vacuum-%s", ctdb_db->db_name) != 0) {
+ if (switch_from_server_to_client(ctdb) != 0) {
DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch vacuum daemon into client mode. Shutting down.\n"));
_exit(1);
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/eventscript.c samba-4.6.5+dfsg/ctdb/server/eventscript.c
--- samba-4.5.8+dfsg/ctdb/server/eventscript.c 2016-09-13 08:21:35.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/eventscript.c 2017-01-26 12:19:08.000000000 +0000
@@ -24,6 +24,7 @@
#include "system/dir.h"
#include "system/locale.h"
#include "system/time.h"
+#include "system/dir.h"
#include
#include
@@ -31,632 +32,619 @@
#include "lib/util/dlinklist.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "ctdb_private.h"
#include "common/rb_tree.h"
-#include "common/system.h"
#include "common/common.h"
#include "common/logging.h"
+#include "common/reqid.h"
+#include "common/sock_io.h"
+#include "protocol/protocol_api.h"
-static void ctdb_event_script_timeout(struct tevent_context *ev,
- struct tevent_timer *te,
- struct timeval t, void *p);
-
-/* This is attached to the event script state. */
-struct event_script_callback {
- struct event_script_callback *next, *prev;
- struct ctdb_context *ctdb;
+/*
+ * Setting up event daemon
+ */
- /* Warning: this can free us! */
- void (*fn)(struct ctdb_context *, int, void *);
- void *private_data;
+struct eventd_context {
+ struct tevent_context *ev;
+ const char *path;
+ const char *script_dir;
+ const char *pidfile;
+ const char *socket;
+ const char *debug_hung_script;
+
+ /* server state */
+ pid_t eventd_pid;
+ struct tevent_fd *eventd_fde;
+
+ /* client state */
+ struct reqid_context *idr;
+ struct sock_queue *queue;
+ struct eventd_client_state *calls;
};
-struct ctdb_event_script_state {
- struct ctdb_context *ctdb;
- struct event_script_callback *callback;
- pid_t child;
- int fd[2];
- enum ctdb_event call;
- const char *options;
- struct timeval timeout;
+static bool eventd_context_init(TALLOC_CTX *mem_ctx,
+ struct ctdb_context *ctdb,
+ struct eventd_context **out)
+{
+ struct eventd_context *ectx;
+ const char *eventd = CTDB_HELPER_BINDIR "/ctdb_eventd";
+ const char *debug_hung_script = CTDB_ETCDIR "/debug-hung-script.sh";
+ const char *value;
+ char *socket;
+ int ret;
- unsigned int current;
- struct ctdb_script_list_old *scripts;
-};
+ ectx = talloc_zero(mem_ctx, struct eventd_context);
+ if (ectx == NULL) {
+ return false;
+ }
-static struct ctdb_script *get_current_script(struct ctdb_event_script_state *state)
-{
- return &state->scripts->scripts[state->current];
-}
+ ectx->ev = ctdb->ev;
-/* called from ctdb_logging when we have received output on STDERR from
- * one of the eventscripts
- */
-static void log_event_script_output(const char *str, uint16_t len, void *p)
-{
- struct ctdb_event_script_state *state
- = talloc_get_type(p, struct ctdb_event_script_state);
- struct ctdb_script *current;
- unsigned int slen, min;
-
- /* We may have been aborted to run something else. Discard */
- if (state->scripts == NULL) {
- return;
+ value = getenv("CTDB_EVENTD");
+ if (value != NULL) {
+ eventd = value;
}
- current = get_current_script(state);
-
- /* Append, but don't overfill buffer. It starts zero-filled. */
- slen = strlen(current->output);
- min = MIN(len, sizeof(current->output) - slen - 1);
+ ectx->path = talloc_strdup(ectx, eventd);
+ if (ectx->path == NULL) {
+ talloc_free(ectx);
+ return false;
+ }
- memcpy(current->output + slen, str, min);
-}
+ ectx->script_dir = ctdb->event_script_dir;
-int32_t ctdb_control_get_event_script_status(struct ctdb_context *ctdb,
- uint32_t call_type,
- TDB_DATA *outdata)
-{
- if (call_type >= CTDB_EVENT_MAX) {
- return -1;
+ socket = talloc_strdup(ectx, ctdb_get_socketname(ctdb));
+ if (socket == NULL) {
+ talloc_free(ectx);
+ return NULL;
}
- if (ctdb->last_status[call_type] == NULL) {
- /* If it's never been run, return nothing so they can tell. */
- outdata->dsize = 0;
- } else {
- outdata->dsize = talloc_get_size(ctdb->last_status[call_type]);
- outdata->dptr = (uint8_t *)ctdb->last_status[call_type];
+ ectx->socket = talloc_asprintf(ectx, "%s/eventd.sock",
+ dirname(socket));
+ if (ectx->socket == NULL) {
+ talloc_free(ectx);
+ return false;
}
- return 0;
-}
-/* To ignore directory entry return 0, else return non-zero */
-static int script_filter(const struct dirent *de)
-{
- int namelen = strlen(de->d_name);
+ talloc_free(socket);
- /* Ignore . and .. */
- if (namelen < 3) {
- return 0;
+ value = getenv("CTDB_DEBUG_HUNG_SCRIPT");
+ if (value != NULL) {
+ if (value[0] == '\0') {
+ debug_hung_script = NULL;
+ } else {
+ debug_hung_script = value;
+ }
}
- /* Skip temporary files left behind by emacs */
- if (de->d_name[namelen-1] == '~') {
- return 0;
+ if (debug_hung_script != NULL) {
+ ectx->debug_hung_script = talloc_strdup(ectx,
+ debug_hung_script);
+ if (ectx->debug_hung_script == NULL) {
+ talloc_free(ectx);
+ return false;
+ }
}
- /* Filename should start with [0-9][0-9]. */
- if (!isdigit(de->d_name[0]) || !isdigit(de->d_name[1]) ||
- de->d_name[2] != '.') {
- return 0;
+ ret = reqid_init(ectx, 1, &ectx->idr);
+ if (ret != 0) {
+ talloc_free(ectx);
+ return false;
}
- if (namelen > MAX_SCRIPT_NAME) {
- return 0;
- }
+ ectx->eventd_pid = -1;
- return 1;
+ *out = ectx;
+ return true;
}
-/* Return true if OK, otherwise set errno. */
-static bool check_executable(const char *dir, const char *name)
-{
- char *full;
- struct stat st;
+/*
+ * Start and stop event daemon
+ */
- full = talloc_asprintf(NULL, "%s/%s", dir, name);
- if (!full)
- return false;
+static bool eventd_client_connect(struct eventd_context *ectx);
+static void eventd_dead_handler(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data);
- if (stat(full, &st) != 0) {
- DEBUG(DEBUG_ERR,("Could not stat event script %s: %s\n",
- full, strerror(errno)));
- talloc_free(full);
- return false;
- }
+int ctdb_start_eventd(struct ctdb_context *ctdb)
+{
+ struct eventd_context *ectx;
+ const char **argv;
+ int fd[2];
+ pid_t pid;
+ int ret, i;
+ bool status;
- if (!(st.st_mode & S_IXUSR)) {
- DEBUG(DEBUG_DEBUG,("Event script %s is not executable. Ignoring this event script\n", full));
- errno = ENOEXEC;
- talloc_free(full);
- return false;
+ if (ctdb->ectx == NULL) {
+ status = eventd_context_init(ctdb, ctdb, &ctdb->ectx);
+ if (! status) {
+ DEBUG(DEBUG_ERR,
+ ("Failed to initialize eventd context\n"));
+ return -1;
+ }
}
- talloc_free(full);
- return true;
-}
+ ectx = ctdb->ectx;
-static struct ctdb_script_list_old *ctdb_get_script_list(
- struct ctdb_context *ctdb,
- TALLOC_CTX *mem_ctx)
-{
- struct dirent **namelist;
- struct ctdb_script_list_old *scripts;
- int i, count;
-
- /* scan all directory entries and insert all valid scripts into the
- tree
- */
- count = scandir(ctdb->event_script_dir, &namelist, script_filter, alphasort);
- if (count == -1) {
- DEBUG(DEBUG_CRIT, ("Failed to read event script directory '%s' - %s\n",
- ctdb->event_script_dir, strerror(errno)));
- return NULL;
+ ret = unlink(ectx->socket);
+ if (ret == 0) {
+ D_WARNING("Removed stale eventd socket %s\n", ectx->socket);
+ } else if (errno != ENOENT) {
+ D_ERR("Failed to remove stale eventd socket %s\n",
+ ectx->socket);
+ return -1;
}
- /* Overallocates by one, but that's OK */
- scripts = talloc_zero_size(mem_ctx,
- sizeof(*scripts)
- + sizeof(scripts->scripts[0]) * count);
- if (scripts == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " Failed to allocate scripts\n"));
- goto done;
- }
- scripts->num_scripts = count;
-
- for (i = 0; i < count; i++) {
- struct ctdb_script *s = &scripts->scripts[i];
-
- if (strlcpy(s->name, namelist[i]->d_name, sizeof(s->name)) >=
- sizeof(s->name)) {
- s->status = -ENAMETOOLONG;
- continue;
- }
+ argv = talloc_array(ectx, const char *, 14);
+ if (argv == NULL) {
+ return -1;
+ }
- s->status = 0;
- if (!check_executable(ctdb->event_script_dir,
- namelist[i]->d_name)) {
- s->status = -errno;
- }
+ argv[0] = ectx->path;
+ argv[1] = "-e";
+ argv[2] = ectx->script_dir;
+ argv[3] = "-s";
+ argv[4] = ectx->socket;
+ argv[5] = "-P";
+ argv[6] = talloc_asprintf(argv, "%d", ctdb->ctdbd_pid);
+ argv[7] = "-l";
+ argv[8] = getenv("CTDB_LOGGING");
+ argv[9] = "-d";
+ argv[10] = debug_level_to_string(DEBUGLEVEL);
+ if (ectx->debug_hung_script == NULL) {
+ argv[11] = NULL;
+ argv[12] = NULL;
+ } else {
+ argv[11] = "-D";
+ argv[12] = ectx->debug_hung_script;
}
+ argv[13] = NULL;
-done:
- for (i=0; ievent_script_dir, current->name);
- tmp[2] = talloc_asprintf(tmp, "%s", ctdb_eventscript_call_names[call]);
- n = 3;
-
- /* Split options into individual arguments */
- opt = talloc_strdup(mem_ctx, options);
- if (opt == NULL) {
- goto failed;
- }
-
- t = strtok_r(opt, " ", &saveptr);
- while (t != NULL) {
- tmp[n++] = talloc_strdup(tmp, t);
- if (n > MAX_HELPER_ARGS) {
- goto args_failed;
- }
- t = strtok_r(NULL, " ", &saveptr);
+ pid = ctdb_fork(ctdb);
+ if (pid == -1) {
+ close(fd[0]);
+ close(fd[1]);
+ return -1;
}
- for (i=0; ieventd_fde = tevent_add_fd(ctdb->ev, ectx, fd[0],
+ TEVENT_FD_READ,
+ eventd_dead_handler, ectx);
+ if (ectx->eventd_fde == NULL) {
+ ctdb_kill(ctdb, pid, SIGKILL);
+ close(fd[0]);
+ return -1;
+ }
+ tevent_fd_set_auto_close(ectx->eventd_fde);
+ ectx->eventd_pid = pid;
-args_failed:
- DEBUG(DEBUG_ERR, (__location__ " too many arguments '%s' to eventscript '%s'\n",
- options, ctdb_eventscript_call_names[call]));
-
-failed:
- if (tmp) {
- talloc_free(tmp);
+ /* Wait to connect to eventd */
+ for (i=0; i<10; i++) {
+ status = eventd_client_connect(ectx);
+ if (status) {
+ break;
+ }
+ sleep(1);
}
- return false;
-}
-
-static void ctdb_event_script_handler(struct tevent_context *ev,
- struct tevent_fd *fde,
- uint16_t flags, void *p);
+ if (! status) {
+ DEBUG(DEBUG_ERR, ("Failed to initialize event daemon\n"));
+ ctdb_stop_eventd(ctdb);
+ return -1;
+ }
-static char helper_prog[PATH_MAX+1] = "";
+ return 0;
+}
-static int fork_child_for_script(struct ctdb_context *ctdb,
- struct ctdb_event_script_state *state)
+static void eventd_dead_handler(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
{
- int r;
- struct tevent_fd *fde;
- struct ctdb_script *current = get_current_script(state);
- int argc;
- const char **argv;
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
- if (!ctdb_set_helper("event helper", helper_prog, sizeof(helper_prog),
- "CTDB_EVENT_HELPER",
- CTDB_HELPER_BINDIR, "ctdb_event_helper")) {
- ctdb_die(ctdb, __location__
- " Unable to set event helper\n");
- }
-
- current->start = timeval_current();
-
- r = pipe(state->fd);
- if (r != 0) {
- DEBUG(DEBUG_ERR, (__location__ " pipe failed for child eventscript process\n"));
- return -errno;
- }
-
- /* Arguments for helper */
- if (!child_helper_args(state, ctdb, state->call, state->options, current,
- state->fd[1], &argc, &argv)) {
- DEBUG(DEBUG_ERR, (__location__ " failed to create arguments for eventscript helper\n"));
- r = -ENOMEM;
- close(state->fd[0]);
- close(state->fd[1]);
- return r;
- }
-
- if (!ctdb_vfork_with_logging(state, ctdb, current->name,
- helper_prog, argc, argv,
- log_event_script_output,
- state, &state->child)) {
- talloc_free(argv);
- r = -errno;
- close(state->fd[0]);
- close(state->fd[1]);
- return r;
- }
+ DEBUG(DEBUG_ERR, ("Eventd went away\n"));
- talloc_free(argv);
+ TALLOC_FREE(ectx->eventd_fde);
+ ectx->eventd_pid = -1;
+}
- close(state->fd[1]);
- set_close_on_exec(state->fd[0]);
+void ctdb_stop_eventd(struct ctdb_context *ctdb)
+{
+ struct eventd_context *ectx = ctdb->ectx;
- /* Set ourselves up to be called when that's done. */
- fde = tevent_add_fd(ctdb->ev, state, state->fd[0], TEVENT_FD_READ,
- ctdb_event_script_handler, state);
- tevent_fd_set_auto_close(fde);
+ if (ectx == NULL) {
+ return;
+ }
- return 0;
+ TALLOC_FREE(ectx->eventd_fde);
+ if (ectx->eventd_pid != -1) {
+ kill(ectx->eventd_pid, SIGTERM);
+ ectx->eventd_pid = -1;
+ }
+ TALLOC_FREE(ctdb->ectx);
}
/*
- Summarize status of this run of scripts.
+ * Connect to event daemon
*/
-static int script_status(struct ctdb_script_list_old *scripts)
+
+struct eventd_client_state {
+ struct eventd_client_state *prev, *next;
+
+ struct eventd_context *ectx;
+ void (*callback)(struct ctdb_event_reply *reply, void *private_data);
+ void *private_data;
+
+ uint32_t reqid;
+ uint8_t *buf;
+ size_t buflen;
+};
+
+static void eventd_client_read(uint8_t *buf, size_t buflen,
+ void *private_data);
+static int eventd_client_state_destructor(struct eventd_client_state *state);
+
+static bool eventd_client_connect(struct eventd_context *ectx)
{
- unsigned int i;
+ int fd;
- for (i = 0; i < scripts->num_scripts; i++) {
- switch (scripts->scripts[i].status) {
- case -ENAMETOOLONG:
- case -ENOENT:
- case -ENOEXEC:
- /* Disabled or missing; that's OK. */
- break;
- case 0:
- /* No problem. */
- break;
- default:
- return scripts->scripts[i].status;
- }
+ if (ectx->queue != NULL) {
+ return true;
}
- /* All OK! */
- return 0;
+ fd = sock_connect(ectx->socket);
+ if (fd == -1) {
+ return false;
+ }
+
+ ectx->queue = sock_queue_setup(ectx, ectx->ev, fd,
+ eventd_client_read, ectx);
+ if (ectx->queue == NULL) {
+ close(fd);
+ return false;
+ }
+
+ return true;
}
-/* called when child is finished */
-static void ctdb_event_script_handler(struct tevent_context *ev,
- struct tevent_fd *fde,
- uint16_t flags, void *p)
+static int eventd_client_write(struct eventd_context *ectx,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *request,
+ void (*callback)(struct ctdb_event_reply *reply,
+ void *private_data),
+ void *private_data)
{
- struct ctdb_event_script_state *state =
- talloc_get_type(p, struct ctdb_event_script_state);
- struct ctdb_script *current = get_current_script(state);
- struct ctdb_context *ctdb = state->ctdb;
- int r, status;
+ struct eventd_client_state *state;
+ int ret;
- if (ctdb == NULL) {
- DEBUG(DEBUG_ERR,("Eventscript finished but ctdb is NULL\n"));
- return;
+ if (! eventd_client_connect(ectx)) {
+ return -1;
}
- r = sys_read(state->fd[0], ¤t->status, sizeof(current->status));
- if (r < 0) {
- current->status = -errno;
- } else if (r == 0) {
- current->status = -EINTR;
- } else if (r != sizeof(current->status)) {
- current->status = -EIO;
- }
-
- current->finished = timeval_current();
- /* valgrind gets overloaded if we run next script as it's still doing
- * post-execution analysis, so kill finished child here. */
- if (ctdb->valgrinding) {
- ctdb_kill(ctdb, state->child, SIGKILL);
- }
-
- state->child = 0;
-
- status = script_status(state->scripts);
-
- /* Aborted or finished all scripts? We're done. */
- if (status != 0 || state->current+1 == state->scripts->num_scripts) {
- if (status != 0) {
- DEBUG(DEBUG_INFO,
- ("Eventscript %s %s finished with state %d\n",
- ctdb_eventscript_call_names[state->call],
- state->options, status));
- }
+ state = talloc_zero(mem_ctx, struct eventd_client_state);
+ if (state == NULL) {
+ return -1;
+ }
+
+ state->ectx = ectx;
+ state->callback = callback;
+ state->private_data = private_data;
+ state->reqid = reqid_new(ectx->idr, state);
+ if (state->reqid == REQID_INVALID) {
talloc_free(state);
- return;
+ return -1;
}
- /* Forget about that old fd. */
- talloc_free(fde);
+ talloc_set_destructor(state, eventd_client_state_destructor);
- /* Next script! */
- state->current++;
- current++;
- current->status = fork_child_for_script(ctdb, state);
- if (current->status != 0) {
- /* This calls the callback. */
+ ctdb_event_header_fill(&request->header, state->reqid);
+ state->buflen = ctdb_event_request_len(request);
+ state->buf = talloc_size(state, state->buflen);
+ if (state->buf == NULL) {
talloc_free(state);
+ return -1;
}
-}
-struct debug_hung_script_state {
- struct ctdb_context *ctdb;
- pid_t child;
- enum ctdb_event call;
-};
+ ret = ctdb_event_request_push(request, state->buf, &state->buflen);
+ if (ret != 0) {
+ talloc_free(state);
+ return -1;
+ }
-static int debug_hung_script_state_destructor(struct debug_hung_script_state *state)
-{
- if (state->child) {
- ctdb_kill(state->ctdb, state->child, SIGKILL);
+ ret = sock_queue_write(ectx->queue, state->buf, state->buflen);
+ if (ret != 0) {
+ talloc_free(state);
+ return -1;
}
- return 0;
-}
-static void debug_hung_script_timeout(struct tevent_context *ev, struct tevent_timer *te,
- struct timeval t, void *p)
-{
- struct debug_hung_script_state *state =
- talloc_get_type(p, struct debug_hung_script_state);
+ DLIST_ADD(ectx->calls, state);
- talloc_free(state);
+ return 0;
}
-static void debug_hung_script_done(struct tevent_context *ev, struct tevent_fd *fde,
- uint16_t flags, void *p)
+static int eventd_client_state_destructor(struct eventd_client_state *state)
{
- struct debug_hung_script_state *state =
- talloc_get_type(p, struct debug_hung_script_state);
+ struct eventd_context *ectx = state->ectx;
- talloc_free(state);
+ reqid_remove(ectx->idr, state->reqid);
+ DLIST_REMOVE(ectx->calls, state);
+ return 0;
}
-static void ctdb_run_debug_hung_script(struct ctdb_context *ctdb, struct debug_hung_script_state *state)
+static void eventd_client_read(uint8_t *buf, size_t buflen,
+ void *private_data)
{
- pid_t pid;
- const char * debug_hung_script = CTDB_ETCDIR "/debug-hung-script.sh";
- int fd[2];
- struct tevent_timer *ttimer;
- struct tevent_fd *tfd;
- const char **argv;
- int i;
+ struct eventd_context *ectx = talloc_get_type_abort(
+ private_data, struct eventd_context);
+ struct eventd_client_state *state;
+ struct ctdb_event_reply *reply;
+ int ret;
- if (pipe(fd) < 0) {
- DEBUG(DEBUG_ERR,("Failed to create pipe fd for debug hung script\n"));
+ if (buf == NULL) {
+ /* connection lost */
+ TALLOC_FREE(ectx->queue);
return;
}
- if (getenv("CTDB_DEBUG_HUNG_SCRIPT") != NULL) {
- debug_hung_script = getenv("CTDB_DEBUG_HUNG_SCRIPT");
+ reply = talloc_zero(ectx, struct ctdb_event_reply);
+ if (reply == NULL) {
+ return;
}
- argv = talloc_array(state, const char *, 5);
-
- argv[0] = talloc_asprintf(argv, "%d", fd[1]);
- argv[1] = talloc_strdup(argv, debug_hung_script);
- argv[2] = talloc_asprintf(argv, "%d", state->child);
- argv[3] = talloc_strdup(argv, ctdb_eventscript_call_names[state->call]);
- argv[4] = NULL;
-
- for (i=0; i<4; i++) {
- if (argv[i] == NULL) {
- close(fd[0]);
- close(fd[1]);
- talloc_free(argv);
- return;
- }
+ ret = ctdb_event_reply_pull(buf, buflen, reply, reply);
+ if (ret != 0) {
+ D_ERR("Invalid packet received, ret=%d\n", ret);
+ talloc_free(reply);
+ return;
}
-
- if (!ctdb_vfork_with_logging(state, ctdb, "Hung-script",
- helper_prog, 5, argv, NULL, NULL, &pid)) {
- DEBUG(DEBUG_ERR,("Failed to fork a child to track hung event script\n"));
- talloc_free(argv);
- close(fd[0]);
- close(fd[1]);
+ if (buflen != reply->header.length) {
+ D_ERR("Packet size mismatch %zu != %"PRIu32"\n",
+ buflen, reply->header.length);
+ talloc_free(reply);
return;
}
- talloc_free(argv);
- close(fd[1]);
-
- ttimer = tevent_add_timer(ctdb->ev, state,
- timeval_current_ofs(ctdb->tunable.script_timeout, 0),
- debug_hung_script_timeout, state);
- if (ttimer == NULL) {
- close(fd[0]);
+ state = reqid_find(ectx->idr, reply->header.reqid,
+ struct eventd_client_state);
+ if (state == NULL) {
+ talloc_free(reply);
return;
}
- tfd = tevent_add_fd(ctdb->ev, state, fd[0], TEVENT_FD_READ,
- debug_hung_script_done, state);
- if (tfd == NULL) {
- talloc_free(ttimer);
- close(fd[0]);
+ if (state->reqid != reply->header.reqid) {
+ talloc_free(reply);
return;
}
- tevent_fd_set_auto_close(tfd);
+
+ state = talloc_steal(reply, state);
+ state->callback(reply, state->private_data);
+ talloc_free(reply);
}
-/* called when child times out */
-static void ctdb_event_script_timeout(struct tevent_context *ev,
- struct tevent_timer *te,
- struct timeval t, void *p)
-{
- struct ctdb_event_script_state *state = talloc_get_type(p, struct ctdb_event_script_state);
- struct ctdb_context *ctdb = state->ctdb;
- struct ctdb_script *current = get_current_script(state);
- struct debug_hung_script_state *debug_state;
-
- DEBUG(DEBUG_ERR,("Event script '%s %s %s' timed out after %.1fs, pid: %d\n",
- current->name, ctdb_eventscript_call_names[state->call], state->options,
- timeval_elapsed(¤t->start),
- state->child));
+/*
+ * Run an event
+ */
- /* ignore timeouts for these events */
- switch (state->call) {
- case CTDB_EVENT_START_RECOVERY:
- case CTDB_EVENT_RECOVERED:
- case CTDB_EVENT_TAKE_IP:
- case CTDB_EVENT_RELEASE_IP:
- state->scripts->scripts[state->current].status = 0;
- DEBUG(DEBUG_ERR,("Ignoring hung script for %s call %d\n", state->options, state->call));
- break;
- default:
- state->scripts->scripts[state->current].status = -ETIME;
+struct eventd_client_run_state {
+ struct eventd_context *ectx;
+ void (*callback)(int result, void *private_data);
+ void *private_data;
+};
+
+static void eventd_client_run_done(struct ctdb_event_reply *reply,
+ void *private_data);
+
+static int eventd_client_run(struct eventd_context *ectx,
+ TALLOC_CTX *mem_ctx,
+ void (*callback)(int result,
+ void *private_data),
+ void *private_data,
+ enum ctdb_event event,
+ const char *arg_str,
+ uint32_t timeout)
+{
+ struct eventd_client_run_state *state;
+ struct ctdb_event_request request;
+ struct ctdb_event_request_run rdata;
+ int ret;
+
+ state = talloc_zero(mem_ctx, struct eventd_client_run_state);
+ if (state == NULL) {
+ return -1;
}
- debug_state = talloc_zero(ctdb, struct debug_hung_script_state);
- if (debug_state == NULL) {
+ state->ectx = ectx;
+ state->callback = callback;
+ state->private_data = private_data;
+
+ rdata.event = event;
+ rdata.timeout = timeout;
+ rdata.arg_str = arg_str;
+
+ request.rdata.command = CTDB_EVENT_COMMAND_RUN;
+ request.rdata.data.run = &rdata;
+
+ ret = eventd_client_write(ectx, state, &request,
+ eventd_client_run_done, state);
+ if (ret != 0) {
talloc_free(state);
- return;
+ return ret;
}
- /* Save information useful for running debug hung script, so
- * eventscript state can be freed.
- */
- debug_state->ctdb = ctdb;
- debug_state->child = state->child;
- debug_state->call = state->call;
+ return 0;
+}
- /* This destructor will actually kill the hung event script */
- talloc_set_destructor(debug_state, debug_hung_script_state_destructor);
+static void eventd_client_run_done(struct ctdb_event_reply *reply,
+ void *private_data)
+{
+ struct eventd_client_run_state *state = talloc_get_type_abort(
+ private_data, struct eventd_client_run_state);
- state->child = 0;
+ state = talloc_steal(state->ectx, state);
+ state->callback(reply->rdata.result, state->private_data);
talloc_free(state);
-
- ctdb_run_debug_hung_script(ctdb, debug_state);
}
/*
- destroy an event script: kill it if ->child != 0.
+ * CTDB event script functions
*/
-static int event_script_destructor(struct ctdb_event_script_state *state)
-{
- int status;
- struct event_script_callback *callback;
- if (state->child) {
- DEBUG(DEBUG_ERR,(__location__ " Sending SIGTERM to child pid:%d\n", state->child));
+int ctdb_event_script_run(struct ctdb_context *ctdb,
+ TALLOC_CTX *mem_ctx,
+ void (*callback)(struct ctdb_context *ctdb,
+ int result, void *private_data),
+ void *private_data,
+ enum ctdb_event event,
+ const char *fmt, va_list ap)
+ PRINTF_ATTRIBUTE(6,0);
- if (ctdb_kill(state->ctdb, state->child, SIGTERM) != 0) {
- DEBUG(DEBUG_ERR,("Failed to kill child process for eventscript, errno %s(%d)\n", strerror(errno), errno));
- }
+struct ctdb_event_script_run_state {
+ struct ctdb_context *ctdb;
+ void (*callback)(struct ctdb_context *ctdb, int result,
+ void *private_data);
+ void *private_data;
+ enum ctdb_event event;
+};
+
+static bool event_allowed_during_recovery(enum ctdb_event event);
+static void ctdb_event_script_run_done(int result, void *private_data);
+static bool check_options(enum ctdb_event call, const char *options);
+
+int ctdb_event_script_run(struct ctdb_context *ctdb,
+ TALLOC_CTX *mem_ctx,
+ void (*callback)(struct ctdb_context *ctdb,
+ int result, void *private_data),
+ void *private_data,
+ enum ctdb_event event,
+ const char *fmt, va_list ap)
+{
+ struct ctdb_event_script_run_state *state;
+ char *arg_str;
+ int ret;
+
+ if ( (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) &&
+ (! event_allowed_during_recovery(event)) ) {
+ DEBUG(DEBUG_ERR,
+ ("Refusing to run event '%s' while in recovery\n",
+ ctdb_eventscript_call_names[event]));
}
- /* If we were the current monitor, we no longer are. */
- if (state->ctdb->current_monitor == state) {
- state->ctdb->current_monitor = NULL;
+ state = talloc_zero(mem_ctx, struct ctdb_event_script_run_state);
+ if (state == NULL) {
+ return -1;
}
- /* Save our scripts as the last executed status, if we have them.
- * See ctdb_event_script_callback_v where we abort monitor event. */
- if (state->scripts) {
- talloc_free(state->ctdb->last_status[state->call]);
- state->ctdb->last_status[state->call] = state->scripts;
- if (state->current < state->ctdb->last_status[state->call]->num_scripts) {
- state->ctdb->last_status[state->call]->num_scripts = state->current+1;
+ state->ctdb = ctdb;
+ state->callback = callback;
+ state->private_data = private_data;
+ state->event = event;
+
+ if (fmt != NULL) {
+ arg_str = talloc_vasprintf(state, fmt, ap);
+ if (arg_str == NULL) {
+ talloc_free(state);
+ return -1;
}
+ } else {
+ arg_str = NULL;
}
- /* Use last status as result, or "OK" if none. */
- if (state->ctdb->last_status[state->call]) {
- status = script_status(state->ctdb->last_status[state->call]);
- } else {
- status = 0;
+ if (! check_options(event, arg_str)) {
+ DEBUG(DEBUG_ERR,
+ ("Bad event script arguments '%s' for '%s'\n",
+ arg_str, ctdb_eventscript_call_names[event]));
+ talloc_free(arg_str);
+ return -1;
}
- state->ctdb->active_events--;
- if (state->ctdb->active_events < 0) {
- ctdb_fatal(state->ctdb, "Active events < 0");
- }
-
- /* This is allowed to free us; talloc will prevent double free anyway,
- * but beware if you call this outside the destructor!
- * the callback hangs off a different context so we walk the list
- * of "active" callbacks until we find the one state points to.
- * if we cant find it it means the callback has been removed.
- */
- for (callback = state->ctdb->script_callbacks; callback != NULL; callback = callback->next) {
- if (callback == state->callback) {
- break;
- }
+ ret = eventd_client_run(ctdb->ectx, state,
+ ctdb_event_script_run_done, state,
+ event, arg_str, ctdb->tunable.script_timeout);
+ if (ret != 0) {
+ talloc_free(state);
+ return ret;
}
- state->callback = NULL;
+ DEBUG(DEBUG_INFO,
+ (__location__ " Running event %s with arguments %s\n",
+ ctdb_eventscript_call_names[event], arg_str));
+
+ talloc_free(arg_str);
+ return 0;
+}
+
+static void ctdb_event_script_run_done(int result, void *private_data)
+{
+ struct ctdb_event_script_run_state *state = talloc_get_type_abort(
+ private_data, struct ctdb_event_script_run_state);
- if (callback) {
- /* Make sure destructor doesn't free itself! */
- talloc_steal(NULL, callback);
- callback->fn(state->ctdb, status, callback->private_data);
- talloc_free(callback);
+ if (result == -ETIME) {
+ switch (state->event) {
+ case CTDB_EVENT_START_RECOVERY:
+ case CTDB_EVENT_RECOVERED:
+ case CTDB_EVENT_TAKE_IP:
+ case CTDB_EVENT_RELEASE_IP:
+ DEBUG(DEBUG_ERR,
+ ("Ignoring hung script for %s event\n",
+ ctdb_eventscript_call_names[state->event]));
+ result = 0;
+ break;
+
+ default:
+ break;
+ }
}
- return 0;
+ state = talloc_steal(state->ctdb, state);
+ state->callback(state->ctdb, result, state->private_data);
+ talloc_free(state);
}
+
static unsigned int count_words(const char *options)
{
unsigned int words = 0;
+ if (options == NULL) {
+ return 0;
+ }
+
options += strspn(options, " \t");
while (*options) {
words++;
@@ -693,226 +681,28 @@
}
}
-static int remove_callback(struct event_script_callback *callback)
-{
- DLIST_REMOVE(callback->ctdb->script_callbacks, callback);
- return 0;
-}
-
-struct schedule_callback_state {
- struct ctdb_context *ctdb;
- void (*callback)(struct ctdb_context *, int, void *);
- void *private_data;
- int status;
- struct tevent_immediate *im;
-};
-
-static void schedule_callback_handler(struct tevent_context *ctx,
- struct tevent_immediate *im,
- void *private_data)
-{
- struct schedule_callback_state *state =
- talloc_get_type_abort(private_data,
- struct schedule_callback_state);
-
- if (state->callback != NULL) {
- state->callback(state->ctdb, state->status,
- state->private_data);
- }
- talloc_free(state);
-}
-
-static int
-schedule_callback_immediate(struct ctdb_context *ctdb,
- void (*callback)(struct ctdb_context *,
- int, void *),
- void *private_data,
- int status)
-{
- struct schedule_callback_state *state;
- struct tevent_immediate *im;
-
- state = talloc_zero(ctdb, struct schedule_callback_state);
- if (state == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- return -1;
- }
- im = tevent_create_immediate(state);
- if (im == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
- talloc_free(state);
- return -1;
- }
-
- state->ctdb = ctdb;
- state->callback = callback;
- state->private_data = private_data;
- state->status = status;
- state->im = im;
-
- tevent_schedule_immediate(im, ctdb->ev,
- schedule_callback_handler, state);
- return 0;
-}
-
-/*
- run the event script in the background, calling the callback when
- finished
- */
-static int ctdb_event_script_callback_v(struct ctdb_context *ctdb,
- const void *mem_ctx,
- void (*callback)(struct ctdb_context *, int, void *),
- void *private_data,
- enum ctdb_event call,
- const char *fmt, va_list ap)
- PRINTF_ATTRIBUTE(6,0);
-
-static int ctdb_event_script_callback_v(struct ctdb_context *ctdb,
- const void *mem_ctx,
- void (*callback)(struct ctdb_context *, int, void *),
- void *private_data,
- enum ctdb_event call,
- const char *fmt, va_list ap)
+/* only specific events are allowed while in recovery */
+static bool event_allowed_during_recovery(enum ctdb_event event)
{
- struct ctdb_event_script_state *state;
-
- if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) {
- /* we guarantee that only some specifically allowed event scripts are run
- while in recovery */
- const enum ctdb_event allowed_calls[] = {
- CTDB_EVENT_INIT,
- CTDB_EVENT_SETUP,
- CTDB_EVENT_START_RECOVERY,
- CTDB_EVENT_SHUTDOWN,
- CTDB_EVENT_RELEASE_IP,
- CTDB_EVENT_IPREALLOCATED,
- };
- int i;
- for (i=0;iactive_events > 0 &&
- ctdb->current_monitor == NULL) {
- if (callback != NULL) {
- callback(ctdb, -ECANCELED, private_data);
- }
- return 0;
- }
-
- /* Kill off any running monitor events to run this event. */
- if (ctdb->current_monitor) {
- struct ctdb_event_script_state *ms = talloc_get_type(ctdb->current_monitor, struct ctdb_event_script_state);
-
- /* Cancel current monitor callback state only if monitoring
- * context ctdb->monitor->monitor_context has not been freed */
- if (ms->callback != NULL && !ctdb_stopped_monitoring(ctdb)) {
- ms->callback->fn(ctdb, -ECANCELED, ms->callback->private_data);
- talloc_free(ms->callback);
- }
-
- /* Discard script status so we don't save to last_status */
- talloc_free(ctdb->current_monitor->scripts);
- ctdb->current_monitor->scripts = NULL;
- talloc_free(ctdb->current_monitor);
- ctdb->current_monitor = NULL;
- }
-
- state = talloc(ctdb->event_script_ctx, struct ctdb_event_script_state);
- CTDB_NO_MEMORY(ctdb, state);
-
- /* The callback isn't done if the context is freed. */
- state->callback = talloc(mem_ctx, struct event_script_callback);
- CTDB_NO_MEMORY(ctdb, state->callback);
- DLIST_ADD(ctdb->script_callbacks, state->callback);
- talloc_set_destructor(state->callback, remove_callback);
- state->callback->ctdb = ctdb;
- state->callback->fn = callback;
- state->callback->private_data = private_data;
-
- state->ctdb = ctdb;
- state->call = call;
- state->options = talloc_vasprintf(state, fmt, ap);
- state->timeout = timeval_set(ctdb->tunable.script_timeout, 0);
- state->scripts = NULL;
- if (state->options == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " could not allocate state->options\n"));
- talloc_free(state);
- return -1;
- }
- if (!check_options(state->call, state->options)) {
- DEBUG(DEBUG_ERR, ("Bad eventscript options '%s' for '%s'\n",
- state->options,
- ctdb_eventscript_call_names[state->call]));
- talloc_free(state);
- return -1;
- }
-
- DEBUG(DEBUG_INFO,(__location__ " Starting eventscript %s %s\n",
- ctdb_eventscript_call_names[state->call],
- state->options));
-
- /* This is not a child of state, since we save it in destructor. */
- state->scripts = ctdb_get_script_list(ctdb, ctdb);
- if (state->scripts == NULL) {
- talloc_free(state);
- return -1;
- }
- state->current = 0;
- state->child = 0;
+ const enum ctdb_event allowed_events[] = {
+ CTDB_EVENT_INIT,
+ CTDB_EVENT_SETUP,
+ CTDB_EVENT_START_RECOVERY,
+ CTDB_EVENT_SHUTDOWN,
+ CTDB_EVENT_RELEASE_IP,
+ CTDB_EVENT_IPREALLOCATED,
+ };
+ int i;
- /* Nothing to do? */
- if (state->scripts->num_scripts == 0) {
- int ret = schedule_callback_immediate(ctdb, callback,
- private_data, 0);
- talloc_free(state);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,
- ("Unable to schedule callback for 0 scripts\n"));
- return 1;
+ for (i = 0; i < ARRAY_SIZE(allowed_events); i++) {
+ if (event == allowed_events[i]) {
+ return true;
}
- return 0;
- }
-
- state->scripts->scripts[0].status = fork_child_for_script(ctdb, state);
- if (state->scripts->scripts[0].status != 0) {
- talloc_free(state);
- return -1;
- }
-
- if (call == CTDB_EVENT_MONITOR) {
- ctdb->current_monitor = state;
}
- ctdb->active_events++;
-
- talloc_set_destructor(state, event_script_destructor);
-
- if (!timeval_is_zero(&state->timeout)) {
- tevent_add_timer(ctdb->ev, state,
- timeval_current_ofs(state->timeout.tv_sec,
- state->timeout.tv_usec),
- ctdb_event_script_timeout, state);
- } else {
- DEBUG(DEBUG_ERR, (__location__ " eventscript %s %s called with no timeout\n",
- ctdb_eventscript_call_names[state->call],
- state->options));
- }
-
- return 0;
+ return false;
}
-
/*
run the event script in the background, calling the callback when
finished. If mem_ctx is freed, callback will never be called.
@@ -928,24 +718,25 @@
int ret;
va_start(ap, fmt);
- ret = ctdb_event_script_callback_v(ctdb, mem_ctx, callback, private_data, call, fmt, ap);
+ ret = ctdb_event_script_run(ctdb, mem_ctx, callback, private_data,
+ call, fmt, ap);
va_end(ap);
return ret;
}
-struct callback_status {
+struct ctdb_event_script_args_state {
bool done;
int status;
};
-/*
- called when ctdb_event_script() finishes
- */
-static void event_script_callback(struct ctdb_context *ctdb, int status, void *private_data)
+static void ctdb_event_script_args_done(struct ctdb_context *ctdb,
+ int status, void *private_data)
{
- struct callback_status *s = (struct callback_status *)private_data;
+ struct ctdb_event_script_args_state *s =
+ (struct ctdb_event_script_args_state *)private_data;
+
s->done = true;
s->status = status;
}
@@ -959,253 +750,41 @@
{
va_list ap;
int ret;
- struct callback_status status = {
+ struct ctdb_event_script_args_state state = {
.status = -1,
.done = false,
};
va_start(ap, fmt);
- ret = ctdb_event_script_callback_v(ctdb, ctdb,
- event_script_callback, &status, call, fmt, ap);
+ ret = ctdb_event_script_run(ctdb, ctdb,
+ ctdb_event_script_args_done, &state,
+ call, fmt, ap);
va_end(ap);
if (ret != 0) {
return ret;
}
- while (status.done == false && tevent_loop_once(ctdb->ev) == 0) /* noop */;
-
- if (status.status == -ETIME) {
- DEBUG(DEBUG_ERR, (__location__ " eventscript for '%s' timed out."
- " Immediately banning ourself for %d seconds\n",
- ctdb_eventscript_call_names[call],
- ctdb->tunable.recovery_ban_period));
+ while (! state.done) {
+ tevent_loop_once(ctdb->ev);
+ }
+ if (state.status == -ETIME) {
/* Don't ban self if CTDB is starting up or shutting down */
if (call != CTDB_EVENT_INIT && call != CTDB_EVENT_SHUTDOWN) {
+ DEBUG(DEBUG_ERR,
+ (__location__ " eventscript for '%s' timed out."
+ " Immediately banning ourself for %d seconds\n",
+ ctdb_eventscript_call_names[call],
+ ctdb->tunable.recovery_ban_period));
ctdb_ban_self(ctdb);
}
}
- return status.status;
+ return state.status;
}
int ctdb_event_script(struct ctdb_context *ctdb, enum ctdb_event call)
{
/* GCC complains about empty format string, so use %s and "". */
- return ctdb_event_script_args(ctdb, call, "%s", "");
-}
-
-struct eventscript_callback_state {
- struct ctdb_req_control_old *c;
-};
-
-/*
- called when a forced eventscript run has finished
- */
-static void run_eventscripts_callback(struct ctdb_context *ctdb, int status,
- void *private_data)
-{
- const char *errmsg = NULL;
-
- struct eventscript_callback_state *state =
- talloc_get_type(private_data, struct eventscript_callback_state);
-
- if (status != 0) {
- if (status == -ECANCELED) {
- DEBUG(DEBUG_WARNING,
- (__location__ " Eventscript cancelled\n"));
- errmsg = "cancelled";
- } else {
- DEBUG(DEBUG_ERR,
- (__location__ " Failed to run eventscripts\n"));
- }
- }
-
- ctdb_request_control_reply(ctdb, state->c, NULL, status, errmsg);
- /* This will free the struct ctdb_event_script_state we are in! */
- talloc_free(state);
- return;
-}
-
-
-/* Returns rest of string, or NULL if no match. */
-static const char *get_call(const char *p, enum ctdb_event *call)
-{
- unsigned int len;
-
- /* Skip any initial whitespace. */
- p += strspn(p, " \t");
-
- /* See if we match any. */
- for (*call = 0; *call < CTDB_EVENT_MAX; (*call)++) {
- len = strlen(ctdb_eventscript_call_names[*call]);
- if (strncmp(p, ctdb_eventscript_call_names[*call], len) == 0) {
- /* If end of string or whitespace, we're done. */
- if (strcspn(p + len, " \t") == 0) {
- return p + len;
- }
- }
- }
- return NULL;
-}
-
-/*
- A control to force running of the eventscripts from the ctdb client tool
-*/
-int32_t ctdb_run_eventscripts(struct ctdb_context *ctdb,
- struct ctdb_req_control_old *c,
- TDB_DATA indata, bool *async_reply)
-{
- int ret;
- struct eventscript_callback_state *state;
- const char *options;
- enum ctdb_event call;
-
- /* Figure out what call they want. */
- options = get_call((const char *)indata.dptr, &call);
- if (!options) {
- DEBUG(DEBUG_ERR, (__location__ " Invalid event name \"%s\"\n", (const char *)indata.dptr));
- return -1;
- }
-
- if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) {
- DEBUG(DEBUG_ERR, (__location__ " Aborted running eventscript \"%s\" while in RECOVERY mode\n", indata.dptr));
- return -1;
- }
-
- state = talloc(ctdb->event_script_ctx, struct eventscript_callback_state);
- CTDB_NO_MEMORY(ctdb, state);
-
- state->c = NULL;
-
- DEBUG(DEBUG_NOTICE,("Running eventscripts with arguments %s\n", indata.dptr));
-
- ret = ctdb_event_script_callback(ctdb,
- ctdb, run_eventscripts_callback, state,
- call, "%s", options);
-
- if (ret != 0) {
- DEBUG(DEBUG_ERR,(__location__ " Failed to run eventscripts with arguments %s\n", indata.dptr));
- talloc_free(state);
- return -1;
- }
-
- /* tell ctdb_control.c that we will be replying asynchronously */
- *async_reply = true;
- state->c = talloc_steal(state, c);
- return 0;
-}
-
-
-
-int32_t ctdb_control_enable_script(struct ctdb_context *ctdb, TDB_DATA indata)
-{
- const char *script;
- struct stat st;
- char *filename;
- TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
-
- script = (char *)indata.dptr;
- if (indata.dsize == 0) {
- DEBUG(DEBUG_ERR,(__location__ " No script specified.\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- if (indata.dptr[indata.dsize - 1] != '\0') {
- DEBUG(DEBUG_ERR,(__location__ " String is not null terminated.\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- if (index(script,'/') != NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Script name contains '/'. Failed to enable script %s\n", script));
- talloc_free(tmp_ctx);
- return -1;
- }
-
-
- if (stat(ctdb->event_script_dir, &st) != 0 &&
- errno == ENOENT) {
- DEBUG(DEBUG_CRIT,("No event script directory found at '%s'\n", ctdb->event_script_dir));
- talloc_free(tmp_ctx);
- return -1;
- }
-
-
- filename = talloc_asprintf(tmp_ctx, "%s/%s", ctdb->event_script_dir, script);
- if (filename == NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Failed to create script path\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- if (stat(filename, &st) != 0) {
- DEBUG(DEBUG_ERR,("Could not stat event script %s. Failed to enable script.\n", filename));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- if (chmod(filename, st.st_mode | S_IXUSR) == -1) {
- DEBUG(DEBUG_ERR,("Could not chmod %s. Failed to enable script.\n", filename));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- talloc_free(tmp_ctx);
- return 0;
-}
-
-int32_t ctdb_control_disable_script(struct ctdb_context *ctdb, TDB_DATA indata)
-{
- const char *script;
- struct stat st;
- char *filename;
- TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
-
- script = (char *)indata.dptr;
- if (indata.dsize == 0) {
- DEBUG(DEBUG_ERR,(__location__ " No script specified.\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- if (indata.dptr[indata.dsize - 1] != '\0') {
- DEBUG(DEBUG_ERR,(__location__ " String is not null terminated.\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
- if (index(script,'/') != NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Script name contains '/'. Failed to disable script %s\n", script));
- talloc_free(tmp_ctx);
- return -1;
- }
-
-
- if (stat(ctdb->event_script_dir, &st) != 0 &&
- errno == ENOENT) {
- DEBUG(DEBUG_CRIT,("No event script directory found at '%s'\n", ctdb->event_script_dir));
- talloc_free(tmp_ctx);
- return -1;
- }
-
-
- filename = talloc_asprintf(tmp_ctx, "%s/%s", ctdb->event_script_dir, script);
- if (filename == NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Failed to create script path\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- if (stat(filename, &st) != 0) {
- DEBUG(DEBUG_ERR,("Could not stat event script %s. Failed to disable script.\n", filename));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- if (chmod(filename, st.st_mode & ~(S_IXUSR|S_IXGRP|S_IXOTH)) == -1) {
- DEBUG(DEBUG_ERR,("Could not chmod %s. Failed to disable script.\n", filename));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- talloc_free(tmp_ctx);
- return 0;
+ return ctdb_event_script_args(ctdb, call, NULL);
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc.c samba-4.6.5+dfsg/ctdb/server/ipalloc.c
--- samba-4.5.8+dfsg/ctdb/server/ipalloc.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ipalloc.c 2017-01-11 07:55:14.000000000 +0000
@@ -29,6 +29,8 @@
#include "common/logging.h"
#include "common/rb_tree.h"
+#include "protocol/protocol_api.h"
+
#include "server/ipalloc_private.h"
/* Initialise main ipalloc state and sub-structures */
@@ -36,7 +38,9 @@
ipalloc_state_init(TALLOC_CTX *mem_ctx,
uint32_t num_nodes,
enum ipalloc_algorithm algorithm,
+ bool no_ip_takeover,
bool no_ip_failback,
+ bool no_ip_host_on_all_disabled,
uint32_t *force_rebalance_nodes)
{
struct ipalloc_state *ipalloc_state =
@@ -48,14 +52,6 @@
ipalloc_state->num = num_nodes;
- ipalloc_state->noiptakeover =
- talloc_zero_array(ipalloc_state,
- bool,
- ipalloc_state->num);
- if (ipalloc_state->noiptakeover == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " Out of memory\n"));
- goto fail;
- }
ipalloc_state->noiphost =
talloc_zero_array(ipalloc_state,
bool,
@@ -66,7 +62,9 @@
}
ipalloc_state->algorithm = algorithm;
+ ipalloc_state->no_ip_takeover = no_ip_takeover;
ipalloc_state->no_ip_failback = no_ip_failback;
+ ipalloc_state->no_ip_host_on_all_disabled = no_ip_host_on_all_disabled;
ipalloc_state->force_rebalance_nodes = force_rebalance_nodes;
return ipalloc_state;
@@ -160,6 +158,37 @@
return ip_list;
}
+static bool populate_bitmap(struct ipalloc_state *ipalloc_state)
+{
+ struct public_ip_list *ip = NULL;
+ int i, j;
+
+ for (ip = ipalloc_state->all_ips; ip != NULL; ip = ip->next) {
+
+ ip->available_on = talloc_zero_array(ip, bool,
+ ipalloc_state->num);
+ if (ip->available_on == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < ipalloc_state->num; i++) {
+ struct ctdb_public_ip_list *avail =
+ &ipalloc_state->available_public_ips[i];
+
+ /* Check to see if "ip" is available on node "i" */
+ for (j = 0; j < avail->num; j++) {
+ if (ctdb_sock_addr_same_ip(
+ &ip->addr, &avail->ip[j].addr)) {
+ ip->available_on[i] = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
static bool all_nodes_are_disabled(struct ctdb_node_map *nodemap)
{
int i;
@@ -177,7 +206,6 @@
/* Set internal flags for IP allocation:
* Clear ip flags
- * Set NOIPTAKOVER ip flags from per-node NoIPTakeover tunable
* Set NOIPHOST ip flag for each INACTIVE node
* if all nodes are disabled:
* Set NOIPHOST ip flags from per-node NoIPHostOnAllDisabled tunable
@@ -185,39 +213,25 @@
* Set NOIPHOST ip flags for disabled nodes
*/
void ipalloc_set_node_flags(struct ipalloc_state *ipalloc_state,
- struct ctdb_node_map *nodemap,
- uint32_t *tval_noiptakeover,
- uint32_t *tval_noiphostonalldisabled)
+ struct ctdb_node_map *nodemap)
{
int i;
+ bool all_disabled = all_nodes_are_disabled(nodemap);
for (i=0;inum;i++) {
- /* Can not take IPs on node with NoIPTakeover set */
- if (tval_noiptakeover[i] != 0) {
- ipalloc_state->noiptakeover[i] = true;
- }
-
/* Can not host IPs on INACTIVE node */
if (nodemap->node[i].flags & NODE_FLAGS_INACTIVE) {
ipalloc_state->noiphost[i] = true;
}
- }
- if (all_nodes_are_disabled(nodemap)) {
- /* If all nodes are disabled, can not host IPs on node
- * with NoIPHostOnAllDisabled set
+ /* If node is disabled then it can only host IPs if
+ * all nodes are disabled and NoIPHostOnAllDisabled is
+ * unset
*/
- for (i=0;inum;i++) {
- if (tval_noiphostonalldisabled[i] != 0) {
- ipalloc_state->noiphost[i] = true;
- }
- }
- } else {
- /* If some nodes are not disabled, then can not host
- * IPs on DISABLED node
- */
- for (i=0;inum;i++) {
- if (nodemap->node[i].flags & NODE_FLAGS_DISABLED) {
+ if (nodemap->node[i].flags & NODE_FLAGS_DISABLED) {
+ if (!(all_disabled &&
+ ipalloc_state->no_ip_host_on_all_disabled == 0)) {
+
ipalloc_state->noiphost[i] = true;
}
}
@@ -283,6 +297,10 @@
return NULL;
}
+ if (!populate_bitmap(ipalloc_state)) {
+ return NULL;
+ }
+
switch (ipalloc_state->algorithm) {
case IPALLOC_LCP2:
ret = ipalloc_lcp2(ipalloc_state);
diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_common.c samba-4.6.5+dfsg/ctdb/server/ipalloc_common.c
--- samba-4.5.8+dfsg/ctdb/server/ipalloc_common.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ipalloc_common.c 2017-01-11 07:55:14.000000000 +0000
@@ -61,33 +61,18 @@
int32_t pnn,
struct public_ip_list *ip)
{
- struct ctdb_public_ip_list *public_ips;
- int i;
-
if (ipalloc_state->noiphost[pnn]) {
return false;
}
- if (ipalloc_state->available_public_ips == NULL) {
- return false;
- }
-
- public_ips = &ipalloc_state->available_public_ips[pnn];
-
- for (i=0; inum; i++) {
- if (ctdb_sock_addr_same(&ip->addr, &public_ips->ip[i].addr)) {
- /* yes, this node can serve this public ip */
- return true;
- }
- }
- return false;
+ return ip->available_on[pnn];
}
bool can_node_takeover_ip(struct ipalloc_state *ipalloc_state,
int32_t pnn,
struct public_ip_list *ip)
{
- if (ipalloc_state->noiptakeover[pnn]) {
+ if (ipalloc_state->no_ip_takeover) {
return false;
}
diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_deterministic.c samba-4.6.5+dfsg/ctdb/server/ipalloc_deterministic.c
--- samba-4.5.8+dfsg/ctdb/server/ipalloc_deterministic.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ipalloc_deterministic.c 2017-01-11 07:55:14.000000000 +0000
@@ -49,7 +49,8 @@
* back IPs to their "home" node.
*/
if (ipalloc_state->no_ip_failback) {
- DEBUG(DEBUG_WARNING, ("WARNING: 'NoIPFailback' set but ignored - incompatible with 'DeterministicIPs\n"));
+ D_WARNING("WARNING: 'NoIPFailback' set but ignored - "
+ "incompatible with 'Deterministic IPs\n");
}
unassign_unsuitable_ips(ipalloc_state);
diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc.h samba-4.6.5+dfsg/ctdb/server/ipalloc.h
--- samba-4.5.8+dfsg/ctdb/server/ipalloc.h 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ipalloc.h 2017-01-11 07:55:14.000000000 +0000
@@ -31,6 +31,7 @@
struct public_ip_list *next;
uint32_t pnn;
ctdb_sock_addr addr;
+ bool *available_on;
};
#define IP_KEYLEN 4
@@ -48,13 +49,13 @@
struct ipalloc_state * ipalloc_state_init(TALLOC_CTX *mem_ctx,
uint32_t num_nodes,
enum ipalloc_algorithm algorithm,
+ bool no_ip_takeover,
bool no_ip_failback,
+ bool no_ip_host_on_all_disabled,
uint32_t *force_rebalance_nodes);
void ipalloc_set_node_flags(struct ipalloc_state *ipalloc_state,
- struct ctdb_node_map *nodemap,
- uint32_t *tval_noiptakeover,
- uint32_t *tval_noiphostonalldisabled);
+ struct ctdb_node_map *nodemap);
void ipalloc_set_public_ips(struct ipalloc_state *ipalloc_state,
struct ctdb_public_ip_list *known_ips,
diff -Nru samba-4.5.8+dfsg/ctdb/server/ipalloc_private.h samba-4.6.5+dfsg/ctdb/server/ipalloc_private.h
--- samba-4.5.8+dfsg/ctdb/server/ipalloc_private.h 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/server/ipalloc_private.h 2017-01-11 07:55:14.000000000 +0000
@@ -32,12 +32,13 @@
/* Arrays with data for each node */
struct ctdb_public_ip_list *available_public_ips;
struct ctdb_public_ip_list *known_public_ips;
- bool *noiptakeover;
bool *noiphost;
struct public_ip_list *all_ips;
enum ipalloc_algorithm algorithm;
bool no_ip_failback;
+ bool no_ip_takeover;
+ bool no_ip_host_on_all_disabled;
uint32_t *force_rebalance_nodes;
};
diff -Nru samba-4.5.8+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh samba-4.6.5+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh
--- samba-4.5.8+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/complex/35_cifs_external_tickle.sh 2017-01-11 07:55:14.000000000 +0000
@@ -55,15 +55,15 @@
my_exit_hook ()
{
- onnode -q all $CTDB enablescript "10.interface"
- onnode -q all $CTDB disablescript "10.external"
+ onnode -q all $CTDB event script enable "10.interface"
+ onnode -q all $CTDB event script disable "10.external"
}
ctdb_test_exit_hook_add my_exit_hook
echo "Disable 10.interface on all nodes"
-try_command_on_node all $CTDB disablescript 10.interface
+try_command_on_node all $CTDB event script disable 10.interface
echo "Enable 10.external on all nodes"
-try_command_on_node all $CTDB enablescript 10.external
+try_command_on_node all $CTDB event script enable 10.external
test_port=445
diff -Nru samba-4.5.8+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh samba-4.6.5+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh
--- samba-4.5.8+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/complex/60_rogueip_releaseip.sh 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+test_info()
+{
+ cat <] .*sleep+.*
---- ctdb scriptstatus monitor: ----
-[0-9]* scripts were executed last monitor cycle
-99\\.timeout *Status:TIMEDOUT.*
- *OUTPUT:sleeping for [0-9]* seconds\\.\\.\\.
+99\\.timeout *TIMEDOUT.*
+ *OUTPUT: sleeping for [0-9]* seconds\\.\\.\\.
EOF
diff -Nru samba-4.5.8+dfsg/ctdb/tests/cunit/protocol_test_002.sh samba-4.6.5+dfsg/ctdb/tests/cunit/protocol_test_002.sh
--- samba-4.5.8+dfsg/ctdb/tests/cunit/protocol_test_002.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/cunit/protocol_test_002.sh 2017-01-11 07:55:14.000000000 +0000
@@ -11,6 +11,15 @@
echo
)
+last_command=5
+
+command_output=$(
+ for i in $(seq 1 $last_command) ; do
+ echo -n "$i.. "
+ done
+ echo
+)
+
output=$(
echo "ctdb_req_header"
echo "ctdb_req_call"
@@ -27,6 +36,15 @@
echo "ctdb_reply_control"
echo "$control_output"
echo "ctdb_req_message"
+ echo "ctdb_event_header"
+ echo "ctdb_event_request_data"
+ echo "$command_output"
+ echo "ctdb_event_reply_data"
+ echo "$command_output"
+ echo "ctdb_event_request"
+ echo "$command_output"
+ echo "ctdb_event_reply"
+ echo "$command_output"
)
ok "$output"
diff -Nru samba-4.5.8+dfsg/ctdb/tests/cunit/run_proc_001.sh samba-4.6.5+dfsg/ctdb/tests/cunit/run_proc_001.sh
--- samba-4.5.8+dfsg/ctdb/tests/cunit/run_proc_001.sh 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/cunit/run_proc_001.sh 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,140 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+# Invalid path
+ok < "$prog" < "$prog" < "$prog" <"$output" 2>&1
+echo hello
+EOF
+
+ok < "$prog" < "$prog" < "$prog" < "$prog" < "$pidfile"
+sleep 10
+EOF
+
+ok < "$eventd_scriptdir/a.sh" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug"
+EOF
+chmod +x "$eventd_scriptdir/debug.sh"
+
+setup_eventd "$eventd_scriptdir/debug.sh"
+
+result_filter ()
+{
+ _pid="[0-9][0-9]*"
+ sed -e "s| ${_pid}| PID|"
+}
+
+required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug"
+EOF
+chmod +x "$eventd_scriptdir/debug.sh"
+
+setup_eventd "$eventd_scriptdir/debug.sh"
+
+required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" <"$eventd_debug" 2>&1
+
+ctdb_event "$eventd_socket" status monitor lastrun
+EOF
+chmod +x "$eventd_scriptdir/debug.sh"
+
+setup_eventd "$eventd_scriptdir/debug.sh"
+
+required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/debug.sh" < "$eventd_debug"
+EOF
+chmod +x "$eventd_scriptdir/debug.sh"
+
+setup_eventd "$eventd_scriptdir/debug.sh"
+
+result_filter()
+{
+ _pid="[0-9][0-9]*"
+ sed -e "s|${_pid}|PID|"
+}
+
+required_result 62 < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/02.test" < "$eventd_scriptdir/03.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/02.test" < "$eventd_scriptdir/03.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" < "$eventd_scriptdir/01.test" </dev/null || echo)
+ if [ -n "$pid" ] ; then
+ kill $pid || true
+ rm -f "$eventd_pidfile"
+ fi
+ rm -f "$eventd_socket"
+ rm -f "$eventd_logfile"
+ rm -f "$eventd_debug"
+ rm -rf "$eventd_scriptdir"
+}
+
+setup_eventd ()
+{
+ debug "Setting up eventd"
+
+ if [ -n "$1" ]; then
+ extra_args="-D $1"
+ fi
+
+ $VALGRIND ctdb_eventd -s "$eventd_socket" \
+ -p "$eventd_pidfile" \
+ -e "$eventd_scriptdir" \
+ -l "file:" -d "DEBUG" $extra_args 2>&1 | tee "$eventd_logfile" &
+ # Wait till eventd is running
+ while [ ! -S "$eventd_socket" ] ; do
+ sleep 1
+ done
+
+ test_cleanup cleanup_eventd
+}
+
+simple_test_background ()
+{
+ background_log=$(mktemp --tmpdir="$TEST_VAR_DIR")
+ background_status=$(mktemp --tmpdir="$TEST_VAR_DIR")
+ background_running=1
+
+ (
+ (unit_test ctdb_event "$eventd_socket" "$@") \
+ > "$background_log" 2>&1
+ echo $? > "$background_status"
+ ) &
+ background_pid=$!
+}
+
+background_wait ()
+{
+ [ -n "$background_running" ] || return
+
+ count=0
+ while [ ! -s "$background_status" -a $count -lt 30 ] ; do
+ count=$(( $count + 1 ))
+ sleep 1
+ done
+
+ if [ ! -s "$background_status" ] ; then
+ kill -9 "$background_pid"
+ echo TIMEOUT > "$background_status"
+ fi
+}
+
+background_output ()
+{
+ [ -n "$background_running" ] || return
+
+ bg_status=$(cat "$background_status")
+ rm -f "$background_status"
+ echo "--- Background ---"
+ if [ "$bg_status" = "TIMEOUT" ] ; then
+ echo "Background process did not complete"
+ bg_status=1
+ else
+ cat "$background_log"
+ rm -f "$background_log"
+ fi
+ echo "--- Background ---"
+ unset background_running
+ [ $bg_status -eq 0 ] || exit $bg_status
+}
+
+simple_test ()
+{
+ (unit_test ctdb_event "$eventd_socket" "$@")
+ status=$?
+
+ background_wait
+ background_output
+
+ [ $status -eq 0 ] || exit $status
+}
+
+result_filter ()
+{
+ _duration="\<[0-9][0-9]*\.[0-9][0-9][0-9]\>"
+ _day="\(Mon\|Tue\|Wed\|Thu\|Fri\|Sat\|Sun\)"
+ _month="\(Jan\|Feb\|Mar\|Apr\|May\|Jun\|Jul\|Aug\|Sep\|Oct\|Nov\|Dec\)"
+ _date="\( [0-9]\|[0-9][0-9]\)"
+ _time="[0-9][0-9]:[0-9][0-9]:[0-9][0-9]"
+ _year="[0-9][0-9][0-9][0-9]"
+ _datetime="${_day} ${_month} ${_date} ${_time} ${_year}"
+ _pid="[0-9][0-9]*"
+ sed -e "s#${_duration}#DURATION#" \
+ -e "s#${_datetime}#DATETIME#" \
+ -e "s#,${_pid}#,PID#" \
+ -e "s#\[${_pid}\]#[PID]#"
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh
--- samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.monitor.107.sh 2017-01-11 07:55:14.000000000 +0000
@@ -4,7 +4,7 @@
define_test "port 139 down, default tcp checker, debug"
-export CTDB_SCRIPT_DEBUGLEVEL=4
+export CTDB_SCRIPT_DEBUGLEVEL=DEBUG
setup_samba
tcp_port_down 139
diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh
--- samba-4.5.8+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/50.samba.shutdown.001.sh 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+define_test "shutdown, simple"
+
+setup_samba
+
+ok < replay error"
-
-setup_nfs
-
-public_address=$(ctdb_get_1_public_address)
-
-err="foo: bar error occurred"
-
-ok_null
-
-simple_test_event "takeip" $public_address
-
-ctdb_fake_scriptstatus 1 "ERROR" "$err"
-
-eventscript_call ctdb_reconfigure_take_lock
-
-required_result 1 < reconfigure, replay timed out"
-
-setup_nfs
-
-public_address=$(ctdb_get_1_public_address)
-
-err="waiting, waiting..."
-
-ok_null
-
-simple_test_event "takeip" $public_address
-
-ctdb_fake_scriptstatus -62 "TIMEDOUT" "$err"
-
-eventscript_call ctdb_reconfigure_take_lock
-
-required_result 1 < reconfigure, replay disabled"
-
-setup_nfs
-
-public_address=$(ctdb_get_1_public_address)
-
-err=""
-
-ok_null
-
-simple_test_event "takeip" $public_address
-
-ctdb_fake_scriptstatus -8 "DISABLED" "$err"
-
-eventscript_call ctdb_reconfigure_take_lock
-
-ok </dev/null 2>&1 || _failed=true
- echo "Tickle TCP connection $dest $src"
- $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true
- done
+ # Get connections, both directions
+ _conns=$(get_tcp_connections_for_ip "$_ip" | \
+ awk '{ print $1, $2 ; print $2, $1 }')
- if $_failed ; then
- echo "Failed to send tickle control"
- fi
- }
+ echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }'
+ echo "$_conns" | ctdb tickle
}
get_tcp_connections_for_ip ()
@@ -862,139 +853,6 @@
:
}
-ctdb_reconfigure_take_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
- mkdir -p "${_lock%/*}" # dirname
- touch "$_lock"
-
- (
- flock 9
- # This is overkill but will work if we need to extend
- # this to allow certain events to run multiple times
- # in parallel (e.g. takeip) and write multiple PIDs to
- # the file.
- {
- read _locker_event
- if [ -n "$_locker_event" ] ; then
- while read _pid ; do
- if [ -n "$_pid" -a "$_pid" != $$ ] && \
- kill -0 "$_pid" 2>/dev/null ; then
- exit 1
- fi
- done
- fi
- } <"$_lock"
-
- printf "%s\n%s\n" "$event_name" $$ >"$_lock"
- exit 0
- ) 9>"${_lock}.flock"
-}
-
-ctdb_reconfigure_release_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
-
- rm -f "$_lock"
-}
-
-ctdb_replay_monitor_status ()
-{
- echo "Replaying previous status for this script due to reconfigure..."
- # Leading separator ('|') is missing in some versions...
- _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|")
- # Output looks like this:
- # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar|
- # This is the cheapest way of getting fields in the middle.
- # Intentional word splitting here
- # shellcheck disable=SC2046,2086
- set -- $(IFS="|" ; echo $_out)
- _code="$3"
- _status="$4"
- # The error output field can include colons so we'll try to
- # preserve them. The weak checking at the beginning tries to make
- # this work for both broken (no leading '|') and fixed output.
- _out="${_out%|}"
- _err_out="${_out#*monitor|${script_name}|*|*|*|*|}"
- case "$_status" in
- OK) : ;; # Do nothing special.
- TIMEDOUT)
- # Recast this as an error, since we can't exit with the
- # correct negative number.
- _code=1
- _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- DISABLED)
- # Recast this as an OK, since we can't exit with the
- # correct negative number.
- _code=0
- _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- *) : ;; # Must be ERROR, do nothing special.
- esac
- if [ -n "$_err_out" ] ; then
- echo "$_err_out"
- fi
- exit $_code
-}
-
-ctdb_service_check_reconfigure ()
-{
- assert_service_name
-
- # We only care about some events in this function. For others we
- # return now.
- case "$event_name" in
- monitor|ipreallocated|reconfigure) : ;;
- *) return 0 ;;
- esac
-
- if ctdb_reconfigure_take_lock ; then
- # No events covered by this function are running, so proceed
- # with gay abandon.
- case "$event_name" in
- reconfigure)
- (ctdb_service_reconfigure)
- exit $?
- ;;
- ipreallocated)
- if ctdb_service_needs_reconfigure ; then
- ctdb_service_reconfigure
- fi
- ;;
- esac
-
- ctdb_reconfigure_release_lock
- else
- # Somebody else is running an event we don't want to collide
- # with. We proceed with caution.
- case "$event_name" in
- reconfigure)
- # Tell whoever called us to retry.
- exit 2
- ;;
- ipreallocated)
- # Defer any scheduled reconfigure and just run the
- # rest of the ipreallocated event, as per the
- # eventscript. There's an assumption here that the
- # event doesn't depend on any scheduled reconfigure.
- # This is true in the current code.
- return 0
- ;;
- monitor)
- # There is most likely a reconfigure in progress so
- # the service is possibly unstable. As above, we
- # defer any scheduled reconfigured. We also replay
- # the previous monitor status since that's the best
- # information we have.
- ctdb_replay_monitor_status
- ;;
- esac
- fi
-}
-
##################################################################
# Does CTDB manage this service? - and associated auto-start/stop
@@ -1276,16 +1134,12 @@
sort >"$_my_tickles"
# Add tickles for connections that we haven't already got tickles for
- comm -23 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB addtickle "$_src" "$_dst"
- done
+ comm -23 "$_my_connections" "$_my_tickles" | \
+ $CTDB addtickle
# Remove tickles for connections that are no longer there
- comm -13 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB deltickle "$_src" "$_dst"
- done
+ comm -13 "$_my_connections" "$_my_tickles" | \
+ $CTDB deltickle
rm -f "$_my_connections" "$_my_tickles"
diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/scripts/local.sh samba-4.6.5+dfsg/ctdb/tests/eventscripts/scripts/local.sh
--- samba-4.5.8+dfsg/ctdb/tests/eventscripts/scripts/local.sh 2016-12-05 08:18:44.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/scripts/local.sh 2017-01-11 07:55:14.000000000 +0000
@@ -839,6 +839,23 @@
fi
}
+samba_setup_fake_threads ()
+{
+ export FAKE_SMBD_THREAD_PIDS="$*"
+
+ _nl="
+"
+ _out=""
+ _count=0
+ for _pid ; do
+ [ "$_count" -lt 5 ] || break
+ _t=$(program_stack_trace "smbd" $_pid)
+ _out="${_out:+${_out}${_nl}}${_t}"
+ _count=$((_count + 1))
+ done
+ SAMBA_STACK_TRACES="$_out"
+}
+
setup_winbind ()
{
setup_ctdb
@@ -999,6 +1016,17 @@
esac
}
+program_stack_trace ()
+{
+ _prog="$1"
+ _pid="$2"
+
+ cat <] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
+EOF
+}
+
program_stack_traces ()
{
_prog="$1"
@@ -1008,10 +1036,7 @@
for _pid in ${FAKE_NFSD_THREAD_PIDS:-$FAKE_RPC_THREAD_PIDS} ; do
[ $_count -le $_max ] || break
- cat <] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
-EOF
+ program_stack_trace "$_prog" "$_pid"
_count=$(($_count + 1))
done
}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/ctdb samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/ctdb
--- samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/ctdb 2017-01-30 09:56:26.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/ctdb 2017-01-26 12:19:08.000000000 +0000
@@ -59,6 +59,55 @@
touch "$tickles_file"
}
+ctdb_gettickles ()
+{
+ _ip="$1"
+ _port="$2"
+
+ setup_tickles
+
+ echo "|source ip|port|destination ip|port|"
+ while read _src _dst ; do
+ if [ -z "$_ip" -o "$_ip" = "${_dst%:*}" ] ; then
+ if [ -z "$_port" -o "$_port" = "${_dst##*:}" ] ; then
+ echo "|${_src%:*}|${_src##*:}|${_dst%:*}|${_dst##*:}|"
+ fi
+ fi
+ done <"$tickles_file"
+}
+
+ctdb_addtickle ()
+{
+ _src="$1"
+ _dst="$2"
+
+ setup_tickles
+
+ if [ -n "$_dst" ] ; then
+ echo "${_src} ${_dst}" >>"$tickles_file"
+ else
+ cat >>"$tickles_file"
+ fi
+}
+
+ctdb_deltickle ()
+{
+ _src="$1"
+ _dst="$2"
+
+ setup_tickles
+
+ if [ -n "$_dst" ] ; then
+ _t=$(grep -F -v "${_src} $${_dst}" "$tickles_file")
+ else
+ _t=$(cat "$tickles_file")
+ while read _src _dst ; do
+ _t=$(echo "$_t" | grep -F -v "${_src} ${_dst}")
+ done
+ fi
+ echo "$_t" >"$tickles_file"
+}
+
parse_nodespec ()
{
if [ "$nodespec" = "all" ] ; then
@@ -125,7 +174,7 @@
fi
_flags="${_flags}${_flags:+,}${_this}"
done
- CTDB_TEST_LOGLEVEL=2 \
+ CTDB_TEST_LOGLEVEL=NOTICE \
"ctdb_takeover_tests" \
"ipalloc" "$_flags" <"$FAKE_CTDB_IP_LAYOUT" |
sort >"$_t"
@@ -358,22 +407,9 @@
######################################################################
case "$1" in
- gettickles)
- setup_tickles
- echo "|source ip|port|destination ip|port|"
- while read src dst ; do
- echo "|${src}|${dst}|"
- done <"$tickles_file"
- ;;
- addtickle)
- setup_tickles
- echo "$2 $3" >>"$tickles_file"
- ;;
- deltickle)
- setup_tickles
- _t=$(grep -F -v "$2 $3" "$tickles_file")
- echo "$_t" >"$tickles_file"
- ;;
+ gettickles) shift ; ctdb_gettickles "$@" ;;
+ addtickle) shift ; ctdb_addtickle "$@" ;;
+ deltickle) shift ; ctdb_deltickle "$@" ;;
pstore) ctdb_pstore "$@" ;;
pdelete) ctdb_pdelete "$@" ;;
pfetch) ctdb_pfetch "$@" ;;
diff -Nru samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/pidof samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/pidof
--- samba-4.5.8+dfsg/ctdb/tests/eventscripts/stubs/pidof 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/eventscripts/stubs/pidof 2017-01-11 07:55:14.000000000 +0000
@@ -1,12 +1,15 @@
#!/bin/sh
case "$1" in
- nfsd)
+nfsd)
echo "$FAKE_NFSD_THREAD_PIDS"
;;
- rpc.statd|rpc.rquotad|rpc.mountd)
+rpc.statd|rpc.rquotad|rpc.mountd)
echo "$FAKE_RPC_THREAD_PIDS"
;;
+smbd)
+ echo "$FAKE_SMBD_THREAD_PIDS"
+ ;;
*)
echo "pidof: \"$1\" not implemented"
exit 1
diff -Nru samba-4.5.8+dfsg/ctdb/tests/onnode/functions samba-4.6.5+dfsg/ctdb/tests/onnode/functions
--- samba-4.5.8+dfsg/ctdb/tests/onnode/functions 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/onnode/functions 2017-01-11 07:55:14.000000000 +0000
@@ -101,7 +101,7 @@
# configuration file.
debug ()
{
- if [ "${CTDB_SCRIPT_DEBUGLEVEL:-2}" -ge 4 ] ; then
+ if [ "${CTDB_SCRIPT_DEBUGLEVEL:-NOTICE}" = "DEBUG" ] ; then
# If there are arguments then echo them. Otherwise expect to
# use stdin, which allows us to pass lots of debug using a
# here document.
@@ -520,21 +520,12 @@
{
_ip="$1"
- get_tcp_connections_for_ip "$_ip" |
- {
- _failed=false
-
- while read dest src; do
- echo "Tickle TCP connection $src $dest"
- $CTDB tickle "$src" "$dest" >/dev/null 2>&1 || _failed=true
- echo "Tickle TCP connection $dest $src"
- $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true
- done
+ # Get connections, both directions
+ _conns=$(get_tcp_connections_for_ip "$_ip" | \
+ awk '{ print $1, $2 ; print $2, $1 }')
- if $_failed ; then
- echo "Failed to send tickle control"
- fi
- }
+ echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }'
+ echo "$_conns" | ctdb tickle
}
get_tcp_connections_for_ip ()
@@ -862,139 +853,6 @@
:
}
-ctdb_reconfigure_take_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
- mkdir -p "${_lock%/*}" # dirname
- touch "$_lock"
-
- (
- flock 9
- # This is overkill but will work if we need to extend
- # this to allow certain events to run multiple times
- # in parallel (e.g. takeip) and write multiple PIDs to
- # the file.
- {
- read _locker_event
- if [ -n "$_locker_event" ] ; then
- while read _pid ; do
- if [ -n "$_pid" -a "$_pid" != $$ ] && \
- kill -0 "$_pid" 2>/dev/null ; then
- exit 1
- fi
- done
- fi
- } <"$_lock"
-
- printf "%s\n%s\n" "$event_name" $$ >"$_lock"
- exit 0
- ) 9>"${_lock}.flock"
-}
-
-ctdb_reconfigure_release_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
-
- rm -f "$_lock"
-}
-
-ctdb_replay_monitor_status ()
-{
- echo "Replaying previous status for this script due to reconfigure..."
- # Leading separator ('|') is missing in some versions...
- _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|")
- # Output looks like this:
- # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar|
- # This is the cheapest way of getting fields in the middle.
- # Intentional word splitting here
- # shellcheck disable=SC2046,2086
- set -- $(IFS="|" ; echo $_out)
- _code="$3"
- _status="$4"
- # The error output field can include colons so we'll try to
- # preserve them. The weak checking at the beginning tries to make
- # this work for both broken (no leading '|') and fixed output.
- _out="${_out%|}"
- _err_out="${_out#*monitor|${script_name}|*|*|*|*|}"
- case "$_status" in
- OK) : ;; # Do nothing special.
- TIMEDOUT)
- # Recast this as an error, since we can't exit with the
- # correct negative number.
- _code=1
- _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- DISABLED)
- # Recast this as an OK, since we can't exit with the
- # correct negative number.
- _code=0
- _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- *) : ;; # Must be ERROR, do nothing special.
- esac
- if [ -n "$_err_out" ] ; then
- echo "$_err_out"
- fi
- exit $_code
-}
-
-ctdb_service_check_reconfigure ()
-{
- assert_service_name
-
- # We only care about some events in this function. For others we
- # return now.
- case "$event_name" in
- monitor|ipreallocated|reconfigure) : ;;
- *) return 0 ;;
- esac
-
- if ctdb_reconfigure_take_lock ; then
- # No events covered by this function are running, so proceed
- # with gay abandon.
- case "$event_name" in
- reconfigure)
- (ctdb_service_reconfigure)
- exit $?
- ;;
- ipreallocated)
- if ctdb_service_needs_reconfigure ; then
- ctdb_service_reconfigure
- fi
- ;;
- esac
-
- ctdb_reconfigure_release_lock
- else
- # Somebody else is running an event we don't want to collide
- # with. We proceed with caution.
- case "$event_name" in
- reconfigure)
- # Tell whoever called us to retry.
- exit 2
- ;;
- ipreallocated)
- # Defer any scheduled reconfigure and just run the
- # rest of the ipreallocated event, as per the
- # eventscript. There's an assumption here that the
- # event doesn't depend on any scheduled reconfigure.
- # This is true in the current code.
- return 0
- ;;
- monitor)
- # There is most likely a reconfigure in progress so
- # the service is possibly unstable. As above, we
- # defer any scheduled reconfigured. We also replay
- # the previous monitor status since that's the best
- # information we have.
- ctdb_replay_monitor_status
- ;;
- esac
- fi
-}
-
##################################################################
# Does CTDB manage this service? - and associated auto-start/stop
@@ -1276,16 +1134,12 @@
sort >"$_my_tickles"
# Add tickles for connections that we haven't already got tickles for
- comm -23 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB addtickle "$_src" "$_dst"
- done
+ comm -23 "$_my_connections" "$_my_tickles" | \
+ $CTDB addtickle
# Remove tickles for connections that are no longer there
- comm -13 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB deltickle "$_src" "$_dst"
- done
+ comm -13 "$_my_connections" "$_my_tickles" | \
+ $CTDB deltickle
rm -f "$_my_connections" "$_my_tickles"
diff -Nru samba-4.5.8+dfsg/ctdb/tests/run_cluster_tests.sh samba-4.6.5+dfsg/ctdb/tests/run_cluster_tests.sh
--- samba-4.5.8+dfsg/ctdb/tests/run_cluster_tests.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/run_cluster_tests.sh 2017-01-11 07:55:14.000000000 +0000
@@ -155,20 +155,20 @@
# Not perfect, but it will do...
mktemp ()
{
- _dir=false
+ local dir=false
if [ "$1" = "-d" ] ; then
- _dir=true
+ dir=true
fi
- _t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM"
+ local t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM"
(
umask 077
- if $_dir ; then
- mkdir "$_t"
+ if $dir ; then
+ mkdir "$t"
else
- >"$_t"
+ >"$t"
fi
)
- echo "$_t"
+ echo "$t"
}
fi
@@ -179,12 +179,12 @@
run_one_test ()
{
- _f="$1"
+ local f="$1"
- [ -x "$_f" ] || die "test \"$_f\" is not executable"
+ [ -x "$f" ] || die "test \"$f\" is not executable"
tests_total=$(($tests_total + 1))
- ctdb_test_run "$_f" | tee "$tf" | show_progress
+ ctdb_test_run "$f" | tee "$tf" | show_progress
status=$?
if [ $status -eq 0 ] ; then
tests_passed=$(($tests_passed + 1))
@@ -192,37 +192,39 @@
tests_failed=$(($tests_failed + 1))
fi
if $with_summary ; then
+ local t
if [ $status -eq 0 ] ; then
- _t=" PASSED "
+ t=" PASSED "
else
- _t="*FAILED*"
+ t="*FAILED*"
fi
if $with_desc ; then
desc=$(tail -n +4 $tf | head -n 1)
- _f="$desc"
+ f="$desc"
fi
- echo "$_t $_f" >>"$sf"
+ echo "$t $f" >>"$sf"
fi
}
find_and_run_one_test ()
{
- _t="$1"
- _dir="$2"
+ local t="$1"
+ local dir="$2"
- _f="${_dir}${_dir:+/}${_t}"
+ local f="${dir}${dir:+/}${t}"
- if [ -d "$_f" ] ; then
- for _i in $(ls "${_f%/}/"*".sh" 2>/dev/null) ; do
- run_one_test "$_i"
+ if [ -d "$f" ] ; then
+ local i
+ for i in $(ls "${f%/}/"*".sh" 2>/dev/null) ; do
+ run_one_test "$i"
if $exit_on_fail && [ $status -ne 0 ] ; then
break
fi
done
# No tests found? Not a tests directory! Not found...
[ -n "$status" ] || status=127
- elif [ -f "$_f" ] ; then
- run_one_test "$_f"
+ elif [ -f "$f" ] ; then
+ run_one_test "$f"
else
status=127
fi
@@ -255,7 +257,8 @@
# If no tests specified then run some defaults
if [ -z "$1" ] ; then
if [ -n "$TEST_LOCAL_DAEMONS" ] ; then
- set -- onnode takeover tool eventscripts cunit shellcheck simple
+ set -- onnode takeover takeover_helper tool eventscripts \
+ cunit eventd shellcheck simple
else
set -- simple complex
fi
diff -Nru samba-4.5.8+dfsg/ctdb/tests/run_tests.sh samba-4.6.5+dfsg/ctdb/tests/run_tests.sh
--- samba-4.5.8+dfsg/ctdb/tests/run_tests.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/run_tests.sh 2017-01-11 07:55:14.000000000 +0000
@@ -155,20 +155,20 @@
# Not perfect, but it will do...
mktemp ()
{
- _dir=false
+ local dir=false
if [ "$1" = "-d" ] ; then
- _dir=true
+ dir=true
fi
- _t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM"
+ local t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM"
(
umask 077
- if $_dir ; then
- mkdir "$_t"
+ if $dir ; then
+ mkdir "$t"
else
- >"$_t"
+ >"$t"
fi
)
- echo "$_t"
+ echo "$t"
}
fi
@@ -179,12 +179,12 @@
run_one_test ()
{
- _f="$1"
+ local f="$1"
- [ -x "$_f" ] || die "test \"$_f\" is not executable"
+ [ -x "$f" ] || die "test \"$f\" is not executable"
tests_total=$(($tests_total + 1))
- ctdb_test_run "$_f" | tee "$tf" | show_progress
+ ctdb_test_run "$f" | tee "$tf" | show_progress
status=$?
if [ $status -eq 0 ] ; then
tests_passed=$(($tests_passed + 1))
@@ -192,37 +192,39 @@
tests_failed=$(($tests_failed + 1))
fi
if $with_summary ; then
+ local t
if [ $status -eq 0 ] ; then
- _t=" PASSED "
+ t=" PASSED "
else
- _t="*FAILED*"
+ t="*FAILED*"
fi
if $with_desc ; then
desc=$(tail -n +4 $tf | head -n 1)
- _f="$desc"
+ f="$desc"
fi
- echo "$_t $_f" >>"$sf"
+ echo "$t $f" >>"$sf"
fi
}
find_and_run_one_test ()
{
- _t="$1"
- _dir="$2"
+ local t="$1"
+ local dir="$2"
- _f="${_dir}${_dir:+/}${_t}"
+ local f="${dir}${dir:+/}${t}"
- if [ -d "$_f" ] ; then
- for _i in $(ls "${_f%/}/"*".sh" 2>/dev/null) ; do
- run_one_test "$_i"
+ if [ -d "$f" ] ; then
+ local i
+ for i in $(ls "${f%/}/"*".sh" 2>/dev/null) ; do
+ run_one_test "$i"
if $exit_on_fail && [ $status -ne 0 ] ; then
break
fi
done
# No tests found? Not a tests directory! Not found...
[ -n "$status" ] || status=127
- elif [ -f "$_f" ] ; then
- run_one_test "$_f"
+ elif [ -f "$f" ] ; then
+ run_one_test "$f"
else
status=127
fi
@@ -255,7 +257,8 @@
# If no tests specified then run some defaults
if [ -z "$1" ] ; then
if [ -n "$TEST_LOCAL_DAEMONS" ] ; then
- set -- onnode takeover tool eventscripts cunit shellcheck simple
+ set -- onnode takeover takeover_helper tool eventscripts \
+ cunit eventd shellcheck simple
else
set -- simple complex
fi
diff -Nru samba-4.5.8+dfsg/ctdb/tests/scripts/unit.sh samba-4.6.5+dfsg/ctdb/tests/scripts/unit.sh
--- samba-4.5.8+dfsg/ctdb/tests/scripts/unit.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/scripts/unit.sh 2017-01-11 07:55:14.000000000 +0000
@@ -75,7 +75,7 @@
Output (Exit status: ${_rc}):
--------------------------------------------------
EOF
- echo "$_out" | cat $TEST_CAT_RESULTS_OPTS
+ echo "$_out" | result_filter | cat $TEST_CAT_RESULTS_OPTS
fi
if ! $_passed ; then
@@ -208,6 +208,16 @@
result_check || exit $?
}
+
+# Simple test harness for running tests without tracing
+unit_test_notrace ()
+{
+ test_header "$@"
+
+ _out=$("$@" 2>&1)
+
+ result_check || exit $?
+}
test_cleanup_hooks=""
diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh samba-4.6.5+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh
--- samba-4.5.8+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/simple/04_ctdb_setvar.sh 2017-01-11 07:55:14.000000000 +0000
@@ -18,8 +18,8 @@
1. Verify that the status on all of the ctdb nodes is 'OK'.
2. Get a list of all the ctdb tunable variables, using the 'ctdb
listvars' command.
-3. Set the value of one of the variables using the 'setvar' control on
- one of the nodes. E.g. 'ctdb setvar DeterministicIPs 0'.
+3. Increment the value of one of the variables using the 'setvar' control on
+ one of the nodes. E.g. 'ctdb setvar RecoverTimeout 31'.
4. Verify that the 'listvars' control now shows the new value for the
variable.
diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh samba-4.6.5+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh
--- samba-4.5.8+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/simple/13_ctdb_setdebug.sh 2017-01-11 07:55:14.000000000 +0000
@@ -62,14 +62,24 @@
set_and_check_debug $test_node "$new_debug"
done
-i=0
-for new_debug in $levels ; do
+while read new_debug i ; do
[ "$initial_debug" != "$i" ] || continue
echo
set_and_check_debug $test_node "$i" "$new_debug"
- i=$[ $i + 1 ]
-done
+done </dev/null 2>&1 || _failed=true
- echo "Tickle TCP connection $dest $src"
- $CTDB tickle "$dest" "$src" >/dev/null 2>&1 || _failed=true
- done
+ # Get connections, both directions
+ _conns=$(get_tcp_connections_for_ip "$_ip" | \
+ awk '{ print $1, $2 ; print $2, $1 }')
- if $_failed ; then
- echo "Failed to send tickle control"
- fi
- }
+ echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }'
+ echo "$_conns" | ctdb tickle
}
get_tcp_connections_for_ip ()
@@ -862,139 +853,6 @@
:
}
-ctdb_reconfigure_take_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
- mkdir -p "${_lock%/*}" # dirname
- touch "$_lock"
-
- (
- flock 9
- # This is overkill but will work if we need to extend
- # this to allow certain events to run multiple times
- # in parallel (e.g. takeip) and write multiple PIDs to
- # the file.
- {
- read _locker_event
- if [ -n "$_locker_event" ] ; then
- while read _pid ; do
- if [ -n "$_pid" -a "$_pid" != $$ ] && \
- kill -0 "$_pid" 2>/dev/null ; then
- exit 1
- fi
- done
- fi
- } <"$_lock"
-
- printf "%s\n%s\n" "$event_name" $$ >"$_lock"
- exit 0
- ) 9>"${_lock}.flock"
-}
-
-ctdb_reconfigure_release_lock ()
-{
- _ctdb_service_reconfigure_common
- _lock="${_d}/reconfigure_lock"
-
- rm -f "$_lock"
-}
-
-ctdb_replay_monitor_status ()
-{
- echo "Replaying previous status for this script due to reconfigure..."
- # Leading separator ('|') is missing in some versions...
- _out=$($CTDB scriptstatus -X | grep -E "^\|?monitor\|${script_name}\|")
- # Output looks like this:
- # |monitor|60.nfs|1|ERROR|1314764004.030861|1314764004.035514|foo bar|
- # This is the cheapest way of getting fields in the middle.
- # Intentional word splitting here
- # shellcheck disable=SC2046,2086
- set -- $(IFS="|" ; echo $_out)
- _code="$3"
- _status="$4"
- # The error output field can include colons so we'll try to
- # preserve them. The weak checking at the beginning tries to make
- # this work for both broken (no leading '|') and fixed output.
- _out="${_out%|}"
- _err_out="${_out#*monitor|${script_name}|*|*|*|*|}"
- case "$_status" in
- OK) : ;; # Do nothing special.
- TIMEDOUT)
- # Recast this as an error, since we can't exit with the
- # correct negative number.
- _code=1
- _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- DISABLED)
- # Recast this as an OK, since we can't exit with the
- # correct negative number.
- _code=0
- _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
- ;;
- *) : ;; # Must be ERROR, do nothing special.
- esac
- if [ -n "$_err_out" ] ; then
- echo "$_err_out"
- fi
- exit $_code
-}
-
-ctdb_service_check_reconfigure ()
-{
- assert_service_name
-
- # We only care about some events in this function. For others we
- # return now.
- case "$event_name" in
- monitor|ipreallocated|reconfigure) : ;;
- *) return 0 ;;
- esac
-
- if ctdb_reconfigure_take_lock ; then
- # No events covered by this function are running, so proceed
- # with gay abandon.
- case "$event_name" in
- reconfigure)
- (ctdb_service_reconfigure)
- exit $?
- ;;
- ipreallocated)
- if ctdb_service_needs_reconfigure ; then
- ctdb_service_reconfigure
- fi
- ;;
- esac
-
- ctdb_reconfigure_release_lock
- else
- # Somebody else is running an event we don't want to collide
- # with. We proceed with caution.
- case "$event_name" in
- reconfigure)
- # Tell whoever called us to retry.
- exit 2
- ;;
- ipreallocated)
- # Defer any scheduled reconfigure and just run the
- # rest of the ipreallocated event, as per the
- # eventscript. There's an assumption here that the
- # event doesn't depend on any scheduled reconfigure.
- # This is true in the current code.
- return 0
- ;;
- monitor)
- # There is most likely a reconfigure in progress so
- # the service is possibly unstable. As above, we
- # defer any scheduled reconfigured. We also replay
- # the previous monitor status since that's the best
- # information we have.
- ctdb_replay_monitor_status
- ;;
- esac
- fi
-}
-
##################################################################
# Does CTDB manage this service? - and associated auto-start/stop
@@ -1276,16 +1134,12 @@
sort >"$_my_tickles"
# Add tickles for connections that we haven't already got tickles for
- comm -23 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB addtickle "$_src" "$_dst"
- done
+ comm -23 "$_my_connections" "$_my_tickles" | \
+ $CTDB addtickle
# Remove tickles for connections that are no longer there
- comm -13 "$_my_connections" "$_my_tickles" |
- while read _src _dst ; do
- $CTDB deltickle "$_src" "$_dst"
- done
+ comm -13 "$_my_connections" "$_my_tickles" | \
+ $CTDB deltickle
rm -f "$_my_connections" "$_my_tickles"
diff -Nru samba-4.5.8+dfsg/ctdb/tests/simple/scripts/local_daemons.bash samba-4.6.5+dfsg/ctdb/tests/simple/scripts/local_daemons.bash
--- samba-4.5.8+dfsg/ctdb/tests/simple/scripts/local_daemons.bash 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/simple/scripts/local_daemons.bash 2017-01-11 07:55:14.000000000 +0000
@@ -1,20 +1,17 @@
# If we're not running on a real cluster then we need a local copy of
# ctdb (and other stuff) in $PATH and we will use local daemons.
-export CTDB_NODES_SOCKETS=""
-for i in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do
- CTDB_NODES_SOCKETS="${CTDB_NODES_SOCKETS}${CTDB_NODES_SOCKETS:+ }${TEST_VAR_DIR}/sock.${i}"
-done
-
# Use in-tree binaries if running against local daemons.
# Otherwise CTDB need to be installed on all nodes.
if [ -n "$ctdb_dir" -a -d "${ctdb_dir}/bin" ] ; then
# ctdbd_wrapper is in config/ directory
PATH="${ctdb_dir}/bin:${ctdb_dir}/config:${PATH}"
hdir="${ctdb_dir}/bin"
+ export CTDB_EVENTD="${hdir}/ctdb_eventd"
+ export CTDB_EVENT_HELPER="${hdir}/ctdb_event"
export CTDB_LOCK_HELPER="${hdir}/ctdb_lock_helper"
- export CTDB_EVENT_HELPER="${hdir}/ctdb_event_helper"
export CTDB_RECOVERY_HELPER="${hdir}/ctdb_recovery_helper"
+ export CTDB_TAKEOVER_HELPER="${hdir}/ctdb_takeover_helper"
export CTDB_CLUSTER_MUTEX_HELPER="${hdir}/ctdb_mutex_fcntl_helper"
fi
@@ -31,10 +28,54 @@
sed -e 's@=\([^"]\)@="\1@' -e 's@[^"]$@&"@' -e 's@="$@&"@'
}
-setup_ctdb ()
+# If the given IP is hosted then print 2 items: maskbits and iface
+have_ip ()
+{
+ local addr="$1"
+ local bits t
+
+ case "$addr" in
+ *:*) bits=128 ;;
+ *) bits=32 ;;
+ esac
+
+ t=$(ip addr show to "${addr}/${bits}")
+ [ -n "$t" ]
+}
+
+node_dir ()
{
- mkdir -p "${TEST_VAR_DIR}/test.db/persistent"
+ local pnn="$1"
+ echo "${TEST_VAR_DIR}/node.${pnn}"
+}
+
+node_conf ()
+{
+ local pnn="$1"
+
+ local node_dir=$(node_dir "$pnn")
+ echo "${node_dir}/ctdbd.conf"
+}
+
+node_pidfile ()
+{
+ local pnn="$1"
+
+ local node_dir=$(node_dir "$pnn")
+ echo "${node_dir}/ctdbd.pid"
+}
+
+node_socket ()
+{
+ local pnn="$1"
+
+ local node_dir=$(node_dir "$pnn")
+ echo "${node_dir}/ctdbd.socket"
+}
+
+setup_ctdb ()
+{
local public_addresses_all="${TEST_VAR_DIR}/public_addresses_all"
rm -f $CTDB_NODES $public_addresses_all
@@ -53,13 +94,19 @@
mkdir -p "${TEST_VAR_DIR}/events.d"
cp -p "${events_d}/"* "${TEST_VAR_DIR}/events.d/"
+ local have_all_ips=true
local i
for i in $(seq 1 $TEST_LOCAL_DAEMONS) ; do
- if [ "${CTDB_USE_IPV6}x" != "x" ]; then
- j=$((printf "%02x" $i))
- echo "fd00::5357:5f${j}" >>"$CTDB_NODES"
- # FIXME: need to add addresses to lo as root before running :-(
- # ip addr add "fc00:10::${i}/64" dev lo
+ if [ -n "$CTDB_USE_IPV6" ]; then
+ local j=$(printf "%02x" $i)
+ local node_ip="fd00::5357:5f${j}"
+ if have_ip "$node_ip" ; then
+ echo "$node_ip" >>"$CTDB_NODES"
+ else
+ echo "ERROR: ${node_ip} not on an interface, please add it"
+ have_all_ips=false
+ fi
+
# 2 public addresses on most nodes, just to make things interesting.
if [ $(($i - 1)) -ne $no_public_ips ] ; then
echo "fc00:10::1:${i}/64 lo" >>"$public_addresses_all"
@@ -76,9 +123,16 @@
fi
done
+ if ! $have_all_ips ; then
+ return 1
+ fi
+
local pnn
for pnn in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do
- local public_addresses_mine="${TEST_VAR_DIR}/public_addresses.${pnn}"
+ local node_dir=$(node_dir "$pnn")
+ mkdir -p "$node_dir"
+
+ local public_addresses_mine="${node_dir}/public_addresses"
local public_addresses
if [ "$no_public_ips" = $pnn ] ; then
@@ -91,20 +145,24 @@
local node_ip=$(sed -n -e "$(($pnn + 1))p" "$CTDB_NODES")
- local pidfile="${TEST_VAR_DIR}/ctdbd.${pnn}.pid"
- local conf="${TEST_VAR_DIR}/ctdbd.${pnn}.conf"
+ local conf=$(node_conf "$pnn")
+ local socket=$(node_socket "$pnn")
+
+ local db_dir="${node_dir}/db"
+ mkdir -p "${db_dir}/persistent"
+
cat >"$conf" <' | xargs | sed -e 's| |,|g') -o args ww
echo
}
+
+# onnode will use CTDB_NODES_SOCKETS to help the ctdb tool connection
+# to each daemon
+export CTDB_NODES_SOCKETS=""
+for i in $(seq 0 $(($TEST_LOCAL_DAEMONS - 1))) ; do
+ socket=$(node_socket "$i")
+ CTDB_NODES_SOCKETS="${CTDB_NODES_SOCKETS}${CTDB_NODES_SOCKETS:+ }${socket}"
+done
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ctdb_takeover_tests.c samba-4.6.5+dfsg/ctdb/tests/src/ctdb_takeover_tests.c
--- samba-4.5.8+dfsg/ctdb/tests/src/ctdb_takeover_tests.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/ctdb_takeover_tests.c 2017-01-11 07:55:14.000000000 +0000
@@ -32,6 +32,8 @@
#include "server/ipalloc.h"
+#include "ipalloc_read_known_ips.h"
+
static void print_ctdb_public_ip_list(TALLOC_CTX *mem_ctx,
struct public_ip_list * ips)
{
@@ -49,86 +51,6 @@
static enum ctdb_runstate *get_runstate(TALLOC_CTX *tmp_ctx,
int numnodes);
-static void add_ip(TALLOC_CTX *mem_ctx,
- struct ctdb_public_ip_list *l,
- ctdb_sock_addr *addr,
- uint32_t pnn)
-{
-
- l->ip = talloc_realloc(mem_ctx, l->ip,
- struct ctdb_public_ip, l->num + 1);
- assert(l->ip != NULL);
-
- l->ip[l->num].addr = *addr;
- l->ip[l->num].pnn = pnn;
- l->num++;
-}
-
-/* Format of each line is "IP CURRENT_PNN [ALLOWED_PNN,...]".
- * If multi is true then ALLOWED_PNNs are not allowed. */
-static void read_ctdb_public_ip_info_node(int numnodes,
- bool multi,
- struct ctdb_public_ip_list **k,
- struct ctdb_public_ip_list *known)
-{
- char line[1024];
- ctdb_sock_addr addr;
- char *t, *tok;
- int pnn, n;
-
- /* Known public IPs */
- *k = talloc_zero(known, struct ctdb_public_ip_list);
- assert(k != NULL);
-
- while (fgets(line, sizeof(line), stdin) != NULL) {
-
- /* Get rid of pesky newline */
- if ((t = strchr(line, '\n')) != NULL) {
- *t = '\0';
- }
-
- /* Exit on an empty line */
- if (line[0] == '\0') {
- break;
- }
-
- /* Get the IP address */
- tok = strtok(line, " \t");
- if (tok == NULL) {
- DEBUG(DEBUG_ERR, (__location__ " WARNING, bad line ignored :%s\n", line));
- continue;
- }
-
- if (!parse_ip(tok, NULL, 0, &addr)) {
- DEBUG(DEBUG_ERR, (__location__ " ERROR, bad address :%s\n", tok));
- continue;
- }
-
- /* Get the PNN */
- pnn = -1;
- tok = strtok(NULL, " \t");
- if (tok != NULL) {
- pnn = (int) strtol(tok, (char **) NULL, 10);
- }
-
- add_ip(*k, *k, &addr, pnn);
-
- tok = strtok(NULL, " \t#");
- if (tok == NULL) {
- continue;
- }
-
- /* Handle allowed nodes for addr */
- assert(multi == false);
- t = strtok(tok, ",");
- while (t != NULL) {
- n = (int) strtol(t, (char **) NULL, 10);
- add_ip(known, &known[n], &addr, pnn);
- t = strtok(NULL, ",");
- }
- }
-}
-
static void read_ctdb_public_ip_info(TALLOC_CTX *ctx,
int numnodes,
bool multi,
@@ -136,34 +58,15 @@
struct ctdb_public_ip_list ** avail)
{
int n;
- struct ctdb_public_ip_list * k;
enum ctdb_runstate *runstate;
- *known = talloc_zero_array(ctx, struct ctdb_public_ip_list,
- numnodes);
+ *known = ipalloc_read_known_ips(ctx, numnodes, multi);
assert(*known != NULL);
+
*avail = talloc_zero_array(ctx, struct ctdb_public_ip_list,
numnodes);
assert(*avail != NULL);
- if (multi) {
- for (n = 0; n < numnodes; n++) {
- read_ctdb_public_ip_info_node(numnodes, multi,
- &k, *known);
-
- (*known)[n] = *k;
- }
- } else {
- read_ctdb_public_ip_info_node(numnodes, multi, &k, *known);
-
- /* Assign it to any nodes that don't have a list assigned */
- for (n = 0; n < numnodes; n++) {
- if ((*known)[n].num == 0) {
- (*known)[n] = *k;
- }
- }
- }
-
runstate = get_runstate(ctx, numnodes);
for (n = 0; n < numnodes; n++) {
if (runstate[n] == CTDB_RUNSTATE_RUNNING) {
@@ -251,10 +154,11 @@
{
struct ctdb_public_ip_list *known;
struct ctdb_public_ip_list *avail;
- char *tok, *ns, *t;
+ char *tok, *ns;
+ const char *t;
struct ctdb_node_map *nodemap;
- uint32_t *tval_noiptakeover;
- uint32_t *tval_noiptakeoverondisabled;
+ uint32_t noiptakeover;
+ uint32_t noiphostonalldisabled;
ctdb_sock_addr sa_zero = { .ip = { 0 } };
enum ipalloc_algorithm algorithm;
@@ -291,9 +195,26 @@
}
}
+ t = getenv("CTDB_SET_NoIPTakeover");
+ if (t != NULL) {
+ noiptakeover = (uint32_t) strtol(t, NULL, 0);
+ } else {
+ noiptakeover = 0;
+ }
+
+ t = getenv("CTDB_SET_NoIPHostOnAllDisabled");
+ if (t != NULL) {
+ noiphostonalldisabled = (uint32_t) strtol(t, NULL, 0);
+ } else {
+ noiphostonalldisabled = 0;
+ }
+
*ipalloc_state = ipalloc_state_init(mem_ctx, nodemap->num,
algorithm,
- false, NULL);
+ (noiptakeover != 0),
+ false,
+ (noiphostonalldisabled != 0),
+ NULL);
assert(*ipalloc_state != NULL);
read_ctdb_public_ip_info(mem_ctx, nodemap->num,
@@ -302,17 +223,7 @@
ipalloc_set_public_ips(*ipalloc_state, known, avail);
- tval_noiptakeover = get_tunable_values(mem_ctx, nodemap->num,
- "CTDB_SET_NoIPTakeover");
- assert(tval_noiptakeover != NULL);
- tval_noiptakeoverondisabled =
- get_tunable_values(mem_ctx, nodemap->num,
- "CTDB_SET_NoIPHostOnAllDisabled");
- assert(tval_noiptakeoverondisabled != NULL);
-
- ipalloc_set_node_flags(*ipalloc_state, nodemap,
- tval_noiptakeover,
- tval_noiptakeoverondisabled);
+ ipalloc_set_node_flags(*ipalloc_state, nodemap);
}
/* IP layout is read from stdin. See comment for ctdb_test_init() for
@@ -340,10 +251,13 @@
int main(int argc, const char *argv[])
{
- DEBUGLEVEL = DEBUG_DEBUG;
- if (getenv("CTDB_TEST_LOGLEVEL")) {
- DEBUGLEVEL = atoi(getenv("CTDB_TEST_LOGLEVEL"));
- }
+ int loglevel;
+ const char *debuglevelstr = getenv("CTDB_TEST_LOGLEVEL");
+
+ if (! debug_level_parse(debuglevelstr, &loglevel)) {
+ loglevel = DEBUG_DEBUG;
+ }
+ DEBUGLEVEL = loglevel;
if (argc < 2) {
usage();
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/fake_ctdbd.c samba-4.6.5+dfsg/ctdb/tests/src/fake_ctdbd.c
--- samba-4.5.8+dfsg/ctdb/tests/src/fake_ctdbd.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/fake_ctdbd.c 2017-01-11 07:55:14.000000000 +0000
@@ -40,6 +40,8 @@
#include "common/logging.h"
#include "common/tunable.h"
+#include "ipalloc_read_known_ips.h"
+
#define CTDB_PORT 4379
@@ -98,6 +100,14 @@
uint64_t srvid;
};
+struct fake_control_failure {
+ struct fake_control_failure *prev, *next;
+ enum ctdb_controls opcode;
+ uint32_t pnn;
+ const char *error;
+ const char *comment;
+};
+
struct ctdbd_context {
struct node_map *node_map;
struct interface_map *iface_map;
@@ -109,11 +119,13 @@
struct timeval recovery_start_time;
struct timeval recovery_end_time;
bool takeover_disabled;
- enum debug_level log_level;
+ int log_level;
enum ctdb_runstate runstate;
struct ctdb_tunable_list tun_list;
int monitoring_mode;
char *reclock;
+ struct ctdb_public_ip_list *known_ips;
+ struct fake_control_failure *control_failures;
};
/*
@@ -704,6 +716,109 @@
return NULL;
}
+static bool public_ips_parse(struct ctdbd_context *ctdb,
+ uint32_t numnodes)
+{
+ if (numnodes == 0) {
+ D_ERR("Must initialise nodemap before public IPs\n");
+ return false;
+ }
+
+ ctdb->known_ips = ipalloc_read_known_ips(ctdb, numnodes, false);
+
+ return (ctdb->known_ips != NULL);
+}
+
+/* Read information about controls to fail. Format is:
+ * {ERROR|TIMEOUT}
+ */
+static bool control_failures_parse(struct ctdbd_context *ctdb)
+{
+ char line[1024];
+
+ while ((fgets(line, sizeof(line), stdin) != NULL)) {
+ char *tok, *t;
+ enum ctdb_controls opcode;
+ uint32_t pnn;
+ const char *error;
+ const char *comment;
+ struct fake_control_failure *failure = NULL;
+
+ if (line[0] == '\n') {
+ break;
+ }
+
+ /* Get rid of pesky newline */
+ if ((t = strchr(line, '\n')) != NULL) {
+ *t = '\0';
+ }
+
+ /* Get opcode */
+ tok = strtok(line, " \t");
+ if (tok == NULL) {
+ D_ERR("bad line (%s) - missing opcode\n", line);
+ continue;
+ }
+ opcode = (enum ctdb_controls)strtoul(tok, NULL, 0);
+
+ /* Get PNN */
+ tok = strtok(NULL, " \t");
+ if (tok == NULL) {
+ D_ERR("bad line (%s) - missing PNN\n", line);
+ continue;
+ }
+ pnn = (uint32_t)strtoul(tok, NULL, 0);
+
+ /* Get error */
+ tok = strtok(NULL, " \t");
+ if (tok == NULL) {
+ D_ERR("bad line (%s) - missing errno\n", line);
+ continue;
+ }
+ error = talloc_strdup(ctdb, tok);
+ if (error == NULL) {
+ goto fail;
+ }
+ if (strcmp(error, "ERROR") != 0 &&
+ strcmp(error, "TIMEOUT") != 0) {
+ D_ERR("bad line (%s) "
+ "- error must be \"ERROR\" or \"TIMEOUT\"\n",
+ line);
+ goto fail;
+ }
+
+ /* Get comment */
+ tok = strtok(NULL, "\n"); /* rest of line */
+ if (tok == NULL) {
+ D_ERR("bad line (%s) - missing comment\n", line);
+ continue;
+ }
+ comment = talloc_strdup(ctdb, tok);
+ if (comment == NULL) {
+ goto fail;
+ }
+
+ failure = talloc_zero(ctdb, struct fake_control_failure);
+ if (failure == NULL) {
+ goto fail;
+ }
+
+ failure->opcode = opcode;
+ failure->pnn = pnn;
+ failure->error = error;
+ failure->comment = comment;
+
+ DLIST_ADD(ctdb->control_failures, failure);
+ }
+
+ D_INFO("Parsing fake control failures done\n");
+ return true;
+
+fail:
+ D_INFO("Parsing fake control failures failed\n");
+ return false;
+}
+
/*
* CTDB context setup
*/
@@ -769,8 +884,13 @@
status = vnnmap_parse(ctdb->vnn_map);
} else if (strcmp(line, "DBMAP") == 0) {
status = dbmap_parse(ctdb->db_map);
+ } else if (strcmp(line, "PUBLICIPS") == 0) {
+ status = public_ips_parse(ctdb,
+ ctdb->node_map->num_nodes);
} else if (strcmp(line, "RECLOCK") == 0) {
status = reclock_parse(ctdb);
+ } else if (strcmp(line, "CONTROLFAILS") == 0) {
+ status = control_failures_parse(ctdb);
} else {
fprintf(stderr, "Unknown line %s\n", line);
status = false;
@@ -1242,7 +1362,7 @@
struct ctdb_reply_control reply;
reply.rdata.opcode = request->opcode;
- reply.rdata.data.loglevel = debug_level_to_int(ctdb->log_level);
+ reply.rdata.data.loglevel = (uint32_t)ctdb->log_level;
reply.status = 0;
reply.errmsg = NULL;
@@ -1259,7 +1379,7 @@
struct ctdbd_context *ctdb = state->ctdb;
struct ctdb_reply_control reply;
- ctdb->log_level = debug_level_from_int(request->rdata.data.loglevel);
+ ctdb->log_level = (int)request->rdata.data.loglevel;
reply.rdata.opcode = request->opcode;
reply.status = 0;
@@ -1917,6 +2037,174 @@
client_send_control(req, header, &reply);
}
+static void control_release_ip(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_public_ip *ip = request->rdata.data.pubip;
+ struct ctdb_reply_control reply;
+ struct ctdb_public_ip_list *ips = NULL;
+ struct ctdb_public_ip *t = NULL;
+ int i;
+
+ reply.rdata.opcode = request->opcode;
+
+ if (ctdb->known_ips == NULL) {
+ D_INFO("RELEASE_IP %s - not a public IP\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ goto done;
+ }
+
+ ips = &ctdb->known_ips[header->destnode];
+
+ t = NULL;
+ for (i = 0; i < ips->num; i++) {
+ if (ctdb_sock_addr_same_ip(&ips->ip[i].addr, &ip->addr)) {
+ t = &ips->ip[i];
+ break;
+ }
+ }
+ if (t == NULL) {
+ D_INFO("RELEASE_IP %s - not a public IP\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ goto done;
+ }
+
+ if (t->pnn != header->destnode) {
+ if (header->destnode == ip->pnn) {
+ D_ERR("error: RELEASE_IP %s - to TAKE_IP node %d\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr),
+ ip->pnn);
+ reply.status = -1;
+ reply.errmsg = "RELEASE_IP to TAKE_IP node";
+ client_send_control(req, header, &reply);
+ return;
+ }
+
+ D_INFO("RELEASE_IP %s - to node %d - redundant\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr),
+ ip->pnn);
+ t->pnn = ip->pnn;
+ } else {
+ D_NOTICE("RELEASE_IP %s - to node %d\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr),
+ ip->pnn);
+ t->pnn = ip->pnn;
+ }
+
+done:
+ reply.status = 0;
+ reply.errmsg = NULL;
+ client_send_control(req, header, &reply);
+}
+
+static void control_takeover_ip(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_public_ip *ip = request->rdata.data.pubip;
+ struct ctdb_reply_control reply;
+ struct ctdb_public_ip_list *ips = NULL;
+ struct ctdb_public_ip *t = NULL;
+ int i;
+
+ reply.rdata.opcode = request->opcode;
+
+ if (ctdb->known_ips == NULL) {
+ D_INFO("TAKEOVER_IP %s - not a public IP\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ goto done;
+ }
+
+ ips = &ctdb->known_ips[header->destnode];
+
+ t = NULL;
+ for (i = 0; i < ips->num; i++) {
+ if (ctdb_sock_addr_same_ip(&ips->ip[i].addr, &ip->addr)) {
+ t = &ips->ip[i];
+ break;
+ }
+ }
+ if (t == NULL) {
+ D_INFO("TAKEOVER_IP %s - not a public IP\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ goto done;
+ }
+
+ if (t->pnn == header->destnode) {
+ D_INFO("TAKEOVER_IP %s - redundant\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ } else {
+ D_NOTICE("TAKEOVER_IP %s\n",
+ ctdb_sock_addr_to_string(mem_ctx, &ip->addr));
+ t->pnn = ip->pnn;
+ }
+
+done:
+ reply.status = 0;
+ reply.errmsg = NULL;
+ client_send_control(req, header, &reply);
+}
+
+static void control_get_public_ips(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_reply_control reply;
+ struct ctdb_public_ip_list *ips = NULL;
+
+ reply.rdata.opcode = request->opcode;
+
+ if (ctdb->known_ips == NULL) {
+ /* No IPs defined so create a dummy empty struct and ship it */
+ ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);;
+ if (ips == NULL) {
+ reply.status = ENOMEM;
+ reply.errmsg = "Memory error";
+ goto done;
+ }
+ goto ok;
+ }
+
+ ips = &ctdb->known_ips[header->destnode];
+
+ if (request->flags & CTDB_PUBLIC_IP_FLAGS_ONLY_AVAILABLE) {
+ /* If runstate is not RUNNING or a node is then return
+ * no available IPs. Don't worry about interface
+ * states here - we're not faking down to that level.
+ */
+ if (ctdb->runstate != CTDB_RUNSTATE_RUNNING) {
+ /* No available IPs: return dummy empty struct */
+ ips = talloc_zero(mem_ctx, struct ctdb_public_ip_list);;
+ if (ips == NULL) {
+ reply.status = ENOMEM;
+ reply.errmsg = "Memory error";
+ goto done;
+ }
+ }
+ }
+
+ok:
+ reply.rdata.data.pubip_list = ips;
+ reply.status = 0;
+ reply.errmsg = NULL;
+
+done:
+ client_send_control(req, header, &reply);
+}
+
static void control_get_nodemap(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
struct ctdb_req_header *header,
@@ -2163,31 +2451,24 @@
client_send_control(req, header, &reply);
}
-static void control_get_ifaces(TALLOC_CTX *mem_ctx,
- struct tevent_req *req,
- struct ctdb_req_header *header,
- struct ctdb_req_control *request)
+static struct ctdb_iface_list *get_ctdb_iface_list(TALLOC_CTX *mem_ctx,
+ struct ctdbd_context *ctdb)
{
- struct client_state *state = tevent_req_data(
- req, struct client_state);
- struct ctdbd_context *ctdb = state->ctdb;
- struct ctdb_reply_control reply;
struct ctdb_iface_list *iface_list;
struct interface *iface;
int i;
- reply.rdata.opcode = request->opcode;
-
iface_list = talloc_zero(mem_ctx, struct ctdb_iface_list);
if (iface_list == NULL) {
- goto fail;
+ goto done;
}
iface_list->num = ctdb->iface_map->num;
iface_list->iface = talloc_array(iface_list, struct ctdb_iface,
iface_list->num);
if (iface_list->iface == NULL) {
- goto fail;
+ TALLOC_FREE(iface_list);
+ goto done;
}
for (i=0; inum; i++) {
@@ -2200,6 +2481,105 @@
sizeof(iface_list->iface[i].name));
}
+done:
+ return iface_list;
+}
+
+static void control_get_public_ip_info(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_reply_control reply;
+ ctdb_sock_addr *addr = request->rdata.data.addr;
+ struct ctdb_public_ip_list *known = NULL;
+ struct ctdb_public_ip_info *info = NULL;
+ unsigned i;
+
+ reply.rdata.opcode = request->opcode;
+
+ info = talloc_zero(mem_ctx, struct ctdb_public_ip_info);
+ if (info == NULL) {
+ reply.status = ENOMEM;
+ reply.errmsg = "Memory error";
+ goto done;
+ }
+
+ reply.rdata.data.ipinfo = info;
+
+ if (ctdb->known_ips != NULL) {
+ known = &ctdb->known_ips[header->destnode];
+ } else {
+ /* No IPs defined so create a dummy empty struct and
+ * fall through. The given IP won't be matched
+ * below...
+ */
+ known = talloc_zero(mem_ctx, struct ctdb_public_ip_list);;
+ if (known == NULL) {
+ reply.status = ENOMEM;
+ reply.errmsg = "Memory error";
+ goto done;
+ }
+ }
+
+ for (i = 0; i < known->num; i++) {
+ if (ctdb_sock_addr_same_ip(&known->ip[i].addr,
+ addr)) {
+ break;
+ }
+ }
+
+ if (i == known->num) {
+ D_ERR("GET_PUBLIC_IP_INFO: not known public IP %s\n",
+ ctdb_sock_addr_to_string(mem_ctx, addr));
+ reply.status = -1;
+ reply.errmsg = "Unknown address";
+ goto done;
+ }
+
+ info->ip = known->ip[i];
+
+ /* The fake PUBLICIPS stanza and resulting known_ips data
+ * don't know anything about interfaces, so completely fake
+ * this.
+ */
+ info->active_idx = 0;
+
+ info->ifaces = get_ctdb_iface_list(mem_ctx, ctdb);
+ if (info->ifaces == NULL) {
+ reply.status = ENOMEM;
+ reply.errmsg = "Memory error";
+ goto done;
+ }
+
+ reply.status = 0;
+ reply.errmsg = NULL;
+
+done:
+ client_send_control(req, header, &reply);
+}
+
+static void control_get_ifaces(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_reply_control reply;
+ struct ctdb_iface_list *iface_list;
+
+ reply.rdata.opcode = request->opcode;
+
+ iface_list = get_ctdb_iface_list(mem_ctx, ctdb);
+ if (iface_list == NULL) {
+ goto fail;
+ }
+
reply.rdata.data.iface_list = iface_list;
reply.status = 0;
reply.errmsg = NULL;
@@ -2347,6 +2727,21 @@
client_send_control(req, header, &reply);
}
+static void control_ipreallocated(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct ctdb_reply_control reply;
+
+ /* Always succeed */
+ reply.rdata.opcode = request->opcode;
+ reply.status = 0;
+ reply.errmsg = NULL;
+
+ client_send_control(req, header, &reply);
+}
+
static void control_get_runstate(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
struct ctdb_req_header *header,
@@ -2392,6 +2787,44 @@
client_send_control(req, header, &reply);
}
+static bool fake_control_failure(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct ctdb_req_header *header,
+ struct ctdb_req_control *request)
+{
+ struct client_state *state = tevent_req_data(
+ req, struct client_state);
+ struct ctdbd_context *ctdb = state->ctdb;
+ struct ctdb_reply_control reply;
+ struct fake_control_failure *f = NULL;
+
+ D_DEBUG("Checking fake control failure for control %u on node %u\n",
+ request->opcode, header->destnode);
+ for (f = ctdb->control_failures; f != NULL; f = f->next) {
+ if (f->opcode == request->opcode &&
+ (f->pnn == header->destnode ||
+ f->pnn == CTDB_UNKNOWN_PNN)) {
+
+ reply.rdata.opcode = request->opcode;
+ if (strcmp(f->error, "TIMEOUT") == 0) {
+ /* Causes no reply */
+ D_ERR("Control %u fake timeout on node %u\n",
+ request->opcode, header->destnode);
+ return true;
+ } else if (strcmp(f->error, "ERROR") == 0) {
+ D_ERR("Control %u fake error on node %u\n",
+ request->opcode, header->destnode);
+ reply.status = -1;
+ reply.errmsg = f->comment;
+ client_send_control(req, header, &reply);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static void control_error(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
struct ctdb_req_header *header,
@@ -2753,6 +3186,10 @@
DEBUG(DEBUG_INFO, ("request opcode = %u, reqid = %u\n",
request.opcode, header.reqid));
+ if (fake_control_failure(mem_ctx, req, &header, &request)) {
+ goto done;
+ }
+
switch (request.opcode) {
case CTDB_CONTROL_PROCESS_EXISTS:
control_process_exists(mem_ctx, req, &header, &request);
@@ -2862,6 +3299,18 @@
control_get_capabilities(mem_ctx, req, &header, &request);
break;
+ case CTDB_CONTROL_RELEASE_IP:
+ control_release_ip(mem_ctx, req, &header, &request);
+ break;
+
+ case CTDB_CONTROL_TAKEOVER_IP:
+ control_takeover_ip(mem_ctx, req, &header, &request);
+ break;
+
+ case CTDB_CONTROL_GET_PUBLIC_IPS:
+ control_get_public_ips(mem_ctx, req, &header, &request);
+ break;
+
case CTDB_CONTROL_GET_NODEMAP:
control_get_nodemap(mem_ctx, req, &header, &request);
break;
@@ -2890,6 +3339,10 @@
control_db_get_health(mem_ctx, req, &header, &request);
break;
+ case CTDB_CONTROL_GET_PUBLIC_IP_INFO:
+ control_get_public_ip_info(mem_ctx, req, &header, &request);
+ break;
+
case CTDB_CONTROL_GET_IFACES:
control_get_ifaces(mem_ctx, req, &header, &request);
break;
@@ -2906,6 +3359,10 @@
control_set_db_sticky(mem_ctx, req, &header, &request);
break;
+ case CTDB_CONTROL_IPREALLOCATED:
+ control_ipreallocated(mem_ctx, req, &header, &request);
+ break;
+
case CTDB_CONTROL_GET_RUNSTATE:
control_get_runstate(mem_ctx, req, &header, &request);
break;
@@ -2921,6 +3378,7 @@
break;
}
+done:
talloc_free(mem_ctx);
}
@@ -3165,7 +3623,6 @@
TALLOC_CTX *mem_ctx;
struct ctdbd_context *ctdb;
struct tevent_context *ev;
- enum debug_level debug_level;
poptContext pc;
int opt, fd, ret, pfd[2];
ssize_t len;
@@ -3191,24 +3648,19 @@
exit(1);
}
- if (options.debuglevel == NULL) {
- DEBUGLEVEL = debug_level_to_int(DEBUG_ERR);
- } else {
- if (debug_level_parse(options.debuglevel, &debug_level)) {
- DEBUGLEVEL = debug_level_to_int(debug_level);
- } else {
- fprintf(stderr, "Invalid debug level\n");
- poptPrintHelp(pc, stdout, 0);
- exit(1);
- }
- }
-
mem_ctx = talloc_new(NULL);
if (mem_ctx == NULL) {
fprintf(stderr, "Memory error\n");
exit(1);
}
+ ret = logging_init(mem_ctx, "file:", options.debuglevel, "fake-ctdbd");
+ if (ret != 0) {
+ fprintf(stderr, "Invalid debug level\n");
+ poptPrintHelp(pc, stdout, 0);
+ exit(1);
+ }
+
ctdb = ctdbd_setup(mem_ctx);
if (ctdb == NULL) {
exit(1);
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c
--- samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.c 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,181 @@
+/*
+ Tests support for CTDB IP allocation
+
+ Copyright (C) Martin Schwenke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include
+
+#include "replace.h"
+#include "system/network.h"
+
+#include "lib/util/debug.h"
+
+#include "protocol/protocol.h"
+#include "common/logging.h"
+#include "common/system.h"
+
+#include "ipalloc_read_known_ips.h"
+
+static bool add_ip(TALLOC_CTX *mem_ctx,
+ struct ctdb_public_ip_list *l,
+ ctdb_sock_addr *addr,
+ uint32_t pnn)
+{
+
+ l->ip = talloc_realloc(mem_ctx, l->ip,
+ struct ctdb_public_ip, l->num + 1);
+ if (l->ip == NULL) {
+ D_ERR(__location__ " out of memory\n");
+ return false;
+ }
+
+ l->ip[l->num].addr = *addr;
+ l->ip[l->num].pnn = pnn;
+ l->num++;
+
+ return true;
+}
+
+/* Format of each line is "IP CURRENT_PNN [ALLOWED_PNN,...]".
+ * If multi is true then ALLOWED_PNNs are not allowed. */
+static bool read_ctdb_public_ip_info_node(bool multi,
+ struct ctdb_public_ip_list **k,
+ struct ctdb_public_ip_list *known)
+{
+ char line[1024];
+ ctdb_sock_addr addr;
+ char *t, *tok;
+ int pnn, n;
+
+ /* Known public IPs */
+ *k = talloc_zero(known, struct ctdb_public_ip_list);
+ if (*k == NULL) {
+ goto fail;
+ }
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+
+ /* Get rid of pesky newline */
+ if ((t = strchr(line, '\n')) != NULL) {
+ *t = '\0';
+ }
+
+ /* Exit on an empty line */
+ if (line[0] == '\0') {
+ break;
+ }
+
+ /* Get the IP address */
+ tok = strtok(line, " \t");
+ if (tok == NULL) {
+ D_WARNING("WARNING, bad line ignored :%s\n", line);
+ continue;
+ }
+
+ if (!parse_ip(tok, NULL, 0, &addr)) {
+ D_ERR("ERROR, bad address :%s\n", tok);
+ continue;
+ }
+
+ /* Get the PNN */
+ pnn = -1;
+ tok = strtok(NULL, " \t");
+ if (tok != NULL) {
+ pnn = (int) strtol(tok, (char **) NULL, 10);
+ }
+
+ if (! add_ip(*k, *k, &addr, pnn)) {
+ goto fail;
+ }
+
+ tok = strtok(NULL, " \t#");
+ if (tok == NULL) {
+ continue;
+ }
+
+ /* Handle allowed nodes for addr */
+ if (multi) {
+ D_ERR("ERROR, bad token\n");
+ goto fail;
+ }
+ t = strtok(tok, ",");
+ while (t != NULL) {
+ n = (int) strtol(t, (char **) NULL, 10);
+ if (! add_ip(known, &known[n], &addr, pnn)) {
+ goto fail;
+ }
+ t = strtok(NULL, ",");
+ }
+ }
+
+ return true;
+
+fail:
+ TALLOC_FREE(*k);
+ return false;
+}
+
+struct ctdb_public_ip_list * ipalloc_read_known_ips(TALLOC_CTX *ctx,
+ int numnodes,
+ bool multi)
+{
+ int n;
+ struct ctdb_public_ip_list *k;
+ struct ctdb_public_ip_list *known;
+
+ known = talloc_zero_array(ctx, struct ctdb_public_ip_list,
+ numnodes);
+ if (known == NULL) {
+ D_ERR(__location__ " out of memory\n");
+ goto fail;
+ }
+
+ if (multi) {
+ for (n = 0; n < numnodes; n++) {
+ if (! read_ctdb_public_ip_info_node(multi, &k, known)) {
+ goto fail;
+ }
+
+ known[n] = *k;
+ }
+ } else {
+ if (! read_ctdb_public_ip_info_node(multi, &k, known)) {
+ goto fail;
+ }
+
+ /* Copy it to any nodes that don't have a
+ * list assigned
+ */
+ for (n = 0; n < numnodes; n++) {
+ if (known[n].num == 0) {
+ known[n].num = k->num;
+ known[n].ip = talloc_memdup(
+ known, k->ip,
+ k->num * sizeof(struct ctdb_public_ip));
+ if (known[n].ip == NULL) {
+ goto fail;
+ }
+ }
+ }
+ }
+
+ return known;
+
+fail:
+ talloc_free(known);
+ return NULL;
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h
--- samba-4.5.8+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/ipalloc_read_known_ips.h 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,32 @@
+/*
+ Tests support for CTDB IP allocation
+
+ Copyright (C) Martin Schwenke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#ifndef __IPALLOC_READ_KNOWN_IPS_H__
+#define __IPALLOC_READ_KNOWN_IPS_H__
+
+#include
+#include
+
+#include "protocol/protocol.h"
+
+struct ctdb_public_ip_list * ipalloc_read_known_ips(TALLOC_CTX *ctx,
+ int numnodes,
+ bool multi);
+
+#endif /* __IPALLOC_READ_KNOWN_IPS_H__ */
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/pidfile_test.c samba-4.6.5+dfsg/ctdb/tests/src/pidfile_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/pidfile_test.c 2016-10-24 19:37:30.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/pidfile_test.c 2017-01-11 07:55:14.000000000 +0000
@@ -43,6 +43,7 @@
assert(S_ISREG(st.st_mode));
fp = fopen(pidfile, "r");
+ assert(fp != NULL);
ret = fscanf(fp, "%d", &pid);
assert(ret == 1);
assert(pid == getpid());
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/porting_tests.c samba-4.6.5+dfsg/ctdb/tests/src/porting_tests.c
--- samba-4.5.8+dfsg/ctdb/tests/src/porting_tests.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/porting_tests.c 2017-01-11 07:55:14.000000000 +0000
@@ -31,7 +31,6 @@
#include "lib/util/blocking.h"
#include "protocol/protocol.h"
-#include "common/cmdline.h"
#include "common/system.h"
#include "common/logging.h"
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/protocol_client_test.c samba-4.6.5+dfsg/ctdb/tests/src/protocol_client_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/protocol_client_test.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/protocol_client_test.c 2017-01-11 07:55:14.000000000 +0000
@@ -376,11 +376,6 @@
fill_ctdb_addr_info(mem_ctx, cd->data.addr_info);
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- fill_ctdb_string(mem_ctx, &cd->data.event_str);
- assert(cd->data.event_str != NULL);
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
break;
@@ -411,10 +406,6 @@
case CTDB_CONTROL_GET_NODEMAP:
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- cd->data.event = rand_int(CTDB_EVENT_MAX);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
cd->data.traverse_start = talloc(mem_ctx, struct ctdb_traverse_start);
assert(cd->data.traverse_start != NULL);
@@ -442,16 +433,6 @@
cd->data.role = rand_int(2);
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- fill_ctdb_string(mem_ctx, &cd->data.script);
- assert(cd->data.script != NULL);
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- fill_ctdb_string(mem_ctx, &cd->data.script);
- assert(cd->data.script != NULL);
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
cd->data.ban_state = talloc(mem_ctx, struct ctdb_ban_state);
assert(cd->data.ban_state != NULL);
@@ -821,10 +802,6 @@
verify_ctdb_addr_info(cd->data.addr_info, cd2->data.addr_info);
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- verify_ctdb_string(cd->data.event_str, cd2->data.event_str);
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
break;
@@ -851,10 +828,6 @@
case CTDB_CONTROL_GET_NODEMAP:
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- assert(cd->data.event == cd2->data.event);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
verify_ctdb_traverse_start(cd->data.traverse_start,
cd2->data.traverse_start);
@@ -881,14 +854,6 @@
assert(cd->data.role == cd2->data.role);
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- verify_ctdb_string(cd->data.script, cd2->data.script);
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- verify_ctdb_string(cd->data.script, cd2->data.script);
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
verify_ctdb_ban_state(cd->data.ban_state, cd2->data.ban_state);
break;
@@ -1262,9 +1227,6 @@
case CTDB_CONTROL_DEL_PUBLIC_IP:
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
cd->data.caps = rand32();
break;
@@ -1296,12 +1258,6 @@
fill_ctdb_node_map(mem_ctx, cd->data.nodemap);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- cd->data.script_list = talloc(mem_ctx, struct ctdb_script_list);
- assert(cd->data.script_list != NULL);
- fill_ctdb_script_list(mem_ctx, cd->data.script_list);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
break;
@@ -1325,12 +1281,6 @@
case CTDB_CONTROL_SET_RECMASTERROLE:
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
break;
@@ -1638,9 +1588,6 @@
case CTDB_CONTROL_DEL_PUBLIC_IP:
break;
- case CTDB_CONTROL_RUN_EVENTSCRIPTS:
- break;
-
case CTDB_CONTROL_GET_CAPABILITIES:
assert(cd->data.caps == cd2->data.caps);
break;
@@ -1663,11 +1610,6 @@
verify_ctdb_node_map(cd->data.nodemap, cd2->data.nodemap);
break;
- case CTDB_CONTROL_GET_EVENT_SCRIPT_STATUS:
- verify_ctdb_script_list(cd->data.script_list,
- cd2->data.script_list);
- break;
-
case CTDB_CONTROL_TRAVERSE_KILL:
break;
@@ -1691,12 +1633,6 @@
case CTDB_CONTROL_SET_RECMASTERROLE:
break;
- case CTDB_CONTROL_ENABLE_SCRIPT:
- break;
-
- case CTDB_CONTROL_DISABLE_SCRIPT:
- break;
-
case CTDB_CONTROL_SET_BAN_STATE:
break;
@@ -1837,6 +1773,176 @@
}
/*
+ * Functions to fill and verify eventd protocol structures
+ */
+
+static void fill_ctdb_event_request_data(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_data *r,
+ uint32_t command)
+{
+ r->command = command;
+
+ switch (command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ r->data.run = talloc(mem_ctx, struct ctdb_event_request_run);
+ assert(r->data.run != NULL);
+
+ fill_ctdb_event_request_run(mem_ctx, r->data.run);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ r->data.status = talloc(mem_ctx,
+ struct ctdb_event_request_status);
+ assert(r->data.status != NULL);
+
+ fill_ctdb_event_request_status(mem_ctx, r->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ r->data.script_enable = talloc(mem_ctx,
+ struct ctdb_event_request_script_enable);
+ assert(r->data.script_enable != NULL);
+
+ fill_ctdb_event_request_script_enable(mem_ctx,
+ r->data.script_enable);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ r->data.script_disable = talloc(mem_ctx,
+ struct ctdb_event_request_script_disable);
+ assert(r->data.script_disable != NULL);
+
+ fill_ctdb_event_request_script_disable(mem_ctx,
+ r->data.script_disable);
+ break;
+ }
+}
+
+static void verify_ctdb_event_request_data(struct ctdb_event_request_data *r,
+ struct ctdb_event_request_data *r2)
+{
+ assert(r->command == r2->command);
+
+ switch (r->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ verify_ctdb_event_request_run(r->data.run, r2->data.run);
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ verify_ctdb_event_request_status(r->data.status,
+ r2->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ verify_ctdb_event_request_script_enable(r->data.script_enable,
+ r2->data.script_enable);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ verify_ctdb_event_request_script_disable(r->data.script_disable,
+ r2->data.script_disable);
+ break;
+ }
+}
+
+static void fill_ctdb_event_reply_data(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_data *r,
+ uint32_t command)
+{
+ r->command = command;
+ r->result = rand32i();
+
+ switch (command) {
+ case CTDB_EVENT_COMMAND_STATUS:
+ r->data.status = talloc(mem_ctx,
+ struct ctdb_event_reply_status);
+ assert(r->data.status != NULL);
+
+ fill_ctdb_event_reply_status(mem_ctx, r->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ r->data.script_list = talloc(mem_ctx,
+ struct ctdb_event_reply_script_list);
+ assert(r->data.script_list != NULL);
+
+ fill_ctdb_event_reply_script_list(mem_ctx,
+ r->data.script_list);
+ break;
+ }
+}
+
+static void verify_ctdb_event_reply_data(struct ctdb_event_reply_data *r,
+ struct ctdb_event_reply_data *r2)
+{
+ assert(r->command == r2->command);
+ assert(r->result == r2->result);
+
+ switch (r->command) {
+ case CTDB_EVENT_COMMAND_RUN:
+ break;
+
+ case CTDB_EVENT_COMMAND_STATUS:
+ verify_ctdb_event_reply_status(r->data.status,
+ r2->data.status);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_LIST:
+ verify_ctdb_event_reply_script_list(r->data.script_list,
+ r2->data.script_list);
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
+ break;
+
+ case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
+ break;
+ }
+}
+
+static void verify_ctdb_event_header(struct ctdb_event_header *h,
+ struct ctdb_event_header *h2)
+{
+ verify_buffer(h, h2, ctdb_event_header_len(h));
+}
+
+static void fill_ctdb_event_request(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *r,
+ uint32_t command)
+{
+ ctdb_event_header_fill(&r->header, rand());
+ fill_ctdb_event_request_data(mem_ctx, &r->rdata, command);
+}
+
+static void verify_ctdb_event_request(struct ctdb_event_request *r,
+ struct ctdb_event_request *r2)
+{
+ verify_ctdb_event_header(&r->header, &r2->header);
+ verify_ctdb_event_request_data(&r->rdata, &r2->rdata);
+}
+
+static void fill_ctdb_event_reply(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply *r,
+ uint32_t command)
+{
+ ctdb_event_header_fill(&r->header, rand());
+ fill_ctdb_event_reply_data(mem_ctx, &r->rdata, command);
+}
+
+static void verify_ctdb_event_reply(struct ctdb_event_reply *r,
+ struct ctdb_event_reply *r2)
+{
+ verify_ctdb_event_header(&r->header, &r2->header);
+ verify_ctdb_event_reply_data(&r->rdata, &r2->rdata);
+}
+
+/*
* Functions to test marshalling
*/
@@ -2263,6 +2369,179 @@
talloc_free(mem_ctx);
}
+/*
+ * Functions to test eventd protocol marshalling
+ */
+
+static void test_ctdb_event_header(void)
+{
+ TALLOC_CTX *mem_ctx;
+ size_t buflen;
+ struct ctdb_event_header h, h2;
+ int ret;
+
+ printf("ctdb_event_header\n");
+ fflush(stdout);
+
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ ctdb_event_header_fill(&h, REQID);
+
+ buflen = ctdb_event_header_len(&h);
+ ctdb_event_header_push(&h, BUFFER);
+ ret = ctdb_event_header_pull(BUFFER, buflen, mem_ctx, &h2);
+ assert(ret == 0);
+
+ verify_ctdb_event_header(&h, &h2);
+
+ talloc_free(mem_ctx);
+}
+
+#define NUM_COMMANDS 5
+
+static void test_event_request_data(void)
+{
+ TALLOC_CTX *mem_ctx;
+ size_t buflen;
+ struct ctdb_event_request_data rd, rd2;
+ uint32_t command;
+ int ret;
+
+ printf("ctdb_event_request_data\n");
+ fflush(stdout);
+
+ for (command=1; command<=NUM_COMMANDS; command++) {
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ printf("%u.. ", command);
+ fflush(stdout);
+ fill_ctdb_event_request_data(mem_ctx, &rd, command);
+ buflen = ctdb_event_request_data_len(&rd);
+ ctdb_event_request_data_push(&rd, BUFFER);
+ ret = ctdb_event_request_data_pull(BUFFER, buflen, mem_ctx, &rd2);
+ assert(ret == 0);
+ verify_ctdb_event_request_data(&rd, &rd2);
+
+ talloc_free(mem_ctx);
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static void test_event_reply_data(void)
+{
+ TALLOC_CTX *mem_ctx;
+ size_t buflen;
+ struct ctdb_event_reply_data rd, rd2;
+ uint32_t command;
+ int ret;
+
+ printf("ctdb_event_reply_data\n");
+ fflush(stdout);
+
+ for (command=1; command<=NUM_COMMANDS; command++) {
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ printf("%u.. ", command);
+ fflush(stdout);
+ fill_ctdb_event_reply_data(mem_ctx, &rd, command);
+ buflen = ctdb_event_reply_data_len(&rd);
+ ctdb_event_reply_data_push(&rd, BUFFER);
+ ret = ctdb_event_reply_data_pull(BUFFER, buflen, mem_ctx, &rd2);
+ assert(ret == 0);
+ verify_ctdb_event_reply_data(&rd, &rd2);
+
+ talloc_free(mem_ctx);
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static void test_event_request(void)
+{
+ TALLOC_CTX *mem_ctx;
+ uint8_t *buf;
+ size_t len, buflen;
+ int ret;
+ struct ctdb_event_request r, r2;
+ uint32_t command;
+
+ printf("ctdb_event_request\n");
+ fflush(stdout);
+
+ for (command=1; command<=NUM_COMMANDS; command++) {
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ printf("%u.. ", command);
+ fflush(stdout);
+ fill_ctdb_event_request(mem_ctx, &r, command);
+ buflen = ctdb_event_request_len(&r);
+ buf = talloc_size(mem_ctx, buflen);
+ assert(buf != NULL);
+ len = 0;
+ ret = ctdb_event_request_push(&r, buf, &len);
+ assert(ret == EMSGSIZE);
+ assert(len == buflen);
+ ret = ctdb_event_request_push(&r, buf, &buflen);
+ assert(ret == 0);
+ ret = ctdb_event_request_pull(buf, buflen, mem_ctx, &r2);
+ assert(ret == 0);
+ assert(r2.header.length == buflen);
+ verify_ctdb_event_request(&r, &r2);
+
+ talloc_free(mem_ctx);
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static void test_event_reply(void)
+{
+ TALLOC_CTX *mem_ctx;
+ uint8_t *buf;
+ size_t len, buflen;
+ int ret;
+ struct ctdb_event_reply r, r2;
+ uint32_t command;
+
+ printf("ctdb_event_reply\n");
+ fflush(stdout);
+
+ for (command=1; command<=NUM_COMMANDS; command++) {
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ printf("%u.. ", command);
+ fflush(stdout);
+ fill_ctdb_event_reply(mem_ctx, &r, command);
+ buflen = ctdb_event_reply_len(&r);
+ buf = talloc_size(mem_ctx, buflen);
+ assert(buf != NULL);
+ len = 0;
+ ret = ctdb_event_reply_push(&r, buf, &len);
+ assert(ret == EMSGSIZE);
+ assert(len == buflen);
+ ret = ctdb_event_reply_push(&r, buf, &buflen);
+ assert(ret == 0);
+ ret = ctdb_event_reply_pull(buf, buflen, mem_ctx, &r2);
+ assert(ret == 0);
+ assert(r2.header.length == buflen);
+ verify_ctdb_event_reply(&r, &r2);
+
+ talloc_free(mem_ctx);
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
int main(int argc, char *argv[])
{
if (argc == 2) {
@@ -2286,5 +2565,12 @@
test_req_message_test();
+ test_ctdb_event_header();
+
+ test_event_request_data();
+ test_event_reply_data();
+ test_event_request();
+ test_event_reply();
+
return 0;
}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/protocol_types_test.c samba-4.6.5+dfsg/ctdb/tests/src/protocol_types_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/protocol_types_test.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/protocol_types_test.c 2017-01-11 07:55:14.000000000 +0000
@@ -27,6 +27,7 @@
#include "protocol/protocol_header.c"
#include "protocol/protocol_call.c"
#include "protocol/protocol_control.c"
+#include "protocol/protocol_event.c"
#include "protocol/protocol_message.c"
#include "protocol/protocol_packet.c"
@@ -47,6 +48,11 @@
return val;
}
+static int32_t rand32i(void)
+{
+ return INT_MIN + random();
+}
+
static uint32_t rand32(void)
{
return random();
@@ -909,6 +915,107 @@
}
}
+static void fill_ctdb_event_request_run(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_run *p)
+{
+ p->event = rand_int(CTDB_EVENT_MAX);
+ p->timeout = rand();
+ fill_ctdb_string(mem_ctx, &p->arg_str);
+}
+
+static void verify_ctdb_event_request_run(struct ctdb_event_request_run *p1,
+ struct ctdb_event_request_run *p2)
+{
+ assert(p1->event == p2->event);
+ assert(p1->timeout == p2->timeout);
+ verify_ctdb_string(p1->arg_str, p2->arg_str);
+}
+
+static void fill_ctdb_event_request_status(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_status *p)
+{
+ p->event = rand_int(CTDB_EVENT_MAX);
+ p->state = rand_int(3) + 1;
+}
+
+static void verify_ctdb_event_request_status(
+ struct ctdb_event_request_status *p1,
+ struct ctdb_event_request_status *p2)
+{
+ assert(p1->event == p2->event);
+ assert(p1->state == p2->state);
+}
+
+static void fill_ctdb_event_request_script_enable(
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script_enable *p)
+{
+ fill_ctdb_string(mem_ctx, &p->script_name);
+}
+
+static void verify_ctdb_event_request_script_enable(
+ struct ctdb_event_request_script_enable *p1,
+ struct ctdb_event_request_script_enable *p2)
+{
+ verify_ctdb_string(p1->script_name, p2->script_name);
+}
+
+static void fill_ctdb_event_request_script_disable(
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script_disable *p)
+{
+ fill_ctdb_string(mem_ctx, &p->script_name);
+}
+
+static void verify_ctdb_event_request_script_disable(
+ struct ctdb_event_request_script_disable *p1,
+ struct ctdb_event_request_script_disable *p2)
+{
+ verify_ctdb_string(p1->script_name, p2->script_name);
+}
+
+static void fill_ctdb_event_reply_status(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status *p)
+{
+ if (rand_int(2) == 0) {
+ p->status = 0;
+ p->script_list = talloc(mem_ctx, struct ctdb_script_list);
+ assert(p->script_list != NULL);
+ fill_ctdb_script_list(mem_ctx, p->script_list);
+ } else {
+ p->status = rand32i();
+ p->script_list = NULL;
+ }
+}
+
+static void verify_ctdb_event_reply_status(struct ctdb_event_reply_status *p1,
+ struct ctdb_event_reply_status *p2)
+{
+ assert(p1->status == p2->status);
+ if (p1->script_list == NULL) {
+ assert(p1->script_list == p2->script_list);
+ } else {
+ verify_ctdb_script_list(p1->script_list, p2->script_list);
+ }
+}
+
+static void fill_ctdb_event_reply_script_list(
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_script_list *p)
+{
+ p->script_list = talloc(mem_ctx, struct ctdb_script_list);
+ assert(p->script_list != NULL);
+
+ fill_ctdb_script_list(mem_ctx, p->script_list);
+}
+
+static void verify_ctdb_event_reply_script_list(
+ struct ctdb_event_reply_script_list *p1,
+ struct ctdb_event_reply_script_list *p2)
+{
+ verify_ctdb_script_list(p1->script_list, p2->script_list);
+}
+
#ifndef PROTOCOL_TEST
static void fill_ctdb_election_message(TALLOC_CTX *mem_ctx,
@@ -1017,6 +1124,20 @@
* Functions to test marshalling routines
*/
+static void test_ctdb_int32(void)
+{
+ int32_t p1, p2;
+ size_t buflen;
+ int ret;
+
+ p1 = rand32i();
+ buflen = ctdb_int32_len(p1);
+ ctdb_int32_push(p1, BUFFER);
+ ret = ctdb_int32_pull(BUFFER, buflen, NULL, &p2);
+ assert(ret == 0);
+ assert(p1 == p2);
+}
+
static void test_ctdb_uint32(void)
{
uint32_t p1, p2;
@@ -1206,6 +1327,15 @@
DEFINE_TEST(struct ctdb_disable_message, ctdb_disable_message);
DEFINE_TEST(struct ctdb_g_lock_list, ctdb_g_lock_list);
+DEFINE_TEST(struct ctdb_event_request_run, ctdb_event_request_run);
+DEFINE_TEST(struct ctdb_event_request_status, ctdb_event_request_status);
+DEFINE_TEST(struct ctdb_event_request_script_enable,
+ ctdb_event_request_script_enable);
+DEFINE_TEST(struct ctdb_event_request_script_disable,
+ ctdb_event_request_script_disable);
+DEFINE_TEST(struct ctdb_event_reply_status, ctdb_event_reply_status);
+DEFINE_TEST(struct ctdb_event_reply_script_list, ctdb_event_reply_script_list);
+
static void test_ctdb_rec_buffer_read_write(void)
{
TALLOC_CTX *mem_ctx = talloc_new(NULL);
@@ -1259,6 +1389,7 @@
srandom(seed);
}
+ test_ctdb_int32();
test_ctdb_uint32();
test_ctdb_uint64();
test_ctdb_double();
@@ -1312,6 +1443,13 @@
TEST_FUNC(ctdb_disable_message)();
TEST_FUNC(ctdb_g_lock_list)();
+ TEST_FUNC(ctdb_event_request_run)();
+ TEST_FUNC(ctdb_event_request_status)();
+ TEST_FUNC(ctdb_event_request_script_enable)();
+ TEST_FUNC(ctdb_event_request_script_disable)();
+ TEST_FUNC(ctdb_event_reply_status)();
+ TEST_FUNC(ctdb_event_reply_script_list)();
+
test_ctdb_rec_buffer_read_write();
return 0;
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/rb_perftest.c samba-4.6.5+dfsg/ctdb/tests/src/rb_perftest.c
--- samba-4.5.8+dfsg/ctdb/tests/src/rb_perftest.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/rb_perftest.c 1970-01-01 00:00:00.000000000 +0000
@@ -1,123 +0,0 @@
-/*
- simple rb vs dlist benchmark
-
- Copyright (C) Ronnie Sahlberg 2007
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see .
-*/
-
-#include "includes.h"
-#include "lib/events/events.h"
-#include "lib/util/dlinklist.h"
-#include "system/filesys.h"
-#include "popt.h"
-#include "cmdline.h"
-
-#include
-#include
-#include "common/rb_tree.h"
-
-static struct timeval tp1,tp2;
-
-static void start_timer(void)
-{
- gettimeofday(&tp1,NULL);
-}
-
-static double end_timer(void)
-{
- gettimeofday(&tp2,NULL);
- return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
- (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
-}
-
-
-static int num_records = 1000;
-
-
-struct list_node {
- struct list_node *prev, *next;
-};
-
-/*
- main program
-*/
-int main(int argc, const char *argv[])
-{
- struct poptOption popt_options[] = {
- POPT_AUTOHELP
- POPT_CTDB_CMDLINE
- { "num-records", 'r', POPT_ARG_INT, &num_records, 0, "num_records", "integer" },
- POPT_TABLEEND
- };
- int opt;
- const char **extra_argv;
- int extra_argc = 0;
- int ret;
- poptContext pc;
- struct event_context *ev;
- double elapsed;
- int i;
- trbt_tree_t *tree;
- struct list_node *list, *list_new, *list_head=NULL;
-
- pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
-
- while ((opt = poptGetNextOpt(pc)) != -1) {
- switch (opt) {
- default:
- fprintf(stderr, "Invalid option %s: %s\n",
- poptBadOption(pc, 0), poptStrerror(opt));
- exit(1);
- }
- }
-
- /* setup the remaining options for the main program to use */
- extra_argv = poptGetArgs(pc);
- if (extra_argv) {
- extra_argv++;
- while (extra_argv[extra_argc]) extra_argc++;
- }
-
- ev = event_context_init(NULL);
-
-
- printf("testing tree insert for %d records\n", num_records);
- tree = trbt_create(NULL);
- start_timer();
- for (i=0;inext;list=list->next) {
- /* the events code does a timeval_compare */
- timeval_compare(&tp1, &tp2);
- }
-
- list_new=talloc(NULL, struct list_node);
- DLIST_ADD_AFTER(list_head, list_new, list);
- }
- elapsed=end_timer();
- printf("%f seconds\n",(float)elapsed);
-
- return 0;
-}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/run_proc_test.c samba-4.6.5+dfsg/ctdb/tests/src/run_proc_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/run_proc_test.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/run_proc_test.c 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,105 @@
+/*
+ run_proc test wrapper
+
+ Copyright (C) Amitay Isaacs 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+
+#include
+#include
+
+#include "common/db_hash.c"
+#include "common/run_proc.c"
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ struct run_proc_context *run_ctx;
+ struct timeval tv;
+ char *output;
+ struct run_proc_result result;
+ pid_t pid;
+ int timeout, ret;
+ bool status;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s \n",
+ argv[0]);
+ exit(1);
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ fprintf(stderr, "talloc_new() failed\n");
+ exit(1);
+ }
+
+ ev = tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ fprintf(stderr, "tevent_context_init() failed\n");
+ exit(1);
+ }
+
+ timeout = atoi(argv[1]);
+ if (timeout <= 0) {
+ tv = tevent_timeval_zero();
+ } else {
+ tv = tevent_timeval_current_ofs(timeout, 0);
+ }
+
+ ret = run_proc_init(mem_ctx, ev, &run_ctx);
+ if (ret != 0) {
+ fprintf(stderr, "run_proc_init() failed, ret=%d\n", ret);
+ exit(1);
+ }
+
+ req = run_proc_send(mem_ctx, ev, run_ctx, argv[2], &argv[2], tv);
+ if (req == NULL) {
+ fprintf(stderr, "run_proc_send() failed\n");
+ exit(1);
+ }
+
+ tevent_req_poll(req, ev);
+
+ status = run_proc_recv(req, &ret, &result, &pid, mem_ctx, &output);
+ if (! status) {
+ fprintf(stderr, "run_proc_recv() failed, ret=%d\n", ret);
+ exit(1);
+ }
+
+ if (result.sig > 0) {
+ printf("Process exited with signal %d\n", result.sig);
+ } else if (result.err > 0) {
+ printf("Process exited with error %d\n", result.err);
+ } else {
+ printf("Process exited with status %d\n", result.status);
+ }
+
+ if (pid != -1) {
+ printf("Child = %d\n", pid);
+ }
+
+ if (output != NULL) {
+ printf("Output = (%s)\n", output);
+ }
+
+ talloc_free(mem_ctx);
+
+ exit(0);
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/sock_daemon_test.c samba-4.6.5+dfsg/ctdb/tests/src/sock_daemon_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/sock_daemon_test.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/sock_daemon_test.c 2017-01-26 12:19:08.000000000 +0000
@@ -0,0 +1,992 @@
+/*
+ sock daemon tests
+
+ Copyright (C) Amitay Isaacs 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/wait.h"
+
+#include
+
+#include "common/logging.c"
+#include "common/pkt_read.c"
+#include "common/pkt_write.c"
+#include "common/comm.c"
+#include "common/pidfile.c"
+#include "common/sock_daemon.c"
+#include "common/sock_io.c"
+
+static struct tevent_req *dummy_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sock_client_context *client,
+ uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ return NULL;
+}
+
+static bool dummy_read_recv(struct tevent_req *req, int *perr)
+{
+ if (perr != NULL) {
+ *perr = EINVAL;
+ }
+ return false;
+}
+
+static struct sock_socket_funcs dummy_socket_funcs = {
+ .read_send = dummy_read_send,
+ .read_recv = dummy_read_recv,
+};
+
+static void test1(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ struct sock_daemon_context *sockd;
+ struct stat st;
+ int ret;
+
+ ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE", pidfile,
+ NULL, NULL, &sockd);
+ assert(ret == 0);
+ assert(sockd != NULL);
+
+ ret = stat(pidfile, &st);
+ assert(ret == 0);
+ assert(S_ISREG(st.st_mode));
+
+ ret = sock_daemon_add_unix(sockd, sockpath, &dummy_socket_funcs, NULL);
+ assert(ret == 0);
+
+ ret = stat(sockpath, &st);
+ assert(ret == 0);
+ assert(S_ISSOCK(st.st_mode));
+
+ talloc_free(sockd);
+
+ ret = stat(pidfile, &st);
+ assert(ret == -1);
+
+ ret = stat(sockpath, &st);
+ assert(ret == -1);
+}
+
+static void test2_startup(void *private_data)
+{
+ int fd = *(int *)private_data;
+ int ret = 1;
+ ssize_t nwritten;
+
+ nwritten = write(fd, &ret, sizeof(ret));
+ assert(nwritten == sizeof(ret));
+}
+
+static void test2_reconfigure(void *private_data)
+{
+ int fd = *(int *)private_data;
+ int ret = 2;
+ ssize_t nwritten;
+
+ nwritten = write(fd, &ret, sizeof(ret));
+ assert(nwritten == sizeof(ret));
+}
+
+static void test2_shutdown(void *private_data)
+{
+ int fd = *(int *)private_data;
+ int ret = 3;
+ ssize_t nwritten;
+
+ nwritten = write(fd, &ret, sizeof(ret));
+ assert(nwritten == sizeof(ret));
+}
+
+static struct sock_daemon_funcs test2_funcs = {
+ .startup = test2_startup,
+ .reconfigure = test2_reconfigure,
+ .shutdown = test2_shutdown,
+};
+
+static void test2(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ struct stat st;
+ int fd[2];
+ pid_t pid, pid2;
+ int ret;
+ ssize_t n;
+
+ ret = pipe(fd);
+ assert(ret == 0);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ struct tevent_context *ev;
+ struct sock_daemon_context *sockd;
+
+ close(fd[0]);
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE",
+ pidfile, &test2_funcs, &fd[1], &sockd);
+ assert(ret == 0);
+
+ ret = sock_daemon_add_unix(sockd, sockpath,
+ &dummy_socket_funcs, NULL);
+ assert(ret == 0);
+
+ ret = sock_daemon_run(ev, sockd, -1);
+ assert(ret == EINTR);
+
+ exit(0);
+ }
+
+ close(fd[1]);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 1);
+
+ ret = kill(pid, SIGHUP);
+ assert(ret == 0);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 2);
+
+ ret = kill(pid, SIGUSR1);
+ assert(ret == 0);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 2);
+
+ ret = kill(pid, SIGTERM);
+ assert(ret == 0);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 3);
+
+ pid2 = waitpid(pid, &ret, 0);
+ assert(pid2 == pid);
+ assert(WEXITSTATUS(ret) == 0);
+
+ close(fd[0]);
+
+ ret = stat(pidfile, &st);
+ assert(ret == -1);
+
+ ret = stat(sockpath, &st);
+ assert(ret == -1);
+}
+
+static void test3(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ struct stat st;
+ pid_t pid_watch, pid, pid2;
+ int ret;
+
+ pid_watch = fork();
+ assert(pid_watch != -1);
+
+ if (pid_watch == 0) {
+ sleep(10);
+ exit(0);
+ }
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ struct tevent_context *ev;
+ struct sock_daemon_context *sockd;
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ ret = sock_daemon_setup(mem_ctx, "test3", "file:", "NOTICE",
+ NULL, NULL, NULL, &sockd);
+ assert(ret == 0);
+
+ ret = sock_daemon_add_unix(sockd, sockpath,
+ &dummy_socket_funcs, NULL);
+ assert(ret == 0);
+
+ ret = sock_daemon_run(ev, sockd, pid_watch);
+ assert(ret == ESRCH);
+
+ exit(0);
+ }
+
+ pid2 = waitpid(pid_watch, &ret, 0);
+ assert(pid2 == pid_watch);
+ assert(WEXITSTATUS(ret) == 0);
+
+ pid2 = waitpid(pid, &ret, 0);
+ assert(pid2 == pid);
+ assert(WEXITSTATUS(ret) == 0);
+
+ ret = stat(pidfile, &st);
+ assert(ret == -1);
+
+ ret = stat(sockpath, &st);
+ assert(ret == -1);
+}
+
+struct test4_wait_state {
+};
+
+static void test4_wait_done(struct tevent_req *subreq);
+
+static struct tevent_req *test4_wait_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *private_data)
+{
+ struct tevent_req *req, *subreq;
+ struct test4_wait_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct test4_wait_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = tevent_wakeup_send(state, ev,
+ tevent_timeval_current_ofs(10,0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, test4_wait_done, req);
+
+ return req;
+}
+
+static void test4_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ bool status;
+
+ status = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (! status) {
+ tevent_req_error(req, EIO);
+ } else {
+ tevent_req_done(req);
+ }
+}
+
+static bool test4_wait_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static struct sock_daemon_funcs test4_funcs = {
+ .wait_send = test4_wait_send,
+ .wait_recv = test4_wait_recv,
+};
+
+static void test4(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ struct stat st;
+ pid_t pid, pid2;
+ int ret;
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ struct tevent_context *ev;
+ struct sock_daemon_context *sockd;
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ ret = sock_daemon_setup(mem_ctx, "test4", "file:", "NOTICE",
+ pidfile, &test4_funcs, NULL, &sockd);
+ assert(ret == 0);
+
+ ret = sock_daemon_run(ev, sockd, -1);
+ assert(ret == 0);
+
+ exit(0);
+ }
+
+ pid2 = waitpid(pid, &ret, 0);
+ assert(pid2 == pid);
+ assert(WEXITSTATUS(ret) == 0);
+
+ ret = stat(pidfile, &st);
+ assert(ret == -1);
+
+ ret = stat(sockpath, &st);
+ assert(ret == -1);
+}
+
+#define TEST5_MAX_CLIENTS 10
+
+struct test5_pkt {
+ uint32_t len;
+ int data;
+};
+
+struct test5_client_state {
+ int id;
+ int fd;
+ bool done;
+};
+
+static void test5_client_callback(uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct test5_client_state *state =
+ (struct test5_client_state *)private_data;
+ struct test5_pkt *pkt;
+ ssize_t n;
+ int ret;
+
+ if (buf == NULL) {
+ assert(buflen == 0);
+
+ ret = 0;
+ } else {
+ assert(buflen == sizeof(struct test5_pkt));
+ pkt = (struct test5_pkt *)buf;
+ assert(pkt->len == sizeof(struct test5_pkt));
+
+ ret = pkt->data;
+ }
+
+ assert(state->fd != -1);
+
+ n = write(state->fd, (void *)&ret, sizeof(int));
+ assert(n == sizeof(int));
+
+ state->done = true;
+}
+
+static int test5_client(const char *sockpath, int id)
+{
+ pid_t pid;
+ int fd[2];
+ int ret;
+ ssize_t n;
+
+ ret = pipe(fd);
+ assert(ret == 0);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ struct tevent_context *ev;
+ struct test5_client_state state;
+ struct sock_queue *queue;
+ struct test5_pkt pkt;
+ int conn;
+
+ close(fd[0]);
+
+ ev = tevent_context_init(NULL);
+ assert(ev != NULL);
+
+ conn = sock_connect(sockpath);
+ assert(conn != -1);
+
+ state.id = id;
+ state.fd = fd[1];
+ state.done = false;
+
+ queue = sock_queue_setup(ev, ev, conn,
+ test5_client_callback, &state);
+ assert(queue != NULL);
+
+ pkt.len = 8;
+ pkt.data = 0xbaba;
+
+ ret = sock_queue_write(queue, (uint8_t *)&pkt,
+ sizeof(struct test5_pkt));
+ assert(ret == 0);
+
+ while (! state.done) {
+ tevent_loop_once(ev);
+ }
+
+ close(fd[0]);
+ state.fd = -1;
+
+ sleep(10);
+ exit(0);
+ }
+
+ close(fd[1]);
+
+ ret = 0;
+ n = read(fd[0], &ret, sizeof(ret));
+ if (n == 0) {
+ fprintf(stderr, "client id %d read 0 bytes\n", id);
+ }
+ assert(n == 0 || n == sizeof(ret));
+
+ close(fd[0]);
+
+ return ret;
+}
+
+struct test5_server_state {
+ int num_clients;
+};
+
+static bool test5_connect(struct sock_client_context *client,
+ void *private_data)
+{
+ struct test5_server_state *state =
+ (struct test5_server_state *)private_data;
+
+ if (state->num_clients == TEST5_MAX_CLIENTS) {
+ return false;
+ }
+
+ state->num_clients += 1;
+ assert(state->num_clients <= TEST5_MAX_CLIENTS);
+ return true;
+}
+
+static void test5_disconnect(struct sock_client_context *client,
+ void *private_data)
+{
+ struct test5_server_state *state =
+ (struct test5_server_state *)private_data;
+
+ state->num_clients -= 1;
+ assert(state->num_clients >= 0);
+}
+
+struct test5_read_state {
+ struct test5_pkt reply;
+};
+
+static void test5_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *test5_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sock_client_context *client,
+ uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct test5_server_state *server_state =
+ (struct test5_server_state *)private_data;
+ struct tevent_req *req, *subreq;
+ struct test5_read_state *state;
+ struct test5_pkt *pkt;
+
+ req = tevent_req_create(mem_ctx, &state, struct test5_read_state);
+ assert(req != NULL);
+
+ assert(buflen == sizeof(struct test5_pkt));
+
+ pkt = (struct test5_pkt *)buf;
+ assert(pkt->data == 0xbaba);
+
+ state->reply.len = sizeof(struct test5_pkt);
+ state->reply.data = server_state->num_clients;
+
+ subreq = sock_socket_write_send(state, ev, client,
+ (uint8_t *)&state->reply,
+ state->reply.len);
+ assert(subreq != NULL);
+
+ tevent_req_set_callback(subreq, test5_read_done, req);
+
+ return req;
+}
+
+static void test5_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+ bool status;
+
+ status = sock_socket_write_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool test5_read_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static struct sock_socket_funcs test5_client_funcs = {
+ .connect = test5_connect,
+ .disconnect = test5_disconnect,
+ .read_send = test5_read_send,
+ .read_recv = test5_read_recv,
+};
+
+static void test5_startup(void *private_data)
+{
+ int fd = *(int *)private_data;
+ int ret = 1;
+ ssize_t nwritten;
+
+ nwritten = write(fd, &ret, sizeof(ret));
+ assert(nwritten == sizeof(ret));
+ close(fd);
+}
+
+static struct sock_daemon_funcs test5_funcs = {
+ .startup = test5_startup,
+};
+
+static void test5(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ pid_t pid_server, pid;
+ int fd[2], ret, i;
+ ssize_t n;
+
+ pid = getpid();
+
+ ret = pipe(fd);
+ assert(ret == 0);
+
+ pid_server = fork();
+ assert(pid_server != -1);
+
+ if (pid_server == 0) {
+ struct tevent_context *ev;
+ struct sock_daemon_context *sockd;
+ struct test5_server_state state;
+
+ close(fd[0]);
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ ret = sock_daemon_setup(mem_ctx, "test5", "file:", "NOTICE",
+ pidfile, &test5_funcs, &fd[1], &sockd);
+ assert(ret == 0);
+
+ state.num_clients = 0;
+
+ ret = sock_daemon_add_unix(sockd, sockpath,
+ &test5_client_funcs, &state);
+ assert(ret == 0);
+
+ ret = sock_daemon_run(ev, sockd, pid);
+ assert(ret == EINTR);
+
+ exit(0);
+ }
+
+ close(fd[1]);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 1);
+
+ close(fd[0]);
+
+ for (i=0; i<100; i++) {
+ ret = test5_client(sockpath, i);
+ if (i < TEST5_MAX_CLIENTS) {
+ assert(ret == i+1);
+ } else {
+ assert(ret == 0);
+ }
+ }
+
+ for (i=0; i<100; i++) {
+ pid = wait(&ret);
+ assert(pid != -1);
+ }
+
+ ret = kill(pid_server, SIGTERM);
+ assert(ret == 0);
+}
+
+struct test6_pkt {
+ uint32_t len;
+ uint32_t data;
+};
+
+struct test6_client_state {
+ bool done;
+};
+
+static void test6_client_callback(uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct test6_client_state *state =
+ (struct test6_client_state *)private_data;
+ struct test6_pkt *pkt;
+
+ assert(buflen == sizeof(struct test6_pkt));
+ pkt = (struct test6_pkt *)buf;
+ assert(pkt->len == sizeof(struct test6_pkt));
+ assert(pkt->data == 0xffeeddcc);
+
+ state->done = true;
+}
+
+static void test6_client(const char *sockpath)
+{
+ struct tevent_context *ev;
+ struct test6_client_state state;
+ struct sock_queue *queue;
+ struct test6_pkt pkt;
+ int conn, ret;
+
+ ev = tevent_context_init(NULL);
+ assert(ev != NULL);
+
+ conn = sock_connect(sockpath);
+ assert(conn != -1);
+
+ state.done = false;
+
+ queue = sock_queue_setup(ev, ev, conn,
+ test6_client_callback, &state);
+ assert(queue != NULL);
+
+ pkt.len = 8;
+ pkt.data = 0xaabbccdd;
+
+ ret = sock_queue_write(queue, (uint8_t *)&pkt,
+ sizeof(struct test6_pkt));
+ assert(ret == 0);
+
+ while (! state.done) {
+ tevent_loop_once(ev);
+ }
+
+ talloc_free(ev);
+}
+
+struct test6_server_state {
+ struct sock_daemon_context *sockd;
+ int fd, done;
+};
+
+struct test6_read_state {
+ struct test6_server_state *server_state;
+ struct test6_pkt reply;
+};
+
+static void test6_read_done(struct tevent_req *subreq);
+
+static struct tevent_req *test6_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sock_client_context *client,
+ uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct test6_server_state *server_state =
+ (struct test6_server_state *)private_data;
+ struct tevent_req *req, *subreq;
+ struct test6_read_state *state;
+ struct test6_pkt *pkt;
+
+ req = tevent_req_create(mem_ctx, &state, struct test6_read_state);
+ assert(req != NULL);
+
+ state->server_state = server_state;
+
+ assert(buflen == sizeof(struct test6_pkt));
+
+ pkt = (struct test6_pkt *)buf;
+ assert(pkt->data == 0xaabbccdd);
+
+ state->reply.len = sizeof(struct test6_pkt);
+ state->reply.data = 0xffeeddcc;
+
+ subreq = sock_socket_write_send(state, ev, client,
+ (uint8_t *)&state->reply,
+ state->reply.len);
+ assert(subreq != NULL);
+
+ tevent_req_set_callback(subreq, test6_read_done, req);
+
+ return req;
+}
+
+static void test6_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct test6_read_state *state = tevent_req_data(
+ req, struct test6_read_state);
+ int ret;
+ bool status;
+
+ status = sock_socket_write_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->server_state->done = 1;
+ tevent_req_done(req);
+}
+
+static bool test6_read_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static struct sock_socket_funcs test6_client_funcs = {
+ .read_send = test6_read_send,
+ .read_recv = test6_read_recv,
+};
+
+static void test6_startup(void *private_data)
+{
+ struct test6_server_state *server_state =
+ (struct test6_server_state *)private_data;
+ int ret = 1;
+ ssize_t nwritten;
+
+ nwritten = write(server_state->fd, &ret, sizeof(ret));
+ assert(nwritten == sizeof(ret));
+ close(server_state->fd);
+ server_state->fd = -1;
+}
+
+struct test6_wait_state {
+ struct test6_server_state *server_state;
+};
+
+static void test6_wait_done(struct tevent_req *subreq);
+
+static struct tevent_req *test6_wait_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *private_data)
+{
+ struct tevent_req *req, *subreq;
+ struct test6_wait_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct test6_wait_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->server_state = (struct test6_server_state *)private_data;
+
+ subreq = tevent_wakeup_send(state, ev,
+ tevent_timeval_current_ofs(10,0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, test6_wait_done, req);
+
+ return req;
+}
+
+static void test6_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct test6_wait_state *state = tevent_req_data(
+ req, struct test6_wait_state);
+ bool status;
+
+ status = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (! status) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (state->server_state->done == 0) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool test6_wait_recv(struct tevent_req *req, int *perr)
+{
+ int ret;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static struct sock_daemon_funcs test6_funcs = {
+ .startup = test6_startup,
+ .wait_send = test6_wait_send,
+ .wait_recv = test6_wait_recv,
+};
+
+static void test6(TALLOC_CTX *mem_ctx, const char *pidfile,
+ const char *sockpath)
+{
+ pid_t pid_server, pid;
+ int fd[2], ret;
+ ssize_t n;
+
+ pid = getpid();
+
+ ret = pipe(fd);
+ assert(ret == 0);
+
+ pid_server = fork();
+ assert(pid_server != -1);
+
+ if (pid_server == 0) {
+ struct tevent_context *ev;
+ struct sock_daemon_context *sockd;
+ struct test6_server_state server_state = { 0 };
+
+ close(fd[0]);
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ server_state.fd = fd[1];
+
+ ret = sock_daemon_setup(mem_ctx, "test6", "file:", "NOTICE",
+ pidfile, &test6_funcs, &server_state,
+ &sockd);
+ assert(ret == 0);
+
+ server_state.sockd = sockd;
+ server_state.done = 0;
+
+ ret = sock_daemon_add_unix(sockd, sockpath,
+ &test6_client_funcs, &server_state);
+ assert(ret == 0);
+
+ ret = sock_daemon_run(ev, sockd, pid);
+ assert(ret == 0);
+
+ exit(0);
+ }
+
+ close(fd[1]);
+
+ n = read(fd[0], &ret, sizeof(ret));
+ assert(n == sizeof(ret));
+ assert(ret == 1);
+
+ close(fd[0]);
+
+ test6_client(sockpath);
+
+ pid = wait(&ret);
+ assert(pid != -1);
+}
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ const char *pidfile, *sockpath;
+ int num;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s \n", argv[0]);
+ exit(1);
+ }
+
+ pidfile = argv[1];
+ sockpath = argv[2];
+ num = atoi(argv[3]);
+
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ switch (num) {
+ case 1:
+ test1(mem_ctx, pidfile, sockpath);
+ break;
+
+ case 2:
+ test2(mem_ctx, pidfile, sockpath);
+ break;
+
+ case 3:
+ test3(mem_ctx, pidfile, sockpath);
+ break;
+
+ case 4:
+ test4(mem_ctx, pidfile, sockpath);
+ break;
+
+ case 5:
+ test5(mem_ctx, pidfile, sockpath);
+ break;
+
+ case 6:
+ test6(mem_ctx, pidfile, sockpath);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown test number %d\n", num);
+ }
+
+ return 0;
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/sock_io_test.c samba-4.6.5+dfsg/ctdb/tests/src/sock_io_test.c
--- samba-4.5.8+dfsg/ctdb/tests/src/sock_io_test.c 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/sock_io_test.c 2017-01-26 12:18:16.000000000 +0000
@@ -0,0 +1,283 @@
+/*
+ sock I/O tests
+
+ Copyright (C) Amitay Isaacs 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/wait.h"
+
+#include
+
+#include "common/sock_io.c"
+
+static int socket_init(const char *sockpath)
+{
+ struct sockaddr_un addr;
+ int fd, ret;
+ size_t len;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ len = strlcpy(addr.sun_path, sockpath, sizeof(addr.sun_path));
+ assert(len < sizeof(addr.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ assert(fd != -1);
+
+ ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+ assert(ret != -1);
+
+ ret = listen(fd, 10);
+ assert(ret != -1);
+
+ return fd;
+}
+
+static void test1_writer(int fd)
+{
+ uint8_t buf[1024];
+ ssize_t nwritten;
+ uint32_t len;
+
+ for (len = 10; len < 1000; len += 10) {
+ int value = len / 10;
+ uint32_t buflen = len + sizeof(uint32_t);
+
+ memset(buf, value, buflen);
+ memcpy(buf, &buflen, sizeof(uint32_t));
+
+ nwritten = sys_write(fd, buf, buflen);
+ assert(nwritten == buflen);
+ }
+}
+
+struct test1_reader_state {
+ size_t pkt_len;
+ bool done;
+};
+
+static void test1_reader(uint8_t *buf, size_t buflen, void *private_data)
+{
+ struct test1_reader_state *state =
+ (struct test1_reader_state *)private_data;
+
+ if (buflen == 0) {
+ state->done = true;
+ return;
+ }
+
+ assert(buflen == state->pkt_len);
+
+ state->pkt_len += 10;
+}
+
+static void test1(TALLOC_CTX *mem_ctx, const char *sockpath)
+{
+ struct test1_reader_state state;
+ struct tevent_context *ev;
+ struct sock_queue *queue;
+ pid_t pid;
+ int pfd[2], fd, ret;
+ ssize_t n;
+
+ ret = pipe(pfd);
+ assert(ret == 0);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ int newfd;
+
+ close(pfd[0]);
+
+ fd = socket_init(sockpath);
+ assert(fd != -1);
+
+ ret = 1;
+ n = sys_write(pfd[1], &ret, sizeof(int));
+ assert(n == sizeof(int));
+
+ newfd = accept(fd, NULL, NULL);
+ assert(newfd != -1);
+
+ test1_writer(newfd);
+ close(newfd);
+ unlink(sockpath);
+
+ exit(0);
+ }
+
+ close(pfd[1]);
+
+ n = sys_read(pfd[0], &ret, sizeof(int));
+ assert(n == sizeof(int));
+ assert(ret == 1);
+
+ close(pfd[0]);
+
+ fd = sock_connect(sockpath);
+ assert(fd != -1);
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ state.pkt_len = 10 + sizeof(uint32_t);
+ state.done = false;
+
+ queue = sock_queue_setup(mem_ctx, ev, fd, test1_reader, &state);
+ assert(queue != NULL);
+
+ while (! state.done) {
+ tevent_loop_once(ev);
+ }
+
+ talloc_free(queue);
+ talloc_free(ev);
+
+ pid = wait(&ret);
+ assert(pid != -1);
+}
+
+static void test2_reader(int fd)
+{
+ uint8_t buf[1024];
+ size_t pkt_len = 10 + sizeof(uint32_t);
+ ssize_t n;
+
+ while (1) {
+ n = sys_read(fd, buf, 1024);
+ assert(n != -1);
+
+ if (n == 0) {
+ return;
+ }
+
+ assert(n == pkt_len);
+ pkt_len += 10;
+ }
+}
+
+static void test2_dummy_reader(uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ assert(buflen == -1);
+}
+
+static void test2_writer(struct sock_queue *queue)
+{
+ uint8_t buf[1024];
+ uint32_t len;
+ int ret;
+
+ for (len = 10; len < 1000; len += 10) {
+ int value = len / 10;
+ uint32_t buflen = len + sizeof(uint32_t);
+
+ memset(buf, value, buflen);
+ memcpy(buf, &buflen, sizeof(uint32_t));
+
+ ret = sock_queue_write(queue, buf, buflen);
+ assert(ret == 0);
+ }
+}
+
+static void test2(TALLOC_CTX *mem_ctx, const char *sockpath)
+{
+ struct tevent_context *ev;
+ struct sock_queue *queue;
+ pid_t pid;
+ int pfd[2], fd, ret;
+ ssize_t n;
+
+ ret = pipe(pfd);
+ assert(ret == 0);
+
+ pid = fork();
+ assert(pid != -1);
+
+ if (pid == 0) {
+ int newfd;
+
+ close(pfd[0]);
+
+ fd = socket_init(sockpath);
+ assert(fd != -1);
+
+ ret = 1;
+ n = sys_write(pfd[1], &ret, sizeof(int));
+ assert(n == sizeof(int));
+
+ newfd = accept(fd, NULL, NULL);
+ assert(newfd != -1);
+
+ test2_reader(newfd);
+ close(newfd);
+ unlink(sockpath);
+
+ exit(0);
+ }
+
+ close(pfd[1]);
+
+ n = sys_read(pfd[0], &ret, sizeof(int));
+ assert(n == sizeof(int));
+ assert(ret == 1);
+
+ close(pfd[0]);
+
+ fd = sock_connect(sockpath);
+ assert(fd != -1);
+
+ ev = tevent_context_init(mem_ctx);
+ assert(ev != NULL);
+
+ queue = sock_queue_setup(mem_ctx, ev, fd, test2_dummy_reader, NULL);
+ assert(queue != NULL);
+
+ test2_writer(queue);
+
+ talloc_free(queue);
+ talloc_free(ev);
+
+ pid = wait(&ret);
+ assert(pid != -1);
+}
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ const char *sockpath;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s \n", argv[0]);
+ exit(1);
+ }
+
+ sockpath = argv[1];
+
+ mem_ctx = talloc_new(NULL);
+ assert(mem_ctx != NULL);
+
+ test1(mem_ctx, sockpath);
+ test2(mem_ctx, sockpath);
+
+ return 0;
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/test_options.c samba-4.6.5+dfsg/ctdb/tests/src/test_options.c
--- samba-4.5.8+dfsg/ctdb/tests/src/test_options.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/test_options.c 2017-01-11 07:55:14.000000000 +0000
@@ -88,7 +88,7 @@
static bool verify_options_basic(struct test_options *opts)
{
- enum debug_level log_level;
+ int log_level;
bool status;
status = debug_level_parse(opts->debugstr, &log_level);
@@ -98,7 +98,7 @@
return false;
}
- DEBUGLEVEL = debug_level_to_int(log_level);
+ DEBUGLEVEL = log_level;
return true;
}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/src/transaction_loop.c samba-4.6.5+dfsg/ctdb/tests/src/transaction_loop.c
--- samba-4.5.8+dfsg/ctdb/tests/src/transaction_loop.c 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/src/transaction_loop.c 2017-01-11 07:55:14.000000000 +0000
@@ -162,6 +162,7 @@
ret = ctdb_transaction_fetch_record(state->h, state->key,
state, &data);
if (ret != 0) {
+ fprintf(stderr, "transaction fetch record failed\n");
tevent_req_error(req, ret);
return;
}
@@ -246,6 +247,7 @@
status = tevent_wakeup_recv(subreq);
TALLOC_FREE(subreq);
if (! status) {
+ fprintf(stderr, "tevent wakeup failed\n");
tevent_req_error(req, EIO);
return;
}
@@ -390,7 +392,7 @@
status = transaction_loop_recv(req, &ret);
if (! status) {
- fprintf(stderr, "transaction loop test failed\n");
+ fprintf(stderr, "transaction loop test failed, ret=%d\n", ret);
exit(1);
}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.001.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.001.sh
--- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.001.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.001.sh 2017-01-11 07:55:14.000000000 +0000
@@ -4,7 +4,7 @@
define_test "3 nodes, 3 -> 1 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < 2 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < all healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < all healthy, info logging"
-export CTDB_TEST_LOGLEVEL=3
+export CTDB_TEST_LOGLEVEL=INFO
required_result < 192.168.20.253 -> 0 [+0]
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.005.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.005.sh
--- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.005.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.005.sh 2017-01-11 07:55:14.000000000 +0000
@@ -4,7 +4,7 @@
define_test "3 nodes, 1 -> all healthy, debug logging"
-export CTDB_TEST_LOGLEVEL=4
+export CTDB_TEST_LOGLEVEL=DEBUG
required_result < 1 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < 2 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < all healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < all disconnected"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result <3 unhealthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result <3 unhealthy, NoIPHostOnAllDisabled"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
export CTDB_SET_NoIPHostOnAllDisabled=1
required_result <3 unhealthy, NoIPHostOnAllDisabled on 2"
-
-export CTDB_TEST_LOGLEVEL=0
-
-required_result <2 unhealthy, NoIPHostOnAllDisabled on 2 others"
-
-export CTDB_TEST_LOGLEVEL=0
-
-required_result <3 unhealthy, all IPs assigned, split NoIPTakeover"
+define_test "3 nodes, 2->3 unhealthy, all IPs assigned, NoIPTakeover"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
-# We expect 1/2 the IPs to move, but the rest to stay (as opposed to
-# NoIPHostOnAllDisabled)
+# We expect the IPs stay where they are (as opposed to
+# NoIPHostOnAllDisabled). IPs are hosted when all nodes are disabled,
+# but they have nowhere else to go because of NoIPTakeover.
required_result <3 unhealthy"
-export CTDB_TEST_LOGLEVEL=4
+export CTDB_TEST_LOGLEVEL=DEBUG
required_result <4 unhealthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < 2 [+9216]
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.030.sh samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.030.sh
--- samba-4.5.8+dfsg/ctdb/tests/takeover/lcp2.030.sh 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/lcp2.030.sh 2017-01-11 07:55:14.000000000 +0000
@@ -4,7 +4,7 @@
define_test "900 IPs, 5 nodes, 0 -> 5 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
required_result < 4 healthy"
-export CTDB_TEST_LOGLEVEL=4
+export CTDB_TEST_LOGLEVEL=DEBUG
required_result < 3 -> 4 healthy"
-export CTDB_TEST_LOGLEVEL=0
+export CTDB_TEST_LOGLEVEL=ERR
set -e
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ctdb_takeover.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,888 +0,0 @@
-#!/usr/bin/env python
-
-# ctdb ip takeover code
-
-# Copyright (C) Martin Schwenke, Ronnie Sahlberg 2010, 2011
-
-# Based on original CTDB C code:
-#
-# Copyright (C) Ronnie Sahlberg 2007
-# Copyright (C) Andrew Tridgell 2007
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, see .
-
-
-import os
-import sys
-# Use optparse since newer argparse not available in RHEL5/EPEL.
-from optparse import OptionParser
-import copy
-import random
-import itertools
-
-# For parsing IP addresses
-import socket
-import struct
-
-# For external algorithm
-import subprocess
-import re
-
-options = None
-
-def process_args(extra_options=[]):
- global options
-
- parser = OptionParser(option_list=extra_options)
-
- parser.add_option("--nd",
- action="store_false", dest="deterministic_public_ips",
- default=True,
- help="turn off deterministic_public_ips")
- parser.add_option("--ni",
- action="store_true", dest="no_ip_failback", default=False,
- help="turn on no_ip_failback")
- parser.add_option("-L", "--lcp2",
- action="store_true", dest="lcp2", default=False,
- help="use LCP2 IP rebalancing algorithm [default: %default]")
- parser.add_option("-e", "--external",
- action="store_true", dest="external", default=False,
- help="use external test program to implement IP allocation algorithm [default: %default]")
- parser.add_option("-b", "--balance",
- action="store_true", dest="balance", default=False,
- help="show (im)balance information after each event")
- parser.add_option("-d", "--diff",
- action="store_true", dest="diff", default=False,
- help="show IP address movements for each event")
- parser.add_option("-n", "--no-print",
- action="store_false", dest="show", default=True,
- help="don't show IP address layout after each event")
- parser.add_option("-v", "--verbose",
- action="count", dest="verbose", default=0,
- help="print information and actions taken to stdout")
- parser.add_option("-r", "--retries",
- action="store", type="int", dest="retries", default=5,
- help="number of retry loops for rebalancing non-deterministic failback [default: %default]")
- parser.add_option("-i", "--iterations",
- action="store", type="int", dest="iterations",
- default=1000,
- help="number of iterations to run in test [default: %default]")
- parser.add_option("-o", "--odds",
- action="store", type="int", dest="odds", default=4,
- help="make the chances of a failover 1 in ODDS [default: %default]")
- parser.add_option("-A", "--aggressive",
- action="store_true", dest="aggressive", default=False,
- help="apply ODDS to try to flip each node [default: %default]")
-
- def seed_callback(option, opt, value, parser):
- random.seed(value)
- parser.add_option("-s", "--seed",
- action="callback", type="int", callback=seed_callback,
- help="initial random number seed for random events")
-
- parser.add_option("-x", "--exit",
- action="store_true", dest="exit", default=False,
- help="exit on the 1st gratuitous IP move or IP imbalance")
- parser.add_option("-H", "--hard-imbalance-limit",
- action="store", type="int", dest="hard_limit", default=1,
- help="exceeding this limit causes termination [default: %default]")
- parser.add_option("-S", "--soft-imbalance-limit",
- action="store", type="int", dest="soft_limit", default=1,
- help="exceeding this limit increments a counter [default: %default]")
-
- (options, args) = parser.parse_args()
-
- if len(args) != 0:
- parser.error("too many arguments")
-
- # Could use a callback for this or change the default, but
- # laziness is sometimes a virtue. ;-)
- if options.lcp2:
- options.deterministic_public_ips = False
-
-def print_begin(t, delim='='):
- print delim * 40
- print "%s:" % (t)
-
-def print_end():
- print "-" * 40
-
-def verbose_begin(t):
- if options.verbose > 0:
- print_begin(t)
-
-def verbose_end():
- if options.verbose > 0:
- print_end()
-
-def verbose_print(t):
- if options.verbose > 0:
- if not type(t) == list:
- t = [t]
- if t != []:
- print "\n".join([str(i) for i in t])
-
-# more than this and we switch to the logging module... :-)
-def debug_begin(t):
- if options.verbose > 1:
- print_begin(t, '-')
-
-def debug_end():
- if options.verbose > 1:
- print_end()
-
-def debug_print(t):
- if options.verbose > 1:
- if not type(t) == list:
- t = [t]
- if t != []:
- print "\n".join([str(i) for i in t])
-
-def ip_to_list_of_ints(ip):
- # Be lazy... but only expose errors in IPv4 addresses, since
- # they'll be more commonly used. :-)
- try:
- l = socket.inet_pton(socket.AF_INET6, ip)
- except:
- # Pad with leading 0s. This makes IPv4 addresses comparable
- # with IPv6 but reduces the overall effectiveness of the
- # algorithm. The alternative would be to treat these
- # addresses separately while trying to keep all the IPs in
- # overall balance.
- l = "".join(itertools.repeat("\0", 12)) + \
- socket.inet_pton(socket.AF_INET, ip)
-
- return map(lambda x: struct.unpack('B', x)[0], l)
-
-def ip_distance(ip1, ip2):
- """Calculate the distance between 2 IPs.
-
- This is the length of the longtest common prefix between the IPs.
- It is calculated by XOR-ing the 2 IPs together and counting the
- number of leading zeroes."""
-
- distance = 0
- for (o1, o2) in zip(ip_to_list_of_ints(ip1), ip_to_list_of_ints(ip2)):
- # XOR this pair of octets
- x = o1 ^ o2
- # count number leading zeroes
- if x == 0:
- distance += 8
- else:
- # bin() gives minimal length '0bNNN' string
- distance += (8 - (len(bin(x)) - 2))
- break
-
- return distance
-
-def ip_distance_2_sum(ip, ips):
- """Calculate the IP distance for the given IP relative to IPs.
-
- This could be made more efficient by insering ip_distance_2 into
- the loop in this function. However, that would result in some
- loss of clarity and also will not be necessary in a C
- implemntation."""
-
- sum = 0
- for i in ips:
- sum += ip_distance(ip, i) ** 2
-
- return sum
-
-def imbalance_metric(ips):
- """Return the imbalance metric for a group of IPs.
-
- This is the sum of squares of the IP distances between each pair of IPs."""
- if len(ips) > 1:
- (h, t) = (ips[0], ips[1:])
- return ip_distance_2_sum(h, t) + imbalance_metric(t)
- else:
- return 0
-
-def mean(l):
- return float(sum(l))/len(l)
-
-class Node(object):
- def __init__(self, public_addresses):
- # List of list allows groups of IPs to be passed in. They're
- # not actually used in the algorithm but are just used by
- # calculate_imbalance() for checking the simulation. Note
- # that people can pass in garbage and make this code
- # fail... but we're all friends here in simulation world...
- # :-)
- if type(public_addresses[0]) is str:
- self.public_addresses = set(public_addresses)
- self.ip_groups = []
- else:
- # flatten
- self.public_addresses = set([i for s in public_addresses for i in s])
- self.ip_groups = public_addresses
-
- self.current_addresses = set()
- self.healthy = True
- self.imbalance = -1
-
- def __str__(self):
- return "%s %s%s" % \
- ("*" if len(self.public_addresses) == 0 else \
- (" " if self.healthy else "#"),
- sorted(list(self.current_addresses)),
- " %d" % self.imbalance if options.lcp2 else "")
-
- def can_node_serve_ip(self, ip):
- return ip in self.public_addresses
-
- def node_ip_coverage(self, ips=None):
- return len([a for a in self.current_addresses if ips == None or a in ips])
-
- def set_imbalance(self, imbalance=-1):
- """Set the imbalance metric to the given value. If none given
- then calculate it."""
-
- if imbalance != -1:
- self.imbalance = imbalance
- else:
- self.imbalance = imbalance_metric(list(self.current_addresses))
-
- def get_imbalance(self):
- return self.imbalance
-
-class Cluster(object):
- def __init__(self):
- self.nodes = []
- self.deterministic_public_ips = options.deterministic_public_ips
- self.no_ip_failback = options.no_ip_failback
- self.all_public_ips = set()
-
- # Statistics
- self.ip_moves = []
- self.grat_ip_moves = []
- self.imbalance = []
- self.imbalance_groups = []
- self.imbalance_count = 0
- self.imbalance_groups_count = itertools.repeat(0)
- self.imbalance_metric = []
- self.events = -1
- self.num_unhealthy = []
-
- self.prev = None
-
- def __str__(self):
- return "\n".join(["%2d %s" % (i, n) \
- for (i, n) in enumerate(self.nodes)])
-
- # This is naive. It assumes that IP groups are indicated by the
- # 1st node having IP groups.
- def have_ip_groups(self):
- return (len(self.nodes[0].ip_groups) > 0)
-
- def print_statistics(self):
- print_begin("STATISTICS")
- print "Events: %6d" % self.events
- print "Total IP moves: %6d" % sum(self.ip_moves)
- print "Gratuitous IP moves: %6d" % sum(self.grat_ip_moves)
- print "Max imbalance: %6d" % max(self.imbalance)
- if self.have_ip_groups():
- print "Max group imbalance counts: ", map(max, zip(*self.imbalance_groups))
- print "Mean imbalance: %f" % mean(self.imbalance)
- if self.have_ip_groups():
- print "Mean group imbalances counts: ", map(mean, zip(*self.imbalance_groups))
- print "Final imbalance: %6d" % self.imbalance[-1]
- if self.have_ip_groups():
- print "Final group imbalances: ", self.imbalance_groups[-1]
- if options.lcp2:
- print "Max LCP2 imbalance : %6d" % max(self.imbalance_metric)
- print "Soft imbalance count: %6d" % self.imbalance_count
- if self.have_ip_groups():
- print "Soft imbalance group counts: ", self.imbalance_groups_count
- if options.lcp2:
- print "Final LCP2 imbalance : %6d" % self.imbalance_metric[-1]
- print "Maximum unhealthy: %6d" % max(self.num_unhealthy)
- print_end()
-
- def find_pnn_with_ip(self, ip):
- for (i, n) in enumerate(self.nodes):
- if ip in n.current_addresses:
- return i
- return -1
-
- def quietly_remove_ip(self, ip):
- # Remove address from old node.
- old = self.find_pnn_with_ip(ip)
- if old != -1:
- self.nodes[old].current_addresses.remove(ip)
-
- def add_node(self, node):
- self.nodes.append(node)
- self.all_public_ips |= node.public_addresses
-
- def healthy(self, *pnns):
- verbose_begin("HEALTHY")
-
- for pnn in pnns:
- self.nodes[pnn].healthy = True
- verbose_print(pnn)
-
- verbose_end()
-
- def unhealthy(self, *pnns):
-
- verbose_begin("UNHEALTHY")
-
- for pnn in pnns:
- self.nodes[pnn].healthy = False
- verbose_print(pnn)
-
- verbose_end()
-
- def do_something_random(self):
-
- """Make random node(s) healthy or unhealthy.
-
- If options.aggressive is False then: If all nodes are healthy
- or unhealthy, then invert one of them; otherwise, there's a 1
- in options.odds chance of making another node unhealthy.
-
- If options.aggressive is True then: For each node there is a 1
- in options.odds chance of flipping the state of that node
- between healthy and unhealthy."""
-
- if not options.aggressive:
- num_nodes = len(self.nodes)
- healthy_pnns = [i for (i,n) in enumerate(self.nodes) if n.healthy]
- num_healthy = len(healthy_pnns)
-
- if num_nodes == num_healthy:
- self.unhealthy(random.randint(0, num_nodes-1))
- elif num_healthy == 0:
- self.healthy(random.randint(0, num_nodes-1))
- elif random.randint(1, options.odds) == 1:
- self.unhealthy(random.choice(healthy_pnns))
- else:
- all_pnns = range(num_nodes)
- unhealthy_pnns = sorted(list(set(all_pnns) - set(healthy_pnns)))
- self.healthy(random.choice(unhealthy_pnns))
- else:
- # We need to make at least one change or we retry...x
- changed = False
- while not changed:
- for (pnn, n) in enumerate(self.nodes):
- if random.randint(1, options.odds) == 1:
- changed = True
- if n.healthy:
- self.unhealthy(pnn)
- else:
- self.healthy(pnn)
-
- def random_iterations(self):
- i = 1
- while i <= options.iterations:
- verbose_begin("EVENT %d" % i)
- verbose_end()
- self.do_something_random()
- if self.recover() and options.exit:
- break
- i += 1
-
- self.print_statistics()
-
- def imbalance_for_ips(self, ips):
-
- imbalance = 0
-
- maxnode = -1
- minnode = -1
-
- for ip in ips:
- for (i, n) in enumerate(self.nodes):
-
- if not n.healthy or not n.can_node_serve_ip(ip):
- continue
-
- num = n.node_ip_coverage(ips)
-
- if maxnode == -1 or num > maxnum:
- maxnode = i
- maxnum = num
-
- if minnode == -1 or num < minnum:
- minnode = i
- minnum = num
-
- if maxnode == -1 or minnode == -1:
- continue
-
- i = maxnum - minnum
- #if i < 2:
- # i = 0
- imbalance = max([imbalance, i])
-
- return imbalance
-
-
- def calculate_imbalance(self):
-
- # First, do all the assigned IPs.
- assigned = sorted([ip
- for n in self.nodes
- for ip in n.current_addresses])
-
- i = self.imbalance_for_ips(assigned)
-
- ig = []
- # FIXME? If dealing with IP groups, assume the nodes are all
- # the same.
- for ips in self.nodes[0].ip_groups:
- gi = self.imbalance_for_ips(ips)
- ig.append(gi)
-
- return (i, ig)
-
-
- def diff(self):
- """Calculate differences in IP assignments between self and prev.
-
- Gratuitous IP moves (from a healthy node to a healthy node)
- are prefixed by !!."""
-
- ip_moves = 0
- grat_ip_moves = 0
- details = []
-
- for (new, n) in enumerate(self.nodes):
- for ip in n.current_addresses:
- old = self.prev.find_pnn_with_ip(ip)
- if old != new:
- ip_moves += 1
- if old != -1 and \
- self.prev.nodes[new].healthy and \
- self.nodes[new].healthy and \
- self.nodes[old].healthy and \
- self.prev.nodes[old].healthy:
- prefix = "!!"
- grat_ip_moves += 1
- else:
- prefix = " "
- details.append("%s %s: %d -> %d" %
- (prefix, ip, old, new))
-
- return (ip_moves, grat_ip_moves, details)
-
- def find_takeover_node(self, ip):
-
- pnn = -1
- min = 0
- for (i, n) in enumerate(self.nodes):
- if not n.healthy:
- continue
-
- if not n.can_node_serve_ip(ip):
- continue
-
- num = n.node_ip_coverage()
-
- if (pnn == -1):
- pnn = i
- min = num
- else:
- if num < min:
- pnn = i
- min = num
-
- if pnn == -1:
- verbose_print("Could not find node to take over public address %s" % ip)
- return False
-
- self.nodes[pnn].current_addresses.add(ip)
-
- verbose_print("%s -> %d" % (ip, pnn))
- return True
-
- def basic_allocate_unassigned(self):
-
- assigned = set([ip for n in self.nodes for ip in n.current_addresses])
- unassigned = sorted(list(self.all_public_ips - assigned))
-
- for ip in unassigned:
- self.find_takeover_node(ip)
-
- def basic_failback(self, retries_l):
-
- assigned = sorted([ip
- for n in self.nodes
- for ip in n.current_addresses])
- for ip in assigned:
-
- maxnode = -1
- minnode = -1
- for (i, n) in enumerate(self.nodes):
- if not n.healthy:
- continue
-
- if not n.can_node_serve_ip(ip):
- continue
-
- num = n.node_ip_coverage()
-
- if maxnode == -1:
- maxnode = i
- maxnum = num
- else:
- if num > maxnum:
- maxnode = i
- maxnum = num
- if minnode == -1:
- minnode = i
- minnum = num
- else:
- if num < minnum:
- minnode = i
- minnum = num
-
- if maxnode == -1:
- print "Could not find maxnode. May not be able to serve ip", ip
- continue
-
- #if self.deterministic_public_ips:
- # continue
-
- if maxnum > minnum + 1 and retries_l[0] < options.retries:
- # Remove the 1st ip from maxnode
- t = sorted(list(self.nodes[maxnode].current_addresses))
- realloc = t[0]
- verbose_print("%s <- %d" % (realloc, maxnode))
- self.nodes[maxnode].current_addresses.remove(realloc)
- # Redo the outer loop.
- retries_l[0] += 1
- return True
-
- return False
-
-
- def lcp2_allocate_unassigned(self):
-
- # Assign as many unassigned addresses as possible. Keep
- # selecting the optimal assignment until we don't manage to
- # assign anything.
- assigned = set([ip for n in self.nodes for ip in n.current_addresses])
- unassigned = sorted(list(self.all_public_ips - assigned))
-
- should_loop = True
- while len(unassigned) > 0 and should_loop:
- should_loop = False
-
- debug_begin(" CONSIDERING MOVES (UNASSIGNED)")
-
- minnode = -1
- mindsum = 0
- minip = None
-
- for ip in unassigned:
- for dstnode in range(len(self.nodes)):
- if self.nodes[dstnode].can_node_serve_ip(ip) and \
- self.nodes[dstnode].healthy:
- dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses)
- dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum
- debug_print(" %s -> %d [+%d]" % \
- (ip,
- dstnode,
- dstimbl - self.nodes[dstnode].get_imbalance()))
-
- if (minnode == -1) or (dstdsum < mindsum):
- minnode = dstnode
- minimbl = dstimbl
- mindsum = dstdsum
- minip = ip
- should_loop = True
- debug_end()
-
- if minnode != -1:
- self.nodes[minnode].current_addresses.add(minip)
- self.nodes[minnode].set_imbalance(self.nodes[minnode].get_imbalance() + mindsum)
- verbose_print("%s -> %d [+%d]" % (minip, minnode, mindsum))
- unassigned.remove(minip)
-
- for ip in unassigned:
- verbose_print("Could not find node to take over public address %s" % ip)
-
- def lcp2_failback(self, targets):
-
- # Get the node with the highest imbalance metric.
- srcnode = -1
- maximbl = 0
- for (pnn, n) in enumerate(self.nodes):
- b = n.get_imbalance()
- if (srcnode == -1) or (b > maximbl):
- srcnode = pnn
- maximbl = b
-
- # This means that all nodes had 0 or 1 addresses, so can't
- # be imbalanced.
- if maximbl == 0:
- return False
-
- # We'll need this a few times...
- ips = self.nodes[srcnode].current_addresses
-
- # Find an IP and destination node that best reduces imbalance.
- optimum = None
- debug_begin(" CONSIDERING MOVES FROM %d [%d]" % (srcnode, maximbl))
- for ip in ips:
- # What is this IP address costing the source node?
- srcdsum = ip_distance_2_sum(ip, ips - set([ip]))
- srcimbl = maximbl - srcdsum
-
- # Consider this IP address would cost each potential
- # destination node. Destination nodes are limited to
- # those that are newly healthy, since we don't want to
- # do gratuitous failover of IPs just to make minor
- # balance improvements.
- for dstnode in targets:
- if self.nodes[dstnode].can_node_serve_ip(ip) and \
- self.nodes[dstnode].healthy:
- dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses)
- dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum
- debug_print(" %d [%d] -> %s -> %d [+%d]" % \
- (srcnode,
- srcimbl - self.nodes[srcnode].get_imbalance(),
- ip,
- dstnode,
- dstimbl - self.nodes[dstnode].get_imbalance()))
-
- if (dstimbl < maximbl) and (dstdsum < srcdsum):
- if optimum is None:
- optimum = (ip, srcnode, srcimbl, dstnode, dstimbl)
- else:
- (x, sn, si, dn, di) = optimum
- if (srcimbl + dstimbl) < (si + di):
- optimum = (ip, srcnode, srcimbl, dstnode, dstimbl)
- debug_end()
-
- if optimum is not None:
- # We found a move that makes things better...
- (ip, srcnode, srcimbl, dstnode, dstimbl) = optimum
- ini_srcimbl = self.nodes[srcnode].get_imbalance()
- ini_dstimbl = self.nodes[dstnode].get_imbalance()
-
- self.nodes[srcnode].current_addresses.remove(ip)
- self.nodes[srcnode].set_imbalance(srcimbl)
-
- self.nodes[dstnode].current_addresses.add(ip)
- self.nodes[dstnode].set_imbalance(dstimbl)
-
- verbose_print("%d [%d] -> %s -> %d [+%d]" % \
- (srcnode,
- srcimbl - ini_srcimbl,
- ip,
- dstnode,
- dstimbl - ini_dstimbl))
-
- return True
-
- return False
-
- def ctdb_takeover_run_python(self):
-
- # Don't bother with the num_healthy stuff. It is an
- # irrelevant detail.
-
- # We just keep the allocate IPs in the current_addresses field
- # of the node. This needs to readable, not efficient!
-
- if self.deterministic_public_ips:
- # Remap everything.
- addr_list = sorted(list(self.all_public_ips))
- for (i, ip) in enumerate(addr_list):
- self.quietly_remove_ip(ip)
- # Add addresses to new node.
- pnn = i % len(self.nodes)
- self.nodes[pnn].current_addresses.add(ip)
- verbose_print("%s -> %d" % (ip, pnn))
-
- # Remove public addresses from unhealthy nodes.
- for (pnn, n) in enumerate(self.nodes):
- if not n.healthy:
- verbose_print(["%s <- %d" % (ip, pnn)
- for ip in n.current_addresses])
- n.current_addresses = set()
-
- # If a node can't serve an assigned address then remove it.
- for n in self.nodes:
- verbose_print(["%s <- %d" % (ip, pnn)
- for ip in n.current_addresses - n.public_addresses])
- n.current_addresses &= n.public_addresses
-
- if options.lcp2:
- newly_healthy = [pnn for (pnn, n) in enumerate(self.nodes)
- if len(n.current_addresses) == 0 and n.healthy]
- for n in self.nodes:
- n.set_imbalance()
-
- # We'll only retry the balancing act up to options.retries
- # times (for the basic non-deterministic algorithm). This
- # nonsense gives us a reference on the retries count in
- # Python. It will be easier in C. :-)
- # For LCP2 we reassignas many IPs from heavily "loaded" nodes
- # to nodes that are newly healthy, looping until we fail to
- # reassign an IP.
- retries_l = [0]
- should_loop = True
- while should_loop:
- should_loop = False
-
- if options.lcp2:
- self.lcp2_allocate_unassigned()
- else:
- self.basic_allocate_unassigned()
-
- if self.no_ip_failback or self.deterministic_public_ips:
- break
-
- if options.lcp2:
- if len(newly_healthy) == 0:
- break
- should_loop = self.lcp2_failback(newly_healthy)
- else:
- should_loop = self.basic_failback(retries_l)
-
- def ctdb_takeover_run_external(self):
-
- # Written while asleep...
-
- # Convert the cluster state to something that be fed to
- # ctdb_takeover_tests ipalloc ...
-
- in_lines = []
- for ip in sorted(list(self.all_public_ips)):
- allowed = []
- assigned = -1
- for (i, n) in enumerate(self.nodes):
- if n.can_node_serve_ip(ip):
- allowed.append("%s" % i)
- if ip in n.current_addresses:
- assigned = i
- line = "%s\t%d\t%s" % (ip, assigned, ",".join(allowed))
- in_lines.append(line)
-
- nodestates = ",".join(["0" if n.healthy else "1" for n in self.nodes])
-
- if options.lcp2:
- os.environ["CTDB_LCP2"] = "yes"
- if options.verbose > 1:
- os.environ["CTDB_TEST_LOGLEVEL"] = "4"
- elif options.verbose == 1:
- os.environ["CTDB_TEST_LOGLEVEL"] = "3"
- else:
- os.environ["CTDB_TEST_LOGLEVEL"] = "0"
-
- p = subprocess.Popen("../../bin/ctdb_takeover_tests ipalloc %s 2>&1" % nodestates,
- shell=True,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- p.stdin.write("\n".join(in_lines))
- p.stdin.close()
-
- # Flush all of the assigned IPs.
- for n in self.nodes:
- n.current_addresses = set()
-
- # Uses the results to populate the current_addresses for each
- # node.
- for line in p.stdout.read().split("\n"):
- # Some lines are debug, some are the final IP
- # configuration. Let's use a gross hack that assumes any
- # line with 2 words is IP configuration. That will do for
- # now.
- words = re.split("\s+", line)
- if len(words) == 2:
- # Add the IP as current for the specified node.
- self.nodes[int(words[1])].current_addresses.add(words[0])
- else:
- # First 3 words are log date/time, remove them...
- print " ".join(words[3:])
-
- # Now fake up the LCP calculations.
- for n in self.nodes:
- n.set_imbalance()
-
- def ctdb_takeover_run(self):
-
- self.events += 1
-
- if options.external:
- return self.ctdb_takeover_run_external()
- else:
- return self.ctdb_takeover_run_python()
-
- def recover(self):
- verbose_begin("TAKEOVER")
-
- self.ctdb_takeover_run()
-
- verbose_end()
-
- grat_ip_moves = 0
-
- if self.prev is not None:
- (ip_moves, grat_ip_moves, details) = self.diff()
- self.ip_moves.append(ip_moves)
- self.grat_ip_moves.append(grat_ip_moves)
-
- if options.diff:
- print_begin("DIFF")
- print "\n".join(details)
- print_end()
-
- (imbalance, imbalance_groups) = self.calculate_imbalance()
- self.imbalance.append(imbalance)
- self.imbalance_groups.append(imbalance_groups)
-
- if imbalance > options.soft_limit:
- self.imbalance_count += 1
-
- # There must be a cleaner way...
- t = []
- for (c, i) in zip(self.imbalance_groups_count, imbalance_groups):
- if i > options.soft_limit:
- t.append(c + i)
- else:
- t.append(c)
- self.imbalance_groups_count = t
-
- imbalance_metric = max([n.get_imbalance() for n in self.nodes])
- self.imbalance_metric.append(imbalance_metric)
- if options.balance:
- print_begin("IMBALANCE")
- print "ALL IPS:", imbalance
- if self.have_ip_groups():
- print "IP GROUPS:", imbalance_groups
- if options.lcp2:
- print "LCP2 IMBALANCE:", imbalance_metric
- print_end()
-
- num_unhealthy = len(self.nodes) - \
- len([n for n in self.nodes if n.healthy])
- self.num_unhealthy.append(num_unhealthy)
-
- if options.show:
- print_begin("STATE")
- print self
- print_end()
-
- self.prev = None
- self.prev = copy.deepcopy(self)
-
- # True is bad!
- return (grat_ip_moves > 0) or \
- (not self.have_ip_groups() and imbalance > options.hard_limit) or \
- (self.have_ip_groups() and (max(imbalance_groups) > options.hard_limit))
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/hey_jude.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/hey_jude.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/hey_jude.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/hey_jude.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,24 +0,0 @@
-#!/usr/bin/env python
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses10 = ['10.4.20.%d' % n for n in range(154, 168)]
-addresses172a = ['172.20.106.%d' % n for n in range(110, 124)]
-addresses172b = ['172.20.107.%d' % n for n in range(110, 117)]
-
-c = Cluster()
-
-#for i in range(7):
-# c.add_node(Node([addresses10, addresses172]))
-
-
-for i in range(4):
- c.add_node(Node([addresses172a, addresses172b]))
-for i in range(3):
- c.add_node(Node(addresses10))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups1.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# 2 IP groups, both on the same 5 nodes, with each group on different
-# interfaces/VLANs. One group has many more addresses to test how
-# well an "imbalanced" configuration will balance...
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses20 = ['192.168.20.%d' % n for n in range(1, 13)]
-addresses128 = ['192.168.128.%d' % n for n in range(1, 5)]
-
-c = Cluster()
-
-for i in range(5):
- c.add_node(Node([addresses20, addresses128]))
-
-#for i in range(3):
-# c.add_node(Node([addresses20]))
-
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups2.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-
-# 2 groups of addresses, combined into 1 pool so the checking
-# algorithm doesn't know about the groups, across 2 nodes.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses20 = ['192.168.20.%d' % n for n in range(1, 13)]
-addresses21 = ['192.168.21.%d' % n for n in range(1, 5)]
-
-c = Cluster()
-
-for i in range(2):
- c.add_node(Node(addresses20 + addresses21))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups3.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,27 +0,0 @@
-#!/usr/bin/env python
-
-# 4 IP groups, across 10 nodes, with each group on different
-# interfaces/VLANs. 80 addresses in total but not evenly balanced, to
-# help check some of the more extreme behaviour.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 41)]
-addresses2 = ['192.168.2.%d' % n for n in range(1, 21)]
-addresses3 = ['192.168.3.%d' % n for n in range(1, 11)]
-addresses4 = ['192.168.4.%d' % n for n in range(1, 11)]
-
-# Try detecting imbalance with square root of number of nodes? Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(10):
- c.add_node(Node([addresses1, addresses2, addresses3, addresses4]))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups4.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# 2 IP groups, across 2 nodes, with each group on different
-# interfaces. 4 addresses per group. A nice little canonical 2 node
-# configuration.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 5)]
-addresses2 = ['192.168.2.%d' % n for n in range(1, 5)]
-
-# Try detecting imbalance with square root of number of nodes? Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(2):
- c.add_node(Node([addresses1, addresses2]))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/ip_groups5.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-
-# 1 IP group, to test backward compatibility of LCP2 algorithm. 16
-# addresses across 4 nodes.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 17)]
-
-# Try detecting imbalance with square root of number of nodes? Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(4):
- c.add_node(Node(addresses1))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/mgmt_simple.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-
-# This is an example showing a current SONAS configuration with 3
-# interface node and a management node. When run with deterministic
-# IPs there are gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
-
-c = Cluster()
-
-for i in range(3):
- c.add_node(Node(addresses))
-
-c.add_node(Node([]))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_extra.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-
-# This example demonstrates a node group configuration. Is it meant
-# to be the same as node_group_simple.py, but with a couple of nodes
-# added later, so they are listed after the management node.
-
-# When run with deterministic IPs (use "-d" to show the problem) it
-# does many gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] + ['P', 'Q', 'R', 'S', 'T', 'U']
-addresses2 = ['I', 'J', 'K', 'L']
-
-c = Cluster()
-
-for i in range(4):
- c.add_node(Node(addresses1))
-
-for i in range(3):
- c.add_node(Node(addresses2))
-
-c.add_node(Node([]))
-c.add_node(Node(addresses1))
-c.add_node(Node(addresses2))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,46 +0,0 @@
-#!/usr/bin/env python
-
-# This demonstrates a node group configurations.
-#
-# Node groups can be defined with the syntax "-g N@IP0,IP1-IP2,IP3".
-# This says to create a group of N nodes with IPs IP0, IP1, ..., IP2,
-# IP3. Run it with deterministic IPs causes lots of gratuitous IP
-# reassignments. Running with --nd fixes this.
-
-import ctdb_takeover
-import sys
-from optparse import make_option
-import string
-
-ctdb_takeover.process_args([
- make_option("-g", "--group",
- action="append", type="string", dest="groups",
- help="define a node group using N@IPs syntax"),
- ])
-
-def expand_range(r):
- sr = r.split("-", 1)
- if len(sr) == 2:
- all = string.ascii_uppercase + string.ascii_lowercase
- sr = list(all[all.index(sr[0]):all.index(sr[1])+1])
- return sr
-
-def add_node_group(s):
- (count, ips_str) = s.split("@", 1)
- ips = [i for r in ips_str.split(",") \
- for i in expand_range(r) if r != ""]
- for i in range(int(count)):
- c.add_node(ctdb_takeover.Node(ips))
-
-c = ctdb_takeover.Cluster()
-
-if ctdb_takeover.options.groups is None:
- print "Error: no node groups defined."
- sys.exit(1)
-
-for g in ctdb_takeover.options.groups:
- add_node_group(g)
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/node_group_simple.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-
-# This example demonstrates a simple, sensible node group
-# configuration. When run with deterministic IPs (use "-d" to show
-# the problem) it does many gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
-addresses2 = ['I', 'J', 'K']
-
-c = Cluster()
-
-for i in range(4):
- c.add_node(Node(addresses1))
-
-for i in range(3):
- c.add_node(Node(addresses2))
-
-c.add_node(Node([]))
-
-c.recover()
-
-c.random_iterations()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/nondet_path_01.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# This is a contrived example that makes the balancing algorithm fail
-# for nondeterministic IPs (run with "-dv --nd" to see the failure).
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D']
-addresses2 = ['B', 'E', 'F']
-
-c = Cluster()
-
-for i in range(2):
- c.add_node(Node(addresses1))
-
-c.add_node(Node(addresses2))
-
-c.recover()
-
-c.unhealthy(1)
-c.recover()
-c.healthy(1)
-c.recover()
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/README samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/README
--- samba-4.5.8+dfsg/ctdb/tests/takeover/simulation/README 2016-08-11 07:51:04.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover/simulation/README 1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-This contains a Python simulation of CTDB's IP reallocation algorithm.
-
-It is useful for experimenting with improvements.
-
-To use this on RHEL5 you'll need python2.6 from EPEL
-.
diff -Nru samba-4.5.8+dfsg/ctdb/tests/takeover_helper/000.sh samba-4.6.5+dfsg/ctdb/tests/takeover_helper/000.sh
--- samba-4.5.8+dfsg/ctdb/tests/takeover_helper/000.sh 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/takeover_helper/000.sh 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+define_test "3 nodes, all ok, no IPs"
+
+setup_ctdbd </dev/null || echo)
+ if [ -n "$pid" ] ; then
+ kill $pid || true
+ rm -f "$ctdbd_pidfile"
+ fi
+ rm -f "$ctdbd_socket"
+}
+
+setup_ctdbd ()
+{
+ debug "Setting up fake ctdbd"
+
+ $VALGRIND fake_ctdbd -d "$FAKE_CTDBD_DEBUGLEVEL" \
+ -s "$ctdbd_socket" -p "$ctdbd_pidfile"
+ # This current translates to a 6 second timeout for the
+ # important controls
+ ctdb --socket $ctdbd_socket setvar TakeoverTimeout 2
+ test_cleanup cleanup_ctdbd
+}
+
+ctdbd_getpid ()
+{
+ cat "$ctdbd_pidfile"
+}
+
+# Render non-printable characters. The helper prints the status as
+# binary, so render it for easy comparison.
+result_filter ()
+{
+ sed -e 's|ctdb-takeover\[[0-9]*\]: ||'
+}
+
+ctdb_cmd ()
+{
+ echo Running: ctdb -d "$CTDB_DEBUGLEVEL" --socket $ctdbd_socket "$@"
+ ctdb -d "$CTDB_DEBUGLEVEL" --socket $ctdbd_socket "$@"
+}
+
+test_ctdb_ip_all ()
+{
+ unit_test ctdb -d "$CTDB_DEBUGLEVEL" \
+ --socket $ctdbd_socket ip all || exit $?
+}
+
+takeover_helper_out="${TEST_VAR_DIR}/takover_helper.out"
+
+takeover_helper_format_outfd ()
+{
+ od -A n -t d4 "$takeover_helper_out" | sed -e 's|^[[:space:]]*||'
+}
+
+test_takeover_helper ()
+{
+ (
+ export CTDB_DEBUGLEVEL="$HELPER_DEBUGLEVEL"
+ export CTDB_LOGGING="file:"
+ unit_test ctdb_takeover_helper 3 "$ctdbd_socket" "$@" \
+ 3>"$takeover_helper_out"
+ ) || exit $?
+
+ case "$required_rc" in
+ 255) _t="-1" ;;
+ *) _t="$required_rc" ;;
+ esac
+ ok "$_t"
+
+ unit_test_notrace takeover_helper_format_outfd
+ _ret=$?
+ rm "$takeover_helper_out"
+ [ $? -eq 0 ] || exit $?
+}
diff -Nru samba-4.5.8+dfsg/ctdb/tests/tool/ctdb.ip.001.sh samba-4.6.5+dfsg/ctdb/tests/tool/ctdb.ip.001.sh
--- samba-4.5.8+dfsg/ctdb/tests/tool/ctdb.ip.001.sh 1970-01-01 00:00:00.000000000 +0000
+++ samba-4.6.5+dfsg/ctdb/tests/tool/ctdb.ip.001.sh 2017-01-11 07:55:14.000000000 +0000
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+define_test "3 nodes, all ok, no ips"
+
+setup_ctdbd <
#include
@@ -32,6 +33,7 @@
#include "ctdb_version.h"
#include "lib/util/debug.h"
#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
#include "common/db_hash.h"
#include "common/logging.h"
@@ -644,28 +646,45 @@
return ret;
}
-static int run_helper(const char *command, const char *path, const char *arg1)
+static int run_helper(TALLOC_CTX *mem_ctx, const char *command,
+ const char *path, int argc, const char **argv)
{
pid_t pid;
int save_errno, status, ret;
+ const char **new_argv;
+ int i;
+
+ new_argv = talloc_array(mem_ctx, const char *, argc + 2);
+ if (new_argv == NULL) {
+ return ENOMEM;
+ }
+
+ new_argv[0] = path;
+ for (i=0; i= 64 && pstatus < 255) {
+ fprintf(stderr, "%s failed with error %d\n",
+ command, pstatus-64);
+ ret = pstatus - 64;
+ } else {
+ ret = pstatus;
+ }
return ret;
- }
-
- if (WIFSIGNALED(status)) {
+ } else if (WIFSIGNALED(status)) {
fprintf(stderr, "%s terminated with signal %d\n",
command, WTERMSIG(status));
return EINTR;
@@ -774,7 +802,8 @@
}
static void print_nodemap(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
- struct ctdb_node_map *nodemap, uint32_t mypnn)
+ struct ctdb_node_map *nodemap, uint32_t mypnn,
+ bool print_header)
{
struct ctdb_node_and_flags *node;
int num_deleted_nodes = 0;
@@ -786,11 +815,14 @@
}
}
- if (num_deleted_nodes == 0) {
- printf("Number of nodes:%d\n", nodemap->num);
- } else {
- printf("Number of nodes:%d (including %d deleted nodes)\n",
- nodemap->num, num_deleted_nodes);
+ if (print_header) {
+ if (num_deleted_nodes == 0) {
+ printf("Number of nodes:%d\n", nodemap->num);
+ } else {
+ printf("Number of nodes:%d "
+ "(including %d deleted nodes)\n",
+ nodemap->num, num_deleted_nodes);
+ }
}
for (i=0; inum; i++) {
@@ -816,7 +848,7 @@
{
int i;
- print_nodemap(mem_ctx, ctdb, nodemap, mypnn);
+ print_nodemap(mem_ctx, ctdb, nodemap, mypnn, true);
if (vnnmap->generation == INVALID_GENERATION) {
printf("Generation:INVALID\n");
@@ -1524,7 +1556,8 @@
for (i=0; iev, ctdb->client,
- pnn_list[i], TIMEOUT(), &ips);
+ pnn_list[i], TIMEOUT(),
+ false, &ips);
if (ret != 0) {
goto failed;
}
@@ -1629,7 +1662,8 @@
ret = get_all_public_ips(ctdb, mem_ctx, &ips);
} else {
ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), &ips);
+ ctdb->cmd_pnn, TIMEOUT(),
+ false, &ips);
}
if (ret != 0) {
return ret;
@@ -2277,7 +2311,7 @@
return 1;
}
- return run_helper("LVS helper", lvs_helper, argv[0]);
+ return run_helper(mem_ctx, "LVS helper", lvs_helper, argc, argv);
}
static int control_disable_monitor(TALLOC_CTX *mem_ctx,
@@ -2321,7 +2355,7 @@
static int control_setdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
int argc, const char **argv)
{
- enum debug_level log_level;
+ int log_level;
int ret;
bool found;
@@ -2350,7 +2384,7 @@
static int control_getdebug(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
int argc, const char **argv)
{
- enum debug_level loglevel;
+ int loglevel;
const char *log_str;
int ret;
@@ -2920,7 +2954,7 @@
return ret;
}
- if (vnnmap->generation == 1) {
+ if (vnnmap->generation == INVALID_GENERATION) {
talloc_free(vnnmap);
sleep(1);
goto again;
@@ -3781,7 +3815,7 @@
}
ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
- pnn, TIMEOUT(), &pubip_list);
+ pnn, TIMEOUT(), false, &pubip_list);
if (ret != 0) {
fprintf(stderr, "Failed to get Public IPs from node %u\n",
pnn);
@@ -3909,7 +3943,8 @@
}
ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), &pubip_list);
+ ctdb->cmd_pnn, TIMEOUT(),
+ false, &pubip_list);
if (ret != 0) {
fprintf(stderr, "Failed to get Public IPs from node %u\n",
ctdb->cmd_pnn);
@@ -3972,7 +4007,8 @@
}
ret = ctdb_ctrl_get_public_ips(mem_ctx, ctdb->ev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), &pubip_list);
+ ctdb->cmd_pnn, TIMEOUT(),
+ false, &pubip_list);
if (ret != 0) {
fprintf(stderr, "Failed to get Public IPs from node %u\n",
ctdb->cmd_pnn);
@@ -4006,31 +4042,6 @@
return 0;
}
-static int control_eventscript(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
- int argc, const char **argv)
-{
- int ret;
-
- if (argc != 1) {
- usage("eventscript");
- }
-
- if (strcmp(argv[0], "monitor") != 0) {
- fprintf(stderr, "Only monitor event can be run\n");
- return 1;
- }
-
- ret = ctdb_ctrl_run_eventscripts(mem_ctx, ctdb->ev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), argv[0]);
- if (ret != 0) {
- fprintf(stderr, "Failed to run monitor event on node %u\n",
- ctdb->cmd_pnn);
- return ret;
- }
-
- return 0;
-}
-
#define DB_VERSION 3
#define MAX_DB_NAME 64
#define MAX_REC_BUFFER_SIZE (100*1000)
@@ -4623,239 +4634,70 @@
return 0;
}
-static void print_scriptstatus_one(struct ctdb_script_list *slist,
- const char *event_str)
-{
- int i;
- int num_run = 0;
-
- if (slist == NULL) {
- if (! options.machinereadable) {
- printf("%s cycle never run\n", event_str);
- }
- return;
- }
-
- for (i=0; inum_scripts; i++) {
- if (slist->script[i].status != -ENOEXEC) {
- num_run++;
- }
- }
-
- if (! options.machinereadable) {
- printf("%d scripts were executed last %s cycle\n",
- num_run, event_str);
- }
-
- for (i=0; inum_scripts; i++) {
- const char *status = NULL;
-
- switch (slist->script[i].status) {
- case -ETIME:
- status = "TIMEDOUT";
- break;
- case -ENOEXEC:
- status = "DISABLED";
- break;
- case 0:
- status = "OK";
- break;
- default:
- if (slist->script[i].status > 0) {
- status = "ERROR";
- }
- break;
- }
-
- if (options.machinereadable) {
- printf("%s%s%s%s%s%i%s%s%s%lu.%06lu%s%lu.%06lu%s%s%s\n",
- options.sep,
- event_str, options.sep,
- slist->script[i].name, options.sep,
- slist->script[i].status, options.sep,
- status, options.sep,
- slist->script[i].start.tv_sec,
- slist->script[i].start.tv_usec, options.sep,
- slist->script[i].finished.tv_sec,
- slist->script[i].finished.tv_usec, options.sep,
- slist->script[i].output, options.sep);
- continue;
- }
-
- if (status) {
- printf("%-20s Status:%s ",
- slist->script[i].name, status);
- } else {
- /* Some other error, eg from stat. */
- printf("%-20s Status:CANNOT RUN (%s)",
- slist->script[i].name,
- strerror(-slist->script[i].status));
- }
-
- if (slist->script[i].status >= 0) {
- printf("Duration:%.3lf ",
- timeval_delta(&slist->script[i].finished,
- &slist->script[i].start));
- }
- if (slist->script[i].status != -ENOEXEC) {
- printf("%s", ctime(&slist->script[i].start.tv_sec));
- if (slist->script[i].status != 0) {
- printf(" OUTPUT:%s\n",
- slist->script[i].output);
- }
- } else {
- printf("\n");
- }
- }
-}
-
-static void print_scriptstatus(struct ctdb_script_list **slist,
- int count, const char **event_str)
+static int control_event(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
+ int argc, const char **argv)
{
+ char *t, *event_helper = NULL;
+ char *eventd_socket = NULL;
+ const char **new_argv;
int i;
- if (options.machinereadable) {
- printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
- options.sep,
- "Type", options.sep,
- "Name", options.sep,
- "Code", options.sep,
- "Status", options.sep,
- "Start", options.sep,
- "End", options.sep,
- "Error Output", options.sep);
- }
-
- for (i=0; i 1) {
- usage("scriptstatus");
- }
-
- if (argc == 0) {
- event_str = "monitor";
+ t = getenv("CTDB_EVENT_HELPER");
+ if (t != NULL) {
+ event_helper = talloc_strdup(mem_ctx, t);
} else {
- event_str = argv[0];
+ event_helper = talloc_asprintf(mem_ctx, "%s/ctdb_event",
+ CTDB_HELPER_BINDIR);
}
- valid = false;
- for (i=0; iev,
- ctdb->client,
- ctdb->cmd_pnn,
- TIMEOUT(), event,
- &slist[num]);
- if (ret != 0) {
- fprintf(stderr,
- "failed to get script status for %s event\n",
- all_events[i]);
- return 1;
- }
-
- if (slist[num] == NULL) {
- num++;
- continue;
- }
-
- /* The ETIME status is ignored for certain events.
- * In that case the status is 0, but endtime is not set.
- */
- for (j=0; jnum_scripts; j++) {
- if (slist[num]->script[j].status == 0 &&
- timeval_is_zero(&slist[num]->script[j].finished)) {
- slist[num]->script[j].status = -ETIME;
- }
- }
-
- num++;
+ new_argv[0] = eventd_socket;
+ for (i=0; iev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), argv[0]);
- if (ret != 0) {
- fprintf(stderr, "Failed to enable script %s\n", argv[0]);
- return ret;
- }
-
- return 0;
-}
-
-static int control_disablescript(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
- int argc, const char **argv)
-{
- int ret;
+ const char *new_argv[3];
- if (argc != 1) {
- usage("disablescript");
+ if (argc > 1) {
+ usage("scriptstatus");
}
- ret = ctdb_ctrl_disable_script(mem_ctx, ctdb->ev, ctdb->client,
- ctdb->cmd_pnn, TIMEOUT(), argv[0]);
- if (ret != 0) {
- fprintf(stderr, "Failed to disable script %s\n", argv[0]);
- return ret;
- }
+ new_argv[0] = "status";
+ new_argv[1] = (argc == 0) ? "monitor" : argv[0];
+ new_argv[2] = NULL;
+ (void) control_event(mem_ctx, ctdb, 2, new_argv);
return 0;
}
@@ -4881,13 +4723,15 @@
return 1;
}
- return run_helper("NAT gateway helper", natgw_helper, argv[0]);
+ return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
+ argc, argv);
}
static int control_natgwlist(TALLOC_CTX *mem_ctx, struct ctdb_context *ctdb,
int argc, const char **argv)
{
char *t, *natgw_helper = NULL;
+ const char *cmd_argv[] = { "natgwlist", NULL };
if (argc != 0) {
usage("natgwlist");
@@ -4906,7 +4750,8 @@
return 1;
}
- return run_helper("NAT gateway helper", natgw_helper, "natgwlist");
+ return run_helper(mem_ctx, "NAT gateway helper", natgw_helper,
+ 1, cmd_argv);
}
/*
@@ -4969,7 +4814,7 @@
struct ctdb_context *ctdb,
int argc, const char **argv)
{
- uint32_t lmasterrole;
+ uint32_t lmasterrole = 0;
int ret;
if (argc != 1) {
@@ -4997,7 +4842,7 @@
struct ctdb_context *ctdb,
int argc, const char **argv)
{
- uint32_t recmasterrole;
+ uint32_t recmasterrole = 0;
int ret;
if (argc != 1) {
@@ -5499,9 +5344,9 @@
int argc, const char **argv)
{
struct tdb_context *tdb;
- TDB_DATA key, data, value;
+ TDB_DATA key, data[2], value;
struct ctdb_ltdb_header header;
- size_t offset;
+ uint8_t header_buf[sizeof(struct ctdb_ltdb_header)];
int ret;
if (argc < 3 || argc > 5) {
@@ -5540,19 +5385,15 @@
header.flags = (uint32_t)atol(argv[5]);
}
- offset = ctdb_ltdb_header_len(&header);
- data.dsize = offset + value.dsize;
- data.dptr = talloc_size(mem_ctx, data.dsize);
- if (data.dptr == NULL) {
- fprintf(stderr, "Memory allocation error\n");
- tdb_close(tdb);
- return 1;
- }
+ ctdb_ltdb_header_push(&header, header_buf);
+
+ data[0].dsize = ctdb_ltdb_header_len(&header);
+ data[0].dptr = header_buf;
- ctdb_ltdb_header_push(&header, data.dptr);
- memcpy(data.dptr + offset, value.dptr, value.dsize);
+ data[1].dsize = value.dsize;
+ data[1].dptr = value.dptr;
- ret = tdb_store(tdb, key, data, TDB_REPLACE);
+ ret = tdb_storev(tdb, key, data, 2, TDB_REPLACE);
if (ret != 0) {
fprintf(stderr, "Failed to write record %s to file %s\n",
argv[1], argv[0]);
@@ -5813,6 +5654,7 @@
const char *nodestring = NULL;
struct ctdb_node_map *nodemap;
int ret, i;
+ bool print_hdr = false;
if (argc > 1) {
usage("nodestatus");
@@ -5820,21 +5662,19 @@
if (argc == 1) {
nodestring = argv[0];
+ if (strcmp(nodestring, "all") == 0) {
+ print_hdr = true;
+ }
}
if (! parse_nodestring(mem_ctx, ctdb, nodestring, &nodemap)) {
return 1;
}
- nodemap = get_nodemap(ctdb, false);
- if (nodemap == NULL) {
- return 1;
- }
-
if (options.machinereadable) {
print_nodemap_machine(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
} else {
- print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn);
+ print_nodemap(mem_ctx, ctdb, nodemap, ctdb->cmd_pnn, print_hdr);
}
ret = 0;
@@ -6267,8 +6107,6 @@
"add an ip address to a node", " " },
{ "delip", control_delip, false, true,
"delete an ip address from a node", "" },
- { "eventscript", control_eventscript, false, true,
- "run an event", "monitor" },
{ "backupdb", control_backupdb, false, false,
"backup a database into a file", " " },
{ "restoredb", control_restoredb, false, false,
@@ -6279,13 +6117,11 @@
"wipe the contents of a database.", ""},
{ "recmaster", control_recmaster, false, true,
"show the pnn for the recovery master", NULL },
- { "scriptstatus", control_scriptstatus, false, true,
+ { "event", control_event, true, false,
+ "event and event script commands", NULL },
+ { "scriptstatus", control_scriptstatus, true, false,
"show event script status",
"[init|setup|startup|monitor|takeip|releaseip|ipreallocated]" },
- { "enablescript", control_enablescript, false, true,
- "enable an eventscript", "