xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c (revision 7297a2ff53de3005d30c8874d7c8e4a8c58c709e)
1 /*-
2  * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/queue.h>
32 
33 #include <bsnmp/snmpmod.h>
34 
35 #include <string.h>
36 
37 #include "hast.h"
38 #include "hast_oid.h"
39 #include "hast_proto.h"
40 #include "hast_tree.h"
41 #include "nv.h"
42 #include "pjdlog.h"
43 #include "proto.h"
44 
45 #define UPDATE_INTERVAL	500	/* update interval in ticks */
46 
47 static struct lmodule *module;
48 
49 static const struct asn_oid oid_hast = OIDX_begemotHast;
50 
51 /* the Object Resource registration index */
52 static u_int hast_index = 0;
53 
54 /*
55  * Structure that describes single resource.
56  */
57 struct hast_snmp_resource {
58 	TAILQ_ENTRY(hast_snmp_resource) link;
59 	int32_t		index;
60 	char		name[NAME_MAX];
61 	int		error;
62 	int		role;
63 	char		provname[NAME_MAX];
64 	char		localpath[PATH_MAX];
65 	int32_t		extentsize;
66 	int32_t		keepdirty;
67 	char		remoteaddr[HAST_ADDRSIZE];
68 	char		sourceaddr[HAST_ADDRSIZE];
69 	int		replication;
70 	int		status;
71 	uint64_t	dirty;
72 	uint64_t	reads;
73 	uint64_t	writes;
74 	uint64_t	deletes;
75 	uint64_t	flushes;
76 	uint64_t	activemap_updates;
77 	uint64_t	read_errors;
78 	uint64_t	write_errors;
79 	uint64_t	delete_errors;
80 	uint64_t	flush_errors;
81 };
82 
83 static TAILQ_HEAD(, hast_snmp_resource) resources =
84     TAILQ_HEAD_INITIALIZER(resources);
85 
86 /* Path to configuration file. */
87 static u_char *cfgpath;
88 /* Ticks of the last hast resources update. */
89 static uint64_t last_resources_update;
90 
91 static void free_resources(void);
92 static int hastctl(struct nv *nvin, struct nv **nvout);
93 static int hast_fini(void);
94 static int hast_init(struct lmodule *mod, int argc, char *argv[]);
95 static void hast_start(void);
96 static int set_role(const char *resource, int role);
97 static int str2role(const char *str);
98 static int str2replication(const char *str);
99 static int str2status(const char *str);
100 static int update_resources(void);
101 
102 const struct snmp_module config = {
103     .comment   = "This module implements the BEGEMOT MIB for HAST.",
104     .init      = hast_init,
105     .start     = hast_start,
106     .fini      = hast_fini,
107     .tree      = hast_ctree,
108     .tree_size = hast_CTREE_SIZE,
109 };
110 
111 static int
112 hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
113 {
114 
115 	module = mod;
116 
117 	pjdlog_init(PJDLOG_MODE_SYSLOG);
118 	pjdlog_debug_set(0);
119 
120 	cfgpath = malloc(sizeof(HAST_CONFIG));
121 	if (cfgpath == NULL) {
122 		pjdlog_error("Unable to allocate %zu bytes for cfgpath",
123 		    sizeof(HAST_CONFIG));
124 		return (-1);
125 	}
126 	strcpy(cfgpath, HAST_CONFIG);
127 	return(0);
128 }
129 
130 static void
131 hast_start(void)
132 {
133 	hast_index = or_register(&oid_hast,
134 	    "The MIB module for BEGEMOT-HAST-MIB.", module);
135 }
136 
137 static int
138 hast_fini(void)
139 {
140 
141 	or_unregister(hast_index);
142 	free_resources();
143 	free(cfgpath);
144 	return (0);
145 }
146 
147 static void
148 free_resources(void)
149 {
150 	struct hast_snmp_resource *res;
151 
152 	while ((res = TAILQ_FIRST(&resources)) != NULL) {
153 		TAILQ_REMOVE(&resources, res, link);
154 		free(res);
155 	}
156 }
157 
158 static int
159 str2role(const char *str)
160 {
161 
162 	if (strcmp(str, "init") == 0)
163 		return (HAST_ROLE_INIT);
164 	if (strcmp(str, "primary") == 0)
165 		return (HAST_ROLE_PRIMARY);
166 	if (strcmp(str, "secondary") == 0)
167 		return (HAST_ROLE_SECONDARY);
168 	return (HAST_ROLE_UNDEF);
169 }
170 
171 static int
172 str2replication(const char *str)
173 {
174 
175 	if (strcmp(str, "fullsync") == 0)
176 		return (HAST_REPLICATION_FULLSYNC);
177 	if (strcmp(str, "memsync") == 0)
178 		return (HAST_REPLICATION_MEMSYNC);
179 	if (strcmp(str, "async") == 0)
180 		return (HAST_REPLICATION_ASYNC);
181 	return (-1);
182 }
183 
184 static int
185 str2status(const char *str)
186 {
187 
188 	if (strcmp(str, "complete") == 0)
189 		return (0);
190 	if (strcmp(str, "degraded") == 0)
191 		return (1);
192 	return (-1);
193 }
194 
195 static int
196 hastctl(struct nv *nvin, struct nv **nvout)
197 {
198 	struct hastd_config *cfg;
199 	struct proto_conn *conn;
200 	struct nv *nv;
201 	int error;
202 
203 	cfg = yy_config_parse(cfgpath, true);
204 	if (cfg == NULL)
205 		return (-1);
206 
207 	/* Setup control connection... */
208 	if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) {
209 		pjdlog_error("Unable to setup control connection to %s",
210 		    cfg->hc_controladdr);
211 		return (-1);
212 	}
213 	/* ...and connect to hastd. */
214 	if (proto_connect(conn, HAST_TIMEOUT) == -1) {
215 		pjdlog_error("Unable to connect to hastd via %s",
216 		    cfg->hc_controladdr);
217 		proto_close(conn);
218 		return (-1);
219 	}
220 	/* Send the command to the server... */
221 	if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) {
222 		pjdlog_error("Unable to send command to hastd via %s",
223 		    cfg->hc_controladdr);
224 		proto_close(conn);
225 		return (-1);
226 	}
227 	/* ...and receive reply. */
228 	if (hast_proto_recv_hdr(conn, &nv) == -1) {
229 		pjdlog_error("cannot receive reply from hastd via %s",
230 		    cfg->hc_controladdr);
231 		proto_close(conn);
232 		return (-1);
233 	}
234 	proto_close(conn);
235 	error = nv_get_int16(nv, "error");
236 	if (error != 0) {
237 		pjdlog_error("Error %d received from hastd.", error);
238 		nv_free(nv);
239 		return (-1);
240 	}
241 	nv_set_error(nv, 0);
242 	*nvout = nv;
243 	return (0);
244 }
245 
246 static int
247 set_role(const char *resource, int role)
248 {
249 	struct nv *nvin, *nvout;
250 	int error;
251 
252 	nvin = nv_alloc();
253 	nv_add_string(nvin, resource, "resource%d", 0);
254 	nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd");
255 	nv_add_uint8(nvin, role, "role");
256 	error = hastctl(nvin, &nvout);
257 	nv_free(nvin);
258 	if (error != 0)
259 		return (-1);
260 	nv_free(nvout);
261 	return (SNMP_ERR_NOERROR);
262 }
263 
264 static int
265 update_resources(void)
266 {
267 	struct hast_snmp_resource *res;
268 	struct nv *nvin, *nvout;
269 	static uint64_t now;
270 	unsigned int i;
271 	const char *str;
272 	int error;
273 
274 	now = get_ticks();
275 	if (now - last_resources_update < UPDATE_INTERVAL)
276 		return (0);
277 
278 	last_resources_update = now;
279 
280 	free_resources();
281 
282 	nvin = nv_alloc();
283 	nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd");
284 	nv_add_string(nvin, "all", "resource%d", 0);
285 	error = hastctl(nvin, &nvout);
286 	nv_free(nvin);
287 	if (error != 0)
288 		return (-1);
289 
290 	for (i = 0; ; i++) {
291 		str = nv_get_string(nvout, "resource%u", i);
292 		if (str == NULL)
293 			break;
294 		res = calloc(1, sizeof(*res));
295 		if (res == NULL) {
296 			pjdlog_error("Unable to allocate %zu bytes for "
297 			    "resource", sizeof(*res));
298 			return (-1);
299 		}
300 		res->index = i + 1;
301 		strncpy(res->name, str, sizeof(res->name) - 1);
302 		error = nv_get_int16(nvout, "error%u", i);
303 		if (error != 0)
304 			continue;
305 		str = nv_get_string(nvout, "role%u", i);
306 		res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF;
307 		str = nv_get_string(nvout, "provname%u", i);
308 		if (str != NULL)
309 			strncpy(res->provname, str, sizeof(res->provname) - 1);
310 		str = nv_get_string(nvout, "localpath%u", i);
311 		if (str != NULL) {
312 			strncpy(res->localpath, str,
313 			    sizeof(res->localpath) - 1);
314 		}
315 		res->extentsize = nv_get_uint32(nvout, "extentsize%u", i);
316 		res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i);
317 		str = nv_get_string(nvout, "remoteaddr%u", i);
318 		if (str != NULL) {
319 			strncpy(res->remoteaddr, str,
320 			    sizeof(res->remoteaddr) - 1);
321 		}
322 		str = nv_get_string(nvout, "sourceaddr%u", i);
323 		if (str != NULL) {
324 			strncpy(res->sourceaddr, str,
325 			    sizeof(res->sourceaddr) - 1);
326 		}
327 		str = nv_get_string(nvout, "replication%u", i);
328 		res->replication = str != NULL ? str2replication(str) : -1;
329 		str = nv_get_string(nvout, "status%u", i);
330 		res->status = str != NULL ? str2status(str) : -1;
331 		res->dirty = nv_get_uint64(nvout, "dirty%u", i);
332 		res->reads = nv_get_uint64(nvout, "stat_read%u", i);
333 		res->writes = nv_get_uint64(nvout, "stat_write%u", i);
334 		res->deletes = nv_get_uint64(nvout, "stat_delete%u", i);
335 		res->flushes = nv_get_uint64(nvout, "stat_flush%u", i);
336 		res->activemap_updates =
337 		    nv_get_uint64(nvout, "stat_activemap_update%u", i);
338 		res->read_errors =
339 		    nv_get_uint64(nvout, "stat_read_error%u", i);
340 		res->write_errors =
341 		    nv_get_uint64(nvout, "stat_write_error%u", i);
342 		res->delete_errors =
343 		    nv_get_uint64(nvout, "stat_delete_error%u", i);
344 		res->flush_errors =
345 		    nv_get_uint64(nvout, "stat_flush_error%u", i);
346 		TAILQ_INSERT_TAIL(&resources, res, link);
347 	}
348 	nv_free(nvout);
349 	return (0);
350 }
351 
352 int
353 op_hastConfig(struct snmp_context *context, struct snmp_value *value,
354     u_int sub, u_int iidx __unused, enum snmp_op op)
355 {
356 	asn_subid_t which;
357 
358 	which = value->var.subs[sub - 1];
359 
360 	switch (op) {
361 	case SNMP_OP_GET:
362 		switch (which) {
363 		case LEAF_hastConfigFile:
364 			return (string_get(value, cfgpath, -1));
365 		default:
366 			return (SNMP_ERR_RES_UNAVAIL);
367 		}
368 	case SNMP_OP_SET:
369 		switch (which) {
370 		case LEAF_hastConfigFile:
371 			return (string_save(value, context, -1,
372 			    (u_char **)&cfgpath));
373 		default:
374 			return (SNMP_ERR_RES_UNAVAIL);
375 		}
376 	case SNMP_OP_GETNEXT:
377 	case SNMP_OP_ROLLBACK:
378 	case SNMP_OP_COMMIT:
379 		return (SNMP_ERR_NOERROR);
380 	default:
381 		return (SNMP_ERR_RES_UNAVAIL);
382 	}
383 }
384 
385 int
386 op_hastResourceTable(struct snmp_context *context __unused,
387     struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op)
388 {
389 	struct hast_snmp_resource *res;
390 	asn_subid_t which;
391 	int ret;
392 
393 	if (update_resources() == -1)
394 		return (SNMP_ERR_RES_UNAVAIL);
395 
396 	which = value->var.subs[sub - 1];
397 
398 	switch (op) {
399 	case SNMP_OP_GETNEXT:
400 		res = NEXT_OBJECT_INT(&resources, &value->var, sub);
401 		if (res == NULL)
402 			return (SNMP_ERR_NOSUCHNAME);
403 		value->var.len = sub + 1;
404 		value->var.subs[sub] = res->index;
405 		break;
406 	case SNMP_OP_GET:
407 		if (value->var.len - sub != 1)
408 			return (SNMP_ERR_NOSUCHNAME);
409 		res = FIND_OBJECT_INT(&resources, &value->var, sub);
410 		if (res == NULL)
411 			return (SNMP_ERR_NOSUCHNAME);
412 		break;
413 	case SNMP_OP_SET:
414 		res = FIND_OBJECT_INT(&resources, &value->var, sub);
415 		if (res == NULL)
416 			return (SNMP_ERR_NOSUCHNAME);
417 		switch (which) {
418 		case LEAF_hastResourceRole:
419 			ret = set_role(res->name, value->v.integer);
420 			/* force update on next run */
421 			last_resources_update = 0;
422 			break;
423 		default:
424 			ret = SNMP_ERR_NOT_WRITEABLE;
425 			break;
426 		}
427 		return ret;
428 	case SNMP_OP_ROLLBACK:
429 	case SNMP_OP_COMMIT:
430 		return (SNMP_ERR_NOERROR);
431 	default:
432 		return (SNMP_ERR_RES_UNAVAIL);
433 	}
434 
435 	ret = SNMP_ERR_NOERROR;
436 
437 	switch (which) {
438 	case LEAF_hastResourceIndex:
439 		value->v.integer = res->index;
440 		break;
441 	case LEAF_hastResourceName:
442 		ret = string_get(value, res->name, -1);
443 		break;
444 	case LEAF_hastResourceRole:
445 		value->v.integer = res->role;
446 		break;
447 	case LEAF_hastResourceProvName:
448 		ret = string_get(value, res->provname, -1);
449 		break;
450 	case LEAF_hastResourceLocalPath:
451 		ret = string_get(value, res->localpath, -1);
452 		break;
453 	case LEAF_hastResourceExtentSize:
454 		value->v.integer = res->extentsize;
455 		break;
456 	case LEAF_hastResourceKeepDirty:
457 		value->v.integer = res->keepdirty;
458 		break;
459 	case LEAF_hastResourceRemoteAddr:
460 		ret = string_get(value, res->remoteaddr, -1);
461 		break;
462 	case LEAF_hastResourceSourceAddr:
463 		ret = string_get(value, res->sourceaddr, -1);
464 		break;
465 	case LEAF_hastResourceReplication:
466 		value->v.integer = res->replication;
467 		break;
468 	case LEAF_hastResourceStatus:
469 		value->v.integer = res->status;
470 		break;
471 	case LEAF_hastResourceDirty:
472 		value->v.counter64 = res->dirty;
473 		break;
474 	case LEAF_hastResourceReads:
475 		value->v.counter64 = res->reads;
476 		break;
477 	case LEAF_hastResourceWrites:
478 		value->v.counter64 = res->writes;
479 		break;
480 	case LEAF_hastResourceDeletes:
481 		value->v.counter64 = res->deletes;
482 		break;
483 	case LEAF_hastResourceFlushes:
484 		value->v.counter64 = res->flushes;
485 		break;
486 	case LEAF_hastResourceActivemapUpdates:
487 		value->v.counter64 = res->activemap_updates;
488 		break;
489 	case LEAF_hastResourceReadErrors:
490 		value->v.counter64 = res->read_errors;
491 		break;
492 	case LEAF_hastResourceWriteErrors:
493 		value->v.counter64 = res->write_errors;
494 		break;
495 	case LEAF_hastResourceDeleteErrors:
496 		value->v.counter64 = res->delete_errors;
497 		break;
498 	case LEAF_hastResourceFlushErrors:
499 		value->v.counter64 = res->flush_errors;
500 		break;
501 	default:
502 		ret = SNMP_ERR_RES_UNAVAIL;
503 		break;
504 	}
505 	return (ret);
506 }
507