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