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/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 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 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 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 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 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 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 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 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 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 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 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 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