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