1 /*- 2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 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 }; 83 84 static TAILQ_HEAD(, hast_snmp_resource) resources = 85 TAILQ_HEAD_INITIALIZER(resources); 86 87 /* Path to configuration file. */ 88 static u_char *cfgpath; 89 /* Ticks of the last hast resources update. */ 90 static uint64_t last_resources_update; 91 92 static void free_resources(void); 93 static int hastctl(struct nv *nvin, struct nv **nvout); 94 static int hast_fini(void); 95 static int hast_init(struct lmodule *mod, int argc, char *argv[]); 96 static void hast_start(void); 97 static int set_role(const char *resource, int role); 98 static int str2role(const char *str); 99 static int str2replication(const char *str); 100 static int str2status(const char *str); 101 static int update_resources(void); 102 103 const struct snmp_module config = { 104 .comment = "This module implements the BEGEMOT MIB for HAST.", 105 .init = hast_init, 106 .start = hast_start, 107 .fini = hast_fini, 108 .tree = hast_ctree, 109 .tree_size = hast_CTREE_SIZE, 110 }; 111 112 static int 113 hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) 114 { 115 116 module = mod; 117 118 pjdlog_init(PJDLOG_MODE_SYSLOG); 119 pjdlog_debug_set(0); 120 121 cfgpath = malloc(sizeof(HAST_CONFIG)); 122 if (cfgpath == NULL) { 123 pjdlog_error("Unable to allocate %zu bytes for cfgpath", 124 sizeof(HAST_CONFIG)); 125 return (-1); 126 } 127 strcpy(cfgpath, HAST_CONFIG); 128 return(0); 129 } 130 131 static void 132 hast_start(void) 133 { 134 hast_index = or_register(&oid_hast, 135 "The MIB module for BEGEMOT-HAST-MIB.", module); 136 } 137 138 static int 139 hast_fini(void) 140 { 141 142 or_unregister(hast_index); 143 free_resources(); 144 free(cfgpath); 145 return (0); 146 } 147 148 static void 149 free_resources(void) 150 { 151 struct hast_snmp_resource *res; 152 153 while ((res = TAILQ_FIRST(&resources)) != NULL) { 154 TAILQ_REMOVE(&resources, res, link); 155 free(res); 156 } 157 } 158 159 static int 160 str2role(const char *str) 161 { 162 163 if (strcmp(str, "init") == 0) 164 return (HAST_ROLE_INIT); 165 if (strcmp(str, "primary") == 0) 166 return (HAST_ROLE_PRIMARY); 167 if (strcmp(str, "secondary") == 0) 168 return (HAST_ROLE_SECONDARY); 169 return (HAST_ROLE_UNDEF); 170 } 171 172 static int 173 str2replication(const char *str) 174 { 175 176 if (strcmp(str, "fullsync") == 0) 177 return (HAST_REPLICATION_FULLSYNC); 178 if (strcmp(str, "memsync") == 0) 179 return (HAST_REPLICATION_MEMSYNC); 180 if (strcmp(str, "async") == 0) 181 return (HAST_REPLICATION_ASYNC); 182 return (-1); 183 } 184 185 static int 186 str2status(const char *str) 187 { 188 189 if (strcmp(str, "complete") == 0) 190 return (0); 191 if (strcmp(str, "degraded") == 0) 192 return (1); 193 return (-1); 194 } 195 196 static int 197 hastctl(struct nv *nvin, struct nv **nvout) 198 { 199 struct hastd_config *cfg; 200 struct proto_conn *conn; 201 struct nv *nv; 202 int error; 203 204 cfg = yy_config_parse(cfgpath, true); 205 if (cfg == NULL) 206 return (-1); 207 208 /* Setup control connection... */ 209 if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) { 210 pjdlog_error("Unable to setup control connection to %s", 211 cfg->hc_controladdr); 212 return (-1); 213 } 214 /* ...and connect to hastd. */ 215 if (proto_connect(conn, HAST_TIMEOUT) == -1) { 216 pjdlog_error("Unable to connect to hastd via %s", 217 cfg->hc_controladdr); 218 proto_close(conn); 219 return (-1); 220 } 221 /* Send the command to the server... */ 222 if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) { 223 pjdlog_error("Unable to send command to hastd via %s", 224 cfg->hc_controladdr); 225 proto_close(conn); 226 return (-1); 227 } 228 /* ...and receive reply. */ 229 if (hast_proto_recv_hdr(conn, &nv) == -1) { 230 pjdlog_error("cannot receive reply from hastd via %s", 231 cfg->hc_controladdr); 232 proto_close(conn); 233 return (-1); 234 } 235 proto_close(conn); 236 error = nv_get_int16(nv, "error"); 237 if (error != 0) { 238 pjdlog_error("Error %d received from hastd.", error); 239 nv_free(nv); 240 return (-1); 241 } 242 nv_set_error(nv, 0); 243 *nvout = nv; 244 return (0); 245 } 246 247 static int 248 set_role(const char *resource, int role) 249 { 250 struct nv *nvin, *nvout; 251 int error; 252 253 nvin = nv_alloc(); 254 nv_add_string(nvin, resource, "resource%d", 0); 255 nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd"); 256 nv_add_uint8(nvin, role, "role"); 257 error = hastctl(nvin, &nvout); 258 nv_free(nvin); 259 if (error != 0) 260 return (-1); 261 nv_free(nvout); 262 return (SNMP_ERR_NOERROR); 263 } 264 265 static int 266 update_resources(void) 267 { 268 struct hast_snmp_resource *res; 269 struct nv *nvin, *nvout; 270 static uint64_t now; 271 unsigned int i; 272 const char *str; 273 int error; 274 275 now = get_ticks(); 276 if (now - last_resources_update < UPDATE_INTERVAL) 277 return (0); 278 279 last_resources_update = now; 280 281 free_resources(); 282 283 nvin = nv_alloc(); 284 nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd"); 285 nv_add_string(nvin, "all", "resource%d", 0); 286 error = hastctl(nvin, &nvout); 287 nv_free(nvin); 288 if (error != 0) 289 return (-1); 290 291 for (i = 0; ; i++) { 292 str = nv_get_string(nvout, "resource%u", i); 293 if (str == NULL) 294 break; 295 res = calloc(1, sizeof(*res)); 296 if (res == NULL) { 297 pjdlog_error("Unable to allocate %zu bytes for " 298 "resource", sizeof(*res)); 299 return (-1); 300 } 301 res->index = i + 1; 302 strncpy(res->name, str, sizeof(res->name) - 1); 303 error = nv_get_int16(nvout, "error%u", i); 304 if (error != 0) 305 continue; 306 str = nv_get_string(nvout, "role%u", i); 307 res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF; 308 str = nv_get_string(nvout, "provname%u", i); 309 if (str != NULL) 310 strncpy(res->provname, str, sizeof(res->provname) - 1); 311 str = nv_get_string(nvout, "localpath%u", i); 312 if (str != NULL) { 313 strncpy(res->localpath, str, 314 sizeof(res->localpath) - 1); 315 } 316 res->extentsize = nv_get_uint32(nvout, "extentsize%u", i); 317 res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i); 318 str = nv_get_string(nvout, "remoteaddr%u", i); 319 if (str != NULL) { 320 strncpy(res->remoteaddr, str, 321 sizeof(res->remoteaddr) - 1); 322 } 323 str = nv_get_string(nvout, "sourceaddr%u", i); 324 if (str != NULL) { 325 strncpy(res->sourceaddr, str, 326 sizeof(res->sourceaddr) - 1); 327 } 328 str = nv_get_string(nvout, "replication%u", i); 329 res->replication = str != NULL ? str2replication(str) : -1; 330 str = nv_get_string(nvout, "status%u", i); 331 res->status = str != NULL ? str2status(str) : -1; 332 res->dirty = nv_get_uint64(nvout, "dirty%u", i); 333 res->reads = nv_get_uint64(nvout, "stat_read%u", i); 334 res->writes = nv_get_uint64(nvout, "stat_write%u", i); 335 res->deletes = nv_get_uint64(nvout, "stat_delete%u", i); 336 res->flushes = nv_get_uint64(nvout, "stat_flush%u", i); 337 res->activemap_updates = 338 nv_get_uint64(nvout, "stat_activemap_update%u", i); 339 res->read_errors = 340 nv_get_uint64(nvout, "stat_read_error%u", i); 341 res->write_errors = 342 nv_get_uint64(nvout, "stat_write_error%u", i); 343 res->delete_errors = 344 nv_get_uint64(nvout, "stat_delete_error%u", i); 345 res->flush_errors = 346 nv_get_uint64(nvout, "stat_flush_error%u", i); 347 res->workerpid = nv_get_int32(nvout, "workerpid%u", i); 348 TAILQ_INSERT_TAIL(&resources, res, link); 349 } 350 nv_free(nvout); 351 return (0); 352 } 353 354 int 355 op_hastConfig(struct snmp_context *context, struct snmp_value *value, 356 u_int sub, u_int iidx __unused, enum snmp_op op) 357 { 358 asn_subid_t which; 359 360 which = value->var.subs[sub - 1]; 361 362 switch (op) { 363 case SNMP_OP_GET: 364 switch (which) { 365 case LEAF_hastConfigFile: 366 return (string_get(value, cfgpath, -1)); 367 default: 368 return (SNMP_ERR_RES_UNAVAIL); 369 } 370 case SNMP_OP_SET: 371 switch (which) { 372 case LEAF_hastConfigFile: 373 return (string_save(value, context, -1, 374 (u_char **)&cfgpath)); 375 default: 376 return (SNMP_ERR_RES_UNAVAIL); 377 } 378 case SNMP_OP_GETNEXT: 379 case SNMP_OP_ROLLBACK: 380 case SNMP_OP_COMMIT: 381 return (SNMP_ERR_NOERROR); 382 default: 383 return (SNMP_ERR_RES_UNAVAIL); 384 } 385 } 386 387 int 388 op_hastResourceTable(struct snmp_context *context __unused, 389 struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) 390 { 391 struct hast_snmp_resource *res; 392 asn_subid_t which; 393 int ret; 394 395 if (update_resources() == -1) 396 return (SNMP_ERR_RES_UNAVAIL); 397 398 which = value->var.subs[sub - 1]; 399 400 switch (op) { 401 case SNMP_OP_GETNEXT: 402 res = NEXT_OBJECT_INT(&resources, &value->var, sub); 403 if (res == NULL) 404 return (SNMP_ERR_NOSUCHNAME); 405 value->var.len = sub + 1; 406 value->var.subs[sub] = res->index; 407 break; 408 case SNMP_OP_GET: 409 if (value->var.len - sub != 1) 410 return (SNMP_ERR_NOSUCHNAME); 411 res = FIND_OBJECT_INT(&resources, &value->var, sub); 412 if (res == NULL) 413 return (SNMP_ERR_NOSUCHNAME); 414 break; 415 case SNMP_OP_SET: 416 res = FIND_OBJECT_INT(&resources, &value->var, sub); 417 if (res == NULL) 418 return (SNMP_ERR_NOSUCHNAME); 419 switch (which) { 420 case LEAF_hastResourceRole: 421 ret = set_role(res->name, value->v.integer); 422 /* force update on next run */ 423 last_resources_update = 0; 424 break; 425 default: 426 ret = SNMP_ERR_NOT_WRITEABLE; 427 break; 428 } 429 return ret; 430 case SNMP_OP_ROLLBACK: 431 case SNMP_OP_COMMIT: 432 return (SNMP_ERR_NOERROR); 433 default: 434 return (SNMP_ERR_RES_UNAVAIL); 435 } 436 437 ret = SNMP_ERR_NOERROR; 438 439 switch (which) { 440 case LEAF_hastResourceIndex: 441 value->v.integer = res->index; 442 break; 443 case LEAF_hastResourceName: 444 ret = string_get(value, res->name, -1); 445 break; 446 case LEAF_hastResourceRole: 447 value->v.integer = res->role; 448 break; 449 case LEAF_hastResourceProvName: 450 ret = string_get(value, res->provname, -1); 451 break; 452 case LEAF_hastResourceLocalPath: 453 ret = string_get(value, res->localpath, -1); 454 break; 455 case LEAF_hastResourceExtentSize: 456 value->v.integer = res->extentsize; 457 break; 458 case LEAF_hastResourceKeepDirty: 459 value->v.integer = res->keepdirty; 460 break; 461 case LEAF_hastResourceRemoteAddr: 462 ret = string_get(value, res->remoteaddr, -1); 463 break; 464 case LEAF_hastResourceSourceAddr: 465 ret = string_get(value, res->sourceaddr, -1); 466 break; 467 case LEAF_hastResourceReplication: 468 value->v.integer = res->replication; 469 break; 470 case LEAF_hastResourceStatus: 471 value->v.integer = res->status; 472 break; 473 case LEAF_hastResourceDirty: 474 value->v.counter64 = res->dirty; 475 break; 476 case LEAF_hastResourceReads: 477 value->v.counter64 = res->reads; 478 break; 479 case LEAF_hastResourceWrites: 480 value->v.counter64 = res->writes; 481 break; 482 case LEAF_hastResourceDeletes: 483 value->v.counter64 = res->deletes; 484 break; 485 case LEAF_hastResourceFlushes: 486 value->v.counter64 = res->flushes; 487 break; 488 case LEAF_hastResourceActivemapUpdates: 489 value->v.counter64 = res->activemap_updates; 490 break; 491 case LEAF_hastResourceReadErrors: 492 value->v.counter64 = res->read_errors; 493 break; 494 case LEAF_hastResourceWriteErrors: 495 value->v.counter64 = res->write_errors; 496 break; 497 case LEAF_hastResourceDeleteErrors: 498 value->v.counter64 = res->delete_errors; 499 break; 500 case LEAF_hastResourceFlushErrors: 501 value->v.counter64 = res->flush_errors; 502 break; 503 case LEAF_hastResourceWorkerPid: 504 value->v.integer = res->workerpid; 505 break; 506 default: 507 ret = SNMP_ERR_RES_UNAVAIL; 508 break; 509 } 510 return (ret); 511 } 512