1 /*-
2  * Copyright (c) 2005, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 
13 #ifdef HAVE_STATISTICS
14 static int __repmgr_print_all __P((ENV *, u_int32_t));
15 static int __repmgr_print_sites __P((ENV *));
16 static int __repmgr_print_stats __P((ENV *, u_int32_t));
17 static int __repmgr_stat __P((ENV *, DB_REPMGR_STAT **, u_int32_t));
18 
19 /*
20  * __repmgr_stat_pp --
21  *	DB_ENV->repmgr_stat pre/post processing.
22  *
23  * PUBLIC: int __repmgr_stat_pp __P((DB_ENV *, DB_REPMGR_STAT **, u_int32_t));
24  */
25 int
__repmgr_stat_pp(dbenv,statp,flags)26 __repmgr_stat_pp(dbenv, statp, flags)
27 	DB_ENV *dbenv;
28 	DB_REPMGR_STAT **statp;
29 	u_int32_t flags;
30 {
31 	ENV *env;
32 	int ret;
33 
34 	env = dbenv->env;
35 
36 	ENV_REQUIRES_CONFIG_XX(
37 	    env, rep_handle, "DB_ENV->repmgr_stat", DB_INIT_REP);
38 
39 	if ((ret = __db_fchk(env,
40 	    "DB_ENV->repmgr_stat", flags, DB_STAT_CLEAR)) != 0)
41 		return (ret);
42 
43 	return (__repmgr_stat(env, statp, flags));
44 }
45 
46 /*
47  * __repmgr_stat --
48  *	ENV->repmgr_stat.
49  */
50 static int
__repmgr_stat(env,statp,flags)51 __repmgr_stat(env, statp, flags)
52 	ENV *env;
53 	DB_REPMGR_STAT **statp;
54 	u_int32_t flags;
55 {
56 	DB_REP *db_rep;
57 	DB_REPMGR_STAT *copy, *stats;
58 	REPMGR_SITE *site;
59 	u_int32_t tmp;
60 	u_int i;
61 	int ret;
62 
63 	db_rep = env->rep_handle;
64 	stats = &db_rep->region->mstat;
65 
66 	*statp = NULL;
67 
68 	/* Allocate a stat struct to return to the user. */
69 	if ((ret = __os_umalloc(env, sizeof(DB_REPMGR_STAT), &copy)) != 0)
70 		return (ret);
71 
72 	memcpy(copy, stats, sizeof(*stats));
73 	if (LF_ISSET(DB_STAT_CLEAR)) {
74 		tmp = stats->st_max_elect_threads;
75 		memset(stats, 0, sizeof(DB_REPMGR_STAT));
76 		stats->st_max_elect_threads = tmp;
77 	}
78 	stats->st_incoming_queue_gbytes = db_rep->input_queue.gbytes;
79 	stats->st_incoming_queue_bytes = db_rep->input_queue.bytes;
80 	LOCK_MUTEX(db_rep->mutex);
81 	for (i = 0; i < db_rep->site_cnt; i++) {
82 		site = SITE_FROM_EID(i);
83 		if (site->membership != 0) {
84 			copy->st_site_total++;
85 			if (FLD_ISSET(site->gmdb_flags, SITE_VIEW))
86 				copy->st_site_views++;
87 			else
88 				copy->st_site_participants++;
89 		}
90 	}
91 	UNLOCK_MUTEX(db_rep->mutex);
92 
93 	*statp = copy;
94 	return (0);
95 }
96 
97 /*
98  * __repmgr_stat_print_pp --
99  *	DB_ENV->repmgr_stat_print pre/post processing.
100  *
101  * PUBLIC: int __repmgr_stat_print_pp __P((DB_ENV *, u_int32_t));
102  */
103 int
__repmgr_stat_print_pp(dbenv,flags)104 __repmgr_stat_print_pp(dbenv, flags)
105 	DB_ENV *dbenv;
106 	u_int32_t flags;
107 {
108 	ENV *env;
109 	int ret;
110 
111 	env = dbenv->env;
112 
113 	ENV_REQUIRES_CONFIG_XX(
114 	    env, rep_handle, "DB_ENV->repmgr_stat_print", DB_INIT_REP);
115 
116 	if ((ret = __db_fchk(env, "DB_ENV->repmgr_stat_print",
117 	    flags, DB_STAT_ALL | DB_STAT_CLEAR)) != 0)
118 		return (ret);
119 
120 	return (__repmgr_stat_print(env, flags));
121 }
122 
123 /*
124  * PUBLIC: int __repmgr_stat_print __P((ENV *, u_int32_t));
125  */
126 int
__repmgr_stat_print(env,flags)127 __repmgr_stat_print(env, flags)
128 	ENV *env;
129 	u_int32_t flags;
130 {
131 	u_int32_t orig_flags;
132 	int ret;
133 
134 	orig_flags = flags;
135 	LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM);
136 	if (flags == 0 || LF_ISSET(DB_STAT_ALL)) {
137 		if ((ret = __repmgr_print_stats(env, orig_flags)) == 0)
138 			ret = __repmgr_print_sites(env);
139 		if (flags == 0 || ret != 0)
140 			return (ret);
141 	}
142 
143 	if (LF_ISSET(DB_STAT_ALL) &&
144 	    (ret = __repmgr_print_all(env, orig_flags)) != 0)
145 		return (ret);
146 
147 	return (0);
148 }
149 
150 static int
__repmgr_print_stats(env,flags)151 __repmgr_print_stats(env, flags)
152 	ENV *env;
153 	u_int32_t flags;
154 {
155 	DB_REPMGR_STAT *sp;
156 	int ret;
157 	char* poll_method;
158 
159 	if ((ret = __repmgr_stat(env, &sp, flags)) != 0)
160 		return (ret);
161 
162 	__db_dl(env, "Number of PERM messages not acknowledged",
163 	    (u_long)sp->st_perm_failed);
164 	__db_dl(env, "Number of messages queued due to network delay",
165 	    (u_long)sp->st_msgs_queued);
166 	__db_dl(env, "Number of messages discarded due to queue length",
167 	    (u_long)sp->st_msgs_dropped);
168 	__db_dlbytes(env, "Incoming message size in queue",
169 	    (u_long)sp->st_incoming_queue_gbytes, (u_long)0,
170 	    (u_long)sp->st_incoming_queue_bytes);
171 	__db_dl(env, "Number of messages discarded due to incoming queue full",
172 	    (u_long)sp->st_incoming_msgs_dropped);
173 	__db_dl(env, "Number of existing connections dropped",
174 	    (u_long)sp->st_connection_drop);
175 	__db_dl(env, "Number of failed new connection attempts",
176 	    (u_long)sp->st_connect_fail);
177 	__db_dl(env, "Number of currently active election threads",
178 	    (u_long)sp->st_elect_threads);
179 	__db_dl(env, "Earliest log file still needed by replication group",
180 	    (u_long)sp->st_group_stable_log_file);
181 	__db_dl(env, "Election threads for which space is reserved",
182 	    (u_long)sp->st_max_elect_threads);
183 	__db_dl(env, "Number of participant sites in replication group",
184 	    (u_long)sp->st_site_participants);
185 	__db_dl(env, "Total number of sites in replication group",
186 	    (u_long)sp->st_site_total);
187 	__db_dl(env, "Number of view sites in replication group",
188 	    (u_long)sp->st_site_views);
189 	__db_dl(env, "Number of automatic replication process takeovers",
190 	    (u_long)sp->st_takeovers);
191 	__db_dl(env, "Number of write operations forwarded by this client",
192 	    (u_long)sp->st_write_ops_forwarded);
193 	__db_dl(env, "Number of write operations received by this master",
194 	    (u_long)sp->st_write_ops_received);
195 
196 	switch (sp->st_polling_method) {
197 	case SELECT:
198 		poll_method = "SELECT";
199 		break;
200 	case POLL:
201 		poll_method = "POLL";
202 		break;
203 	case EPOLL:
204 		poll_method = "EPOLL";
205 		break;
206 	default:
207 		poll_method = "Not yet specified";
208 		break;
209 	}
210 	__db_msg(env, "%lu(%s) \tReplication Manager Polling method",
211 	    (u_long)sp->st_polling_method, poll_method);
212 
213 	__os_ufree(env, sp);
214 
215 	return (0);
216 }
217 
218 static int
__repmgr_print_sites(env)219 __repmgr_print_sites(env)
220 	ENV *env;
221 {
222 	DB_REPMGR_SITE *list;
223 	DB_MSGBUF mb;
224 	u_int count, i;
225 	int ret;
226 
227 	if ((ret = __repmgr_site_list_int(env, &count, &list)) != 0)
228 		return (ret);
229 
230 	if (count == 0)
231 		return (0);
232 
233 	__db_msg(env, "%s", DB_GLOBAL(db_line));
234 	__db_msg(env, "DB_REPMGR site information:");
235 
236 	DB_MSGBUF_INIT(&mb);
237 	for (i = 0; i < count; ++i) {
238 		__db_msgadd(env, &mb, "%s (eid: %d, port: %u",
239 		    list[i].host, list[i].eid, list[i].port);
240 		if (list[i].status != 0)
241 			__db_msgadd(env, &mb, ", %sconnected",
242 			    list[i].status == DB_REPMGR_CONNECTED ? "" : "dis");
243 		if (IS_REP_MASTER(env))
244 			__db_msgadd(env, &mb, ", max_ack_lsn: %lu/%lu",
245 			    (u_long)list[i].max_ack_lsn.file,
246 			    (u_long)list[i].max_ack_lsn.offset);
247 		__db_msgadd(env, &mb, ", %selectable",
248 		    F_ISSET(&list[i], DB_REPMGR_ISELECTABLE) ? "" : "non-");
249 		__db_msgadd(env, &mb, ", %speer",
250 		    F_ISSET(&list[i], DB_REPMGR_ISPEER) ? "" : "non-");
251 		__db_msgadd(env, &mb, ", %s",
252 		    F_ISSET(&list[i], DB_REPMGR_ISVIEW) ?
253 		    "view" : "participant");
254 		__db_msgadd(env, &mb, ")");
255 		DB_MSGBUF_FLUSH(env, &mb);
256 	}
257 
258 	__os_ufree(env, list);
259 
260 	return (0);
261 }
262 
263 /*
264  * __repmgr_print_all --
265  *	Display debugging replication manager statistics.
266  */
267 static int
__repmgr_print_all(env,flags)268 __repmgr_print_all(env, flags)
269 	ENV *env;
270 	u_int32_t flags;
271 {
272 	COMPQUIET(env, NULL);
273 	COMPQUIET(flags, 0);
274 	return (0);
275 }
276 
277 #else /* !HAVE_STATISTICS */
278 
279 int
__repmgr_stat_pp(dbenv,statp,flags)280 __repmgr_stat_pp(dbenv, statp, flags)
281 	DB_ENV *dbenv;
282 	DB_REPMGR_STAT **statp;
283 	u_int32_t flags;
284 {
285 	COMPQUIET(statp, NULL);
286 	COMPQUIET(flags, 0);
287 
288 	return (__db_stat_not_built(dbenv->env));
289 }
290 
291 int
__repmgr_stat_print_pp(dbenv,flags)292 __repmgr_stat_print_pp(dbenv, flags)
293 	DB_ENV *dbenv;
294 	u_int32_t flags;
295 {
296 	COMPQUIET(flags, 0);
297 
298 	return (__db_stat_not_built(dbenv->env));
299 }
300 #endif
301 
302 /*
303  * PUBLIC: int __repmgr_site_list_pp
304  * PUBLIC:	__P((DB_ENV *, u_int *, DB_REPMGR_SITE **));
305  */
306 int
__repmgr_site_list_pp(dbenv,countp,listp)307 __repmgr_site_list_pp(dbenv, countp, listp)
308 	DB_ENV *dbenv;
309 	u_int *countp;
310 	DB_REPMGR_SITE **listp;
311 {
312 	ENV *env;
313 	DB_THREAD_INFO *ip;
314 	int ret;
315 
316 	env = dbenv->env;
317 
318 	ENV_ENTER(env, ip);
319 	ret = __repmgr_site_list_int(env, countp, listp);
320 	ENV_LEAVE(env, ip);
321 
322 	return (ret);
323 }
324 
325 /*
326  * PUBLIC: int __repmgr_site_list_int __P((ENV *, u_int *, DB_REPMGR_SITE **));
327  */
328 int
__repmgr_site_list_int(env,countp,listp)329 __repmgr_site_list_int(env, countp, listp)
330 	ENV *env;
331 	u_int *countp;
332 	DB_REPMGR_SITE **listp;
333 {
334 	DB_REP *db_rep;
335 	DB_REPMGR_SITE *status;
336 	REP *rep;
337 	REPMGR_SITE *site;
338 	size_t array_size, total_size;
339 	int eid, locked, ret;
340 	u_int count, i;
341 	char *name;
342 
343 	db_rep = env->rep_handle;
344 	ret = 0;
345 
346 	ENV_NOT_CONFIGURED(
347 	    env, db_rep->region, "DB_ENV->repmgr_site_list", DB_INIT_REP);
348 
349 	if (REP_ON(env)) {
350 		rep = db_rep->region;
351 		LOCK_MUTEX(db_rep->mutex);
352 		locked = TRUE;
353 
354 		if (rep->siteinfo_seq > db_rep->siteinfo_seq)
355 			ret = __repmgr_sync_siteaddr(env);
356 		if (ret != 0)
357 			goto err;
358 	} else {
359 		rep = NULL;
360 		locked = FALSE;
361 	}
362 
363 	/* Initialize for empty list or error return. */
364 	*countp = 0;
365 	*listp = NULL;
366 
367 	/*
368 	 * First, add up how much memory we need for the host names, excluding
369 	 * the local site.
370 	 */
371 	for (i = 0, count = 0, total_size = 0; i < db_rep->site_cnt; i++) {
372 		site = &db_rep->sites[i];
373 
374 		if ((int)i == db_rep->self_eid || site->membership == 0)
375 			continue;
376 
377 		/* Make room for the NUL terminating byte. */
378 		total_size += strlen(site->net_addr.host) + 1;
379 		count++;
380 	}
381 	if (count == 0)
382 		goto err;
383 	array_size = sizeof(DB_REPMGR_SITE) * count;
384 	total_size += array_size;
385 
386 	if ((ret = __os_umalloc(env, total_size, &status)) != 0)
387 		goto err;
388 
389 	/*
390 	 * Put the storage for the host names after the array of structs.  This
391 	 * way, the caller can free the whole thing in one single operation.
392 	 */
393 	name = (char *)((u_int8_t *)status + array_size);
394 	for (eid = 0, i = 0; eid < (int)db_rep->site_cnt; eid++) {
395 		site = &db_rep->sites[eid];
396 		if (eid == db_rep->self_eid || site->membership == 0)
397 			continue;
398 
399 		/* If we don't have rep, we can't really know EID yet. */
400 		status[i].eid = rep ? eid : DB_EID_INVALID;
401 
402 		status[i].host = name;
403 		(void)strcpy(name, site->net_addr.host);
404 		name += strlen(name) + 1;
405 
406 		status[i].port = site->net_addr.port;
407 
408 		status[i].flags = 0;
409 
410 		if (FLD_ISSET(site->config, DB_REPMGR_PEER))
411 			F_SET(&status[i], DB_REPMGR_ISPEER);
412 		if (FLD_ISSET(site->gmdb_flags, SITE_VIEW))
413 			F_SET(&status[i], DB_REPMGR_ISVIEW);
414 		else if (F_ISSET(site, SITE_ELECTABLE))
415 			F_SET(&status[i], DB_REPMGR_ISELECTABLE);
416 
417 		/*
418 		 * If we haven't started a communications thread, connection
419 		 * status is kind of meaningless.  This distinction is useful
420 		 * for calls from the db_stat utility: it could be useful for
421 		 * db_stat to display known sites with EID; but would be
422 		 * confusing for it to display "disconnected" if another process
423 		 * does indeed have a connection established (db_stat can't know
424 		 * that).
425 		 */
426 		if (db_rep->selector == NULL)
427 			status[i].status = 0;
428 		else if (site->state != SITE_CONNECTED)
429 			status[i].status = DB_REPMGR_DISCONNECTED;
430 		else if ((site->ref.conn.in != NULL &&
431 		    IS_READY_STATE(site->ref.conn.in->state)) ||
432 		    (site->ref.conn.out != NULL &&
433 		    IS_READY_STATE(site->ref.conn.out->state)))
434 			status[i].status = DB_REPMGR_CONNECTED;
435 		else
436 			status[i].status = DB_REPMGR_DISCONNECTED;
437 		memcpy(&status[i].max_ack_lsn, &site->max_ack, sizeof(DB_LSN));
438 
439 		i++;
440 	}
441 
442 	*countp = count;
443 	*listp = status;
444 
445 err:	if (locked)
446 		UNLOCK_MUTEX(db_rep->mutex);
447 	return (ret);
448 }
449