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