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