1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2013 Mikolaj Golub <[email protected]>
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/cdefs.h>
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 pid_t workerpid;
82 uint32_t local_queue;
83 uint32_t send_queue;
84 uint32_t recv_queue;
85 uint32_t done_queue;
86 uint32_t idle_queue;
87 };
88
89 static TAILQ_HEAD(, hast_snmp_resource) resources =
90 TAILQ_HEAD_INITIALIZER(resources);
91
92 /* Path to configuration file. */
93 static u_char *cfgpath;
94 /* Ticks of the last hast resources update. */
95 static uint64_t last_resources_update;
96
97 static void free_resources(void);
98 static int hastctl(struct nv *nvin, struct nv **nvout);
99 static int hast_fini(void);
100 static int hast_init(struct lmodule *mod, int argc, char *argv[]);
101 static void hast_start(void);
102 static int set_role(const char *resource, int role);
103 static int str2role(const char *str);
104 static int str2replication(const char *str);
105 static int str2status(const char *str);
106 static int update_resources(void);
107
108 const struct snmp_module config = {
109 .comment = "This module implements the BEGEMOT MIB for HAST.",
110 .init = hast_init,
111 .start = hast_start,
112 .fini = hast_fini,
113 .tree = hast_ctree,
114 .tree_size = hast_CTREE_SIZE,
115 };
116
117 static int
hast_init(struct lmodule * mod,int argc __unused,char * argv[]__unused)118 hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
119 {
120
121 module = mod;
122
123 pjdlog_init(PJDLOG_MODE_SYSLOG);
124 pjdlog_debug_set(0);
125
126 cfgpath = malloc(sizeof(HAST_CONFIG));
127 if (cfgpath == NULL) {
128 pjdlog_error("Unable to allocate %zu bytes for cfgpath",
129 sizeof(HAST_CONFIG));
130 return (-1);
131 }
132 strcpy(cfgpath, HAST_CONFIG);
133 return(0);
134 }
135
136 static void
hast_start(void)137 hast_start(void)
138 {
139 hast_index = or_register(&oid_hast,
140 "The MIB module for BEGEMOT-HAST-MIB.", module);
141 }
142
143 static int
hast_fini(void)144 hast_fini(void)
145 {
146
147 or_unregister(hast_index);
148 free_resources();
149 free(cfgpath);
150 return (0);
151 }
152
153 static void
free_resources(void)154 free_resources(void)
155 {
156 struct hast_snmp_resource *res;
157
158 while ((res = TAILQ_FIRST(&resources)) != NULL) {
159 TAILQ_REMOVE(&resources, res, link);
160 free(res);
161 }
162 }
163
164 static int
str2role(const char * str)165 str2role(const char *str)
166 {
167
168 if (strcmp(str, "init") == 0)
169 return (HAST_ROLE_INIT);
170 if (strcmp(str, "primary") == 0)
171 return (HAST_ROLE_PRIMARY);
172 if (strcmp(str, "secondary") == 0)
173 return (HAST_ROLE_SECONDARY);
174 return (HAST_ROLE_UNDEF);
175 }
176
177 static int
str2replication(const char * str)178 str2replication(const char *str)
179 {
180
181 if (strcmp(str, "fullsync") == 0)
182 return (HAST_REPLICATION_FULLSYNC);
183 if (strcmp(str, "memsync") == 0)
184 return (HAST_REPLICATION_MEMSYNC);
185 if (strcmp(str, "async") == 0)
186 return (HAST_REPLICATION_ASYNC);
187 return (-1);
188 }
189
190 static int
str2status(const char * str)191 str2status(const char *str)
192 {
193
194 if (strcmp(str, "complete") == 0)
195 return (0);
196 if (strcmp(str, "degraded") == 0)
197 return (1);
198 return (-1);
199 }
200
201 static int
hastctl(struct nv * nvin,struct nv ** nvout)202 hastctl(struct nv *nvin, struct nv **nvout)
203 {
204 struct hastd_config *cfg;
205 struct proto_conn *conn;
206 struct nv *nv;
207 int error;
208
209 cfg = yy_config_parse(cfgpath, true);
210 if (cfg == NULL)
211 return (-1);
212
213 /* Setup control connection... */
214 if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) {
215 pjdlog_error("Unable to setup control connection to %s",
216 cfg->hc_controladdr);
217 return (-1);
218 }
219 /* ...and connect to hastd. */
220 if (proto_connect(conn, HAST_TIMEOUT) == -1) {
221 pjdlog_error("Unable to connect to hastd via %s",
222 cfg->hc_controladdr);
223 proto_close(conn);
224 return (-1);
225 }
226 /* Send the command to the server... */
227 if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) {
228 pjdlog_error("Unable to send command to hastd via %s",
229 cfg->hc_controladdr);
230 proto_close(conn);
231 return (-1);
232 }
233 /* ...and receive reply. */
234 if (hast_proto_recv_hdr(conn, &nv) == -1) {
235 pjdlog_error("cannot receive reply from hastd via %s",
236 cfg->hc_controladdr);
237 proto_close(conn);
238 return (-1);
239 }
240 proto_close(conn);
241 error = nv_get_int16(nv, "error");
242 if (error != 0) {
243 pjdlog_error("Error %d received from hastd.", error);
244 nv_free(nv);
245 return (-1);
246 }
247 nv_set_error(nv, 0);
248 *nvout = nv;
249 return (0);
250 }
251
252 static int
set_role(const char * resource,int role)253 set_role(const char *resource, int role)
254 {
255 struct nv *nvin, *nvout;
256 int error;
257
258 nvin = nv_alloc();
259 nv_add_string(nvin, resource, "resource%d", 0);
260 nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd");
261 nv_add_uint8(nvin, role, "role");
262 error = hastctl(nvin, &nvout);
263 nv_free(nvin);
264 if (error != 0)
265 return (-1);
266 nv_free(nvout);
267 return (SNMP_ERR_NOERROR);
268 }
269
270 static int
update_resources(void)271 update_resources(void)
272 {
273 struct hast_snmp_resource *res;
274 struct nv *nvin, *nvout;
275 static uint64_t now;
276 unsigned int i;
277 const char *str;
278 int error;
279
280 now = get_ticks();
281 if (now - last_resources_update < UPDATE_INTERVAL)
282 return (0);
283
284 last_resources_update = now;
285
286 free_resources();
287
288 nvin = nv_alloc();
289 nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd");
290 nv_add_string(nvin, "all", "resource%d", 0);
291 error = hastctl(nvin, &nvout);
292 nv_free(nvin);
293 if (error != 0)
294 return (-1);
295
296 for (i = 0; ; i++) {
297 str = nv_get_string(nvout, "resource%u", i);
298 if (str == NULL)
299 break;
300 res = calloc(1, sizeof(*res));
301 if (res == NULL) {
302 pjdlog_error("Unable to allocate %zu bytes for "
303 "resource", sizeof(*res));
304 return (-1);
305 }
306 res->index = i + 1;
307 strncpy(res->name, str, sizeof(res->name) - 1);
308 error = nv_get_int16(nvout, "error%u", i);
309 if (error != 0)
310 continue;
311 str = nv_get_string(nvout, "role%u", i);
312 res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF;
313 str = nv_get_string(nvout, "provname%u", i);
314 if (str != NULL)
315 strncpy(res->provname, str, sizeof(res->provname) - 1);
316 str = nv_get_string(nvout, "localpath%u", i);
317 if (str != NULL) {
318 strncpy(res->localpath, str,
319 sizeof(res->localpath) - 1);
320 }
321 res->extentsize = nv_get_uint32(nvout, "extentsize%u", i);
322 res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i);
323 str = nv_get_string(nvout, "remoteaddr%u", i);
324 if (str != NULL) {
325 strncpy(res->remoteaddr, str,
326 sizeof(res->remoteaddr) - 1);
327 }
328 str = nv_get_string(nvout, "sourceaddr%u", i);
329 if (str != NULL) {
330 strncpy(res->sourceaddr, str,
331 sizeof(res->sourceaddr) - 1);
332 }
333 str = nv_get_string(nvout, "replication%u", i);
334 res->replication = str != NULL ? str2replication(str) : -1;
335 str = nv_get_string(nvout, "status%u", i);
336 res->status = str != NULL ? str2status(str) : -1;
337 res->dirty = nv_get_uint64(nvout, "dirty%u", i);
338 res->reads = nv_get_uint64(nvout, "stat_read%u", i);
339 res->writes = nv_get_uint64(nvout, "stat_write%u", i);
340 res->deletes = nv_get_uint64(nvout, "stat_delete%u", i);
341 res->flushes = nv_get_uint64(nvout, "stat_flush%u", i);
342 res->activemap_updates =
343 nv_get_uint64(nvout, "stat_activemap_update%u", i);
344 res->read_errors =
345 nv_get_uint64(nvout, "stat_read_error%u", i);
346 res->write_errors =
347 nv_get_uint64(nvout, "stat_write_error%u", i);
348 res->delete_errors =
349 nv_get_uint64(nvout, "stat_delete_error%u", i);
350 res->flush_errors =
351 nv_get_uint64(nvout, "stat_flush_error%u", i);
352 res->workerpid = nv_get_int32(nvout, "workerpid%u", i);
353 res->local_queue =
354 nv_get_uint64(nvout, "local_queue_size%u", i);
355 res->send_queue =
356 nv_get_uint64(nvout, "send_queue_size%u", i);
357 res->recv_queue =
358 nv_get_uint64(nvout, "recv_queue_size%u", i);
359 res->done_queue =
360 nv_get_uint64(nvout, "done_queue_size%u", i);
361 res->idle_queue =
362 nv_get_uint64(nvout, "idle_queue_size%u", i);
363 TAILQ_INSERT_TAIL(&resources, res, link);
364 }
365 nv_free(nvout);
366 return (0);
367 }
368
369 int
op_hastConfig(struct snmp_context * context,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)370 op_hastConfig(struct snmp_context *context, struct snmp_value *value,
371 u_int sub, u_int iidx __unused, enum snmp_op op)
372 {
373 asn_subid_t which;
374
375 which = value->var.subs[sub - 1];
376
377 switch (op) {
378 case SNMP_OP_GET:
379 switch (which) {
380 case LEAF_hastConfigFile:
381 return (string_get(value, cfgpath, -1));
382 default:
383 return (SNMP_ERR_RES_UNAVAIL);
384 }
385 case SNMP_OP_SET:
386 switch (which) {
387 case LEAF_hastConfigFile:
388 return (string_save(value, context, -1,
389 (u_char **)&cfgpath));
390 default:
391 return (SNMP_ERR_RES_UNAVAIL);
392 }
393 case SNMP_OP_GETNEXT:
394 case SNMP_OP_ROLLBACK:
395 case SNMP_OP_COMMIT:
396 return (SNMP_ERR_NOERROR);
397 default:
398 return (SNMP_ERR_RES_UNAVAIL);
399 }
400 }
401
402 int
op_hastResourceTable(struct snmp_context * context __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)403 op_hastResourceTable(struct snmp_context *context __unused,
404 struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op)
405 {
406 struct hast_snmp_resource *res;
407 asn_subid_t which;
408 int ret;
409
410 if (update_resources() == -1)
411 return (SNMP_ERR_RES_UNAVAIL);
412
413 which = value->var.subs[sub - 1];
414
415 switch (op) {
416 case SNMP_OP_GETNEXT:
417 res = NEXT_OBJECT_INT(&resources, &value->var, sub);
418 if (res == NULL)
419 return (SNMP_ERR_NOSUCHNAME);
420 value->var.len = sub + 1;
421 value->var.subs[sub] = res->index;
422 break;
423 case SNMP_OP_GET:
424 if (value->var.len - sub != 1)
425 return (SNMP_ERR_NOSUCHNAME);
426 res = FIND_OBJECT_INT(&resources, &value->var, sub);
427 if (res == NULL)
428 return (SNMP_ERR_NOSUCHNAME);
429 break;
430 case SNMP_OP_SET:
431 res = FIND_OBJECT_INT(&resources, &value->var, sub);
432 if (res == NULL)
433 return (SNMP_ERR_NOSUCHNAME);
434 switch (which) {
435 case LEAF_hastResourceRole:
436 ret = set_role(res->name, value->v.integer);
437 /* force update on next run */
438 last_resources_update = 0;
439 break;
440 default:
441 ret = SNMP_ERR_NOT_WRITEABLE;
442 break;
443 }
444 return ret;
445 case SNMP_OP_ROLLBACK:
446 case SNMP_OP_COMMIT:
447 return (SNMP_ERR_NOERROR);
448 default:
449 return (SNMP_ERR_RES_UNAVAIL);
450 }
451
452 ret = SNMP_ERR_NOERROR;
453
454 switch (which) {
455 case LEAF_hastResourceIndex:
456 value->v.integer = res->index;
457 break;
458 case LEAF_hastResourceName:
459 ret = string_get(value, res->name, -1);
460 break;
461 case LEAF_hastResourceRole:
462 value->v.integer = res->role;
463 break;
464 case LEAF_hastResourceProvName:
465 ret = string_get(value, res->provname, -1);
466 break;
467 case LEAF_hastResourceLocalPath:
468 ret = string_get(value, res->localpath, -1);
469 break;
470 case LEAF_hastResourceExtentSize:
471 value->v.integer = res->extentsize;
472 break;
473 case LEAF_hastResourceKeepDirty:
474 value->v.integer = res->keepdirty;
475 break;
476 case LEAF_hastResourceRemoteAddr:
477 ret = string_get(value, res->remoteaddr, -1);
478 break;
479 case LEAF_hastResourceSourceAddr:
480 ret = string_get(value, res->sourceaddr, -1);
481 break;
482 case LEAF_hastResourceReplication:
483 value->v.integer = res->replication;
484 break;
485 case LEAF_hastResourceStatus:
486 value->v.integer = res->status;
487 break;
488 case LEAF_hastResourceDirty:
489 value->v.counter64 = res->dirty;
490 break;
491 case LEAF_hastResourceReads:
492 value->v.counter64 = res->reads;
493 break;
494 case LEAF_hastResourceWrites:
495 value->v.counter64 = res->writes;
496 break;
497 case LEAF_hastResourceDeletes:
498 value->v.counter64 = res->deletes;
499 break;
500 case LEAF_hastResourceFlushes:
501 value->v.counter64 = res->flushes;
502 break;
503 case LEAF_hastResourceActivemapUpdates:
504 value->v.counter64 = res->activemap_updates;
505 break;
506 case LEAF_hastResourceReadErrors:
507 value->v.counter64 = res->read_errors;
508 break;
509 case LEAF_hastResourceWriteErrors:
510 value->v.counter64 = res->write_errors;
511 break;
512 case LEAF_hastResourceDeleteErrors:
513 value->v.counter64 = res->delete_errors;
514 break;
515 case LEAF_hastResourceFlushErrors:
516 value->v.counter64 = res->flush_errors;
517 break;
518 case LEAF_hastResourceWorkerPid:
519 value->v.integer = res->workerpid;
520 break;
521 case LEAF_hastResourceLocalQueue:
522 value->v.uint32 = res->local_queue;
523 break;
524 case LEAF_hastResourceSendQueue:
525 value->v.uint32 = res->send_queue;
526 break;
527 case LEAF_hastResourceRecvQueue:
528 value->v.uint32 = res->recv_queue;
529 break;
530 case LEAF_hastResourceDoneQueue:
531 value->v.uint32 = res->done_queue;
532 break;
533 case LEAF_hastResourceIdleQueue:
534 value->v.uint32 = res->idle_queue;
535 break;
536 default:
537 ret = SNMP_ERR_RES_UNAVAIL;
538 break;
539 }
540 return (ret);
541 }
542