1 /*
2  * Copyright (c) 2015-2016 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Author: Jan Friesse (jfriesse@redhat.com)
7  *
8  * This software licensed under BSD license, the text of which follows:
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  * - Neither the name of the Red Hat, Inc. nor the names of its
19  *   contributors may be used to endorse or promote products derived from this
20  *   software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "qdevice-net-ipc-cmd.h"
36 #include "qdevice-log.h"
37 #include "dynar-str.h"
38 #include "qdevice-net-algorithm.h"
39 #include "utils.h"
40 
41 static int
qdevice_net_ipc_cmd_status_add_header(struct dynar * outbuf,int verbose)42 qdevice_net_ipc_cmd_status_add_header(struct dynar *outbuf, int verbose)
43 {
44 
45 	return ((dynar_str_catf(outbuf, "Qdevice-net information\n") != -1) &&
46 	    (dynar_str_catf(outbuf, "----------------------\n") != -1));
47 }
48 
49 static int
qdevice_net_ipc_cmd_status_add_tie_breaker(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)50 qdevice_net_ipc_cmd_status_add_tie_breaker(struct qdevice_net_instance *instance,
51     struct dynar *outbuf, int verbose)
52 {
53 
54 	if (dynar_str_catf(outbuf, "Tie-breaker:\t\t") == -1) {
55 		return (0);
56 	}
57 
58 	switch (instance->tie_breaker.mode) {
59 	case TLV_TIE_BREAKER_MODE_LOWEST:
60 		if (dynar_str_catf(outbuf, "Node with lowest node ID") == -1) {
61 			return (0);
62 		}
63 		break;
64 	case TLV_TIE_BREAKER_MODE_HIGHEST:
65 		if (dynar_str_catf(outbuf, "Node with highest node ID") == -1) {
66 			return (0);
67 		}
68 		break;
69 	case TLV_TIE_BREAKER_MODE_NODE_ID:
70 		if (dynar_str_catf(outbuf, "Node with node ID "UTILS_PRI_NODE_ID,
71 		    instance->tie_breaker.node_id) == -1) {
72 			return (0);
73 		}
74 		break;
75 	}
76 
77 	return (dynar_str_catf(outbuf, "\n") != -1);
78 }
79 
80 static int
qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)81 qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance *instance,
82     struct dynar *outbuf, int verbose)
83 {
84 
85 	if (dynar_str_catf(outbuf, "Cluster name:\t\t%s\n", instance->cluster_name) == -1) {
86 		return (0);
87 	}
88 
89 	if (dynar_str_catf(outbuf, "QNetd host:\t\t%s:%"PRIu16"\n",
90 	    instance->host_addr, instance->host_port) == -1) {
91 		return (0);
92 	}
93 
94 	if (verbose && instance->force_ip_version != 0) {
95 		if (dynar_str_catf(outbuf, "Force IP version:\t%u\n",
96 		    instance->force_ip_version) == -1) {
97 			return (0);
98 		}
99 	}
100 
101 	if (verbose) {
102 		if ((dynar_str_catf(outbuf, "Connect timeout:\t%"PRIu32"ms\n",
103 		    instance->connect_timeout) == -1) ||
104 		    (dynar_str_catf(outbuf, "HB interval:\t\t%"PRIu32"ms\n",
105 		    instance->heartbeat_interval) == -1) ||
106 		    (dynar_str_catf(outbuf, "VQ vote timer interval:\t%"PRIu32"ms\n",
107 		    instance->cast_vote_timer_interval) == -1)) {
108 			return (0);
109 		}
110 
111 		if (dynar_str_catf(outbuf, "TLS:\t\t\t%s\n",
112 		    tlv_tls_supported_to_str(instance->tls_supported)) == -1) {
113 			return (0);
114 		}
115 	}
116 
117 	if (dynar_str_catf(outbuf, "Algorithm:\t\t%s\n",
118 	    tlv_decision_algorithm_type_to_str(instance->decision_algorithm)) == -1) {
119 		return (0);
120 	}
121 
122 	return (1);
123 }
124 
125 static int
qdevice_net_ipc_cmd_status_add_poll_timer_status(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)126 qdevice_net_ipc_cmd_status_add_poll_timer_status(struct qdevice_net_instance *instance,
127     struct dynar *outbuf, int verbose)
128 {
129 
130 	if (!verbose) {
131 		return (1);
132 	}
133 
134 	if (dynar_str_catf(outbuf, "Poll timer running:\t%s",
135 	    (instance->cast_vote_timer != NULL ? "Yes" : "No")) == -1) {
136 		return (0);
137 	}
138 
139 	if (instance->cast_vote_timer != NULL && instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
140 		if (dynar_str_catf(outbuf, " (cast vote)") == -1) {
141 			return (0);
142 		}
143 	}
144 
145 	return (dynar_str_catf(outbuf, "\n") != -1);
146 }
147 
148 static int
qdevice_net_ipc_cmd_status_add_state(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)149 qdevice_net_ipc_cmd_status_add_state(struct qdevice_net_instance *instance,
150     struct dynar *outbuf, int verbose)
151 {
152 	const char *state;
153 
154 	if (instance->schedule_disconnect) {
155 		state = "Disconnected";
156 	} else {
157 		if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
158 			state = "Connected";
159 		} else {
160 			if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT ||
161 			    !instance->non_blocking_client.destroyed) {
162 				state = "Connecting";
163 			} else {
164 				state = "Connect failed";
165 			}
166 		}
167 	}
168 
169 	return (dynar_str_catf(outbuf, "State:\t\t\t%s\n", state) != -1);
170 }
171 
172 static int
qdevice_net_ipc_cmd_status_add_heuristics(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)173 qdevice_net_ipc_cmd_status_add_heuristics(struct qdevice_net_instance *instance,
174     struct dynar *outbuf, int verbose)
175 {
176 	enum qdevice_heuristics_mode active_heuristics_mode;
177 	int heuristics_enabled;
178 
179 	active_heuristics_mode = instance->qdevice_instance_ptr->heuristics_instance.mode;
180 	heuristics_enabled = (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
181 	    active_heuristics_mode == QDEVICE_HEURISTICS_MODE_SYNC);
182 
183 	if (!heuristics_enabled) {
184 		return (1);
185 	}
186 
187 	if (dynar_str_catf(outbuf, "Heuristics result:\t%s",
188 	    tlv_heuristics_to_str(instance->latest_heuristics_result)) == -1) {
189 		return (0);
190 	}
191 
192 	if (verbose) {
193 		if (dynar_str_catf(outbuf, " (regular: %s, membership: %s, connect: %s)",
194 		    tlv_heuristics_to_str(instance->latest_regular_heuristics_result),
195 		    tlv_heuristics_to_str(instance->latest_vq_heuristics_result),
196 		    tlv_heuristics_to_str(instance->latest_connect_heuristics_result)) == -1) {
197 			return (0);
198 		}
199 	}
200 
201 	return (dynar_str_catf(outbuf, "\n") != -1);
202 }
203 
204 static int
qdevice_net_ipc_cmd_status_add_tls_state(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)205 qdevice_net_ipc_cmd_status_add_tls_state(struct qdevice_net_instance *instance,
206     struct dynar *outbuf, int verbose)
207 {
208 
209 	if (!verbose || instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
210 		return (1);
211 	}
212 
213 	if (dynar_str_catf(outbuf, "TLS active:\t\t%s", (instance->using_tls ? "Yes" : "No")) == -1) {
214 		return (0);
215 	}
216 
217 	if (instance->using_tls && instance->tls_client_cert_sent) {
218 		if (dynar_str_catf(outbuf, " (client certificate sent)") == -1) {
219 			return (0);
220 		}
221 	}
222 
223 	return (dynar_str_catf(outbuf, "\n") != -1);
224 }
225 
226 static int
qdevice_net_ipc_cmd_status_add_times(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)227 qdevice_net_ipc_cmd_status_add_times(struct qdevice_net_instance *instance,
228     struct dynar *outbuf, int verbose)
229 {
230 	struct tm tm_res;
231 
232 	if (!verbose || instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
233 		return (1);
234 	}
235 
236 	if (instance->connected_since_time != ((time_t) -1)) {
237 		localtime_r(&instance->connected_since_time, &tm_res);
238 		if (dynar_str_catf(outbuf, "Connected since:\t%04d-%02d-%02dT%02d:%02d:%02d\n",
239 		    tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday,
240 		    tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec) == -1) {
241 			return (0);
242 		}
243 	}
244 
245 	if (instance->last_echo_reply_received_time != ((time_t) -1)) {
246 		localtime_r(&instance->last_echo_reply_received_time, &tm_res);
247 		if (dynar_str_catf(outbuf, "Echo reply received:\t%04d-%02d-%02dT%02d:%02d:%02d\n",
248 		    tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday,
249 		    tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec) == -1) {
250 			return (0);
251 		}
252 	}
253 
254 	return (1);
255 }
256 
257 int
qdevice_net_ipc_cmd_status(struct qdevice_net_instance * instance,struct dynar * outbuf,int verbose)258 qdevice_net_ipc_cmd_status(struct qdevice_net_instance *instance, struct dynar *outbuf, int verbose)
259 {
260 
261 	if (qdevice_net_ipc_cmd_status_add_header(outbuf, verbose) &&
262 	    qdevice_net_ipc_cmd_status_add_basic_info(instance, outbuf, verbose) &&
263 	    qdevice_net_ipc_cmd_status_add_tie_breaker(instance, outbuf, verbose) &&
264 	    qdevice_net_ipc_cmd_status_add_poll_timer_status(instance, outbuf, verbose) &&
265 	    qdevice_net_ipc_cmd_status_add_state(instance, outbuf, verbose) &&
266 	    qdevice_net_ipc_cmd_status_add_heuristics(instance, outbuf, verbose) &&
267 	    qdevice_net_ipc_cmd_status_add_tls_state(instance, outbuf, verbose) &&
268 	    qdevice_net_ipc_cmd_status_add_times(instance, outbuf, verbose)) {
269 		return (1);
270 	}
271 
272 	return (0);
273 }
274