1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __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 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 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 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 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 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 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 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 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 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 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 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 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