1250379Strociny/*- 2250379Strociny * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 3250379Strociny * All rights reserved. 4250379Strociny * 5250379Strociny * Redistribution and use in source and binary forms, with or without 6250379Strociny * modification, are permitted provided that the following conditions 7250379Strociny * are met: 8250379Strociny * 1. Redistributions of source code must retain the above copyright 9250379Strociny * notice, this list of conditions and the following disclaimer. 10250379Strociny * 2. Redistributions in binary form must reproduce the above copyright 11250379Strociny * notice, this list of conditions and the following disclaimer in the 12250379Strociny * documentation and/or other materials provided with the distribution. 13250379Strociny * 14250379Strociny * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15250379Strociny * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16250379Strociny * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17250379Strociny * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18250379Strociny * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19250379Strociny * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20250379Strociny * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21250379Strociny * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22250379Strociny * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23250379Strociny * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24250379Strociny * SUCH DAMAGE. 25250379Strociny */ 26250379Strociny 27250379Strociny#include <sys/cdefs.h> 28250379Strociny__FBSDID("$FreeBSD$"); 29250379Strociny 30250379Strociny#include <sys/param.h> 31250379Strociny#include <sys/queue.h> 32250379Strociny 33250379Strociny#include <bsnmp/snmpmod.h> 34250379Strociny 35250379Strociny#include <string.h> 36250379Strociny 37250379Strociny#include "hast.h" 38250379Strociny#include "hast_oid.h" 39250379Strociny#include "hast_proto.h" 40250379Strociny#include "hast_tree.h" 41250379Strociny#include "nv.h" 42250379Strociny#include "pjdlog.h" 43250379Strociny#include "proto.h" 44250379Strociny 45250379Strociny#define UPDATE_INTERVAL 500 /* update interval in ticks */ 46250379Strociny 47250379Strocinystatic struct lmodule *module; 48250379Strociny 49250379Strocinystatic const struct asn_oid oid_hast = OIDX_begemotHast; 50250379Strociny 51250379Strociny/* the Object Resource registration index */ 52250379Strocinystatic u_int hast_index = 0; 53250379Strociny 54250379Strociny/* 55250379Strociny * Structure that describes single resource. 56250379Strociny */ 57250379Strocinystruct hast_snmp_resource { 58250379Strociny TAILQ_ENTRY(hast_snmp_resource) link; 59250379Strociny int32_t index; 60250379Strociny char name[NAME_MAX]; 61250379Strociny int error; 62250379Strociny int role; 63250379Strociny char provname[NAME_MAX]; 64250379Strociny char localpath[PATH_MAX]; 65250379Strociny int32_t extentsize; 66250379Strociny int32_t keepdirty; 67250379Strociny char remoteaddr[HAST_ADDRSIZE]; 68250379Strociny char sourceaddr[HAST_ADDRSIZE]; 69250379Strociny int replication; 70250379Strociny int status; 71250379Strociny uint64_t dirty; 72250379Strociny uint64_t reads; 73250379Strociny uint64_t writes; 74250379Strociny uint64_t deletes; 75250379Strociny uint64_t flushes; 76250379Strociny uint64_t activemap_updates; 77250379Strociny uint64_t read_errors; 78250379Strociny uint64_t write_errors; 79250379Strociny uint64_t delete_errors; 80250379Strociny uint64_t flush_errors; 81252516Strociny pid_t workerpid; 82250379Strociny}; 83250379Strociny 84250379Strocinystatic TAILQ_HEAD(, hast_snmp_resource) resources = 85250379Strociny TAILQ_HEAD_INITIALIZER(resources); 86250379Strociny 87250379Strociny/* Path to configuration file. */ 88250379Strocinystatic u_char *cfgpath; 89250379Strociny/* Ticks of the last hast resources update. */ 90250379Strocinystatic uint64_t last_resources_update; 91250379Strociny 92250379Strocinystatic void free_resources(void); 93250379Strocinystatic int hastctl(struct nv *nvin, struct nv **nvout); 94250379Strocinystatic int hast_fini(void); 95250379Strocinystatic int hast_init(struct lmodule *mod, int argc, char *argv[]); 96250379Strocinystatic void hast_start(void); 97250379Strocinystatic int set_role(const char *resource, int role); 98250379Strocinystatic int str2role(const char *str); 99250379Strocinystatic int str2replication(const char *str); 100250379Strocinystatic int str2status(const char *str); 101250379Strocinystatic int update_resources(void); 102250379Strociny 103250379Strocinyconst struct snmp_module config = { 104250379Strociny .comment = "This module implements the BEGEMOT MIB for HAST.", 105250379Strociny .init = hast_init, 106250379Strociny .start = hast_start, 107250379Strociny .fini = hast_fini, 108250379Strociny .tree = hast_ctree, 109250379Strociny .tree_size = hast_CTREE_SIZE, 110250379Strociny}; 111250379Strociny 112250379Strocinystatic int 113250379Strocinyhast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) 114250379Strociny{ 115250379Strociny 116250379Strociny module = mod; 117250379Strociny 118250379Strociny pjdlog_init(PJDLOG_MODE_SYSLOG); 119250379Strociny pjdlog_debug_set(0); 120250379Strociny 121250379Strociny cfgpath = malloc(sizeof(HAST_CONFIG)); 122250379Strociny if (cfgpath == NULL) { 123250379Strociny pjdlog_error("Unable to allocate %zu bytes for cfgpath", 124250379Strociny sizeof(HAST_CONFIG)); 125250379Strociny return (-1); 126250379Strociny } 127250379Strociny strcpy(cfgpath, HAST_CONFIG); 128250379Strociny return(0); 129250379Strociny} 130250379Strociny 131250379Strocinystatic void 132250379Strocinyhast_start(void) 133250379Strociny{ 134250379Strociny hast_index = or_register(&oid_hast, 135250379Strociny "The MIB module for BEGEMOT-HAST-MIB.", module); 136250379Strociny} 137250379Strociny 138250379Strocinystatic int 139250379Strocinyhast_fini(void) 140250379Strociny{ 141250379Strociny 142250379Strociny or_unregister(hast_index); 143250379Strociny free_resources(); 144250379Strociny free(cfgpath); 145250379Strociny return (0); 146250379Strociny} 147250379Strociny 148250379Strocinystatic void 149250379Strocinyfree_resources(void) 150250379Strociny{ 151250379Strociny struct hast_snmp_resource *res; 152250379Strociny 153250379Strociny while ((res = TAILQ_FIRST(&resources)) != NULL) { 154250379Strociny TAILQ_REMOVE(&resources, res, link); 155250379Strociny free(res); 156250379Strociny } 157250379Strociny} 158250379Strociny 159250379Strocinystatic int 160250379Strocinystr2role(const char *str) 161250379Strociny{ 162250379Strociny 163250379Strociny if (strcmp(str, "init") == 0) 164250379Strociny return (HAST_ROLE_INIT); 165250379Strociny if (strcmp(str, "primary") == 0) 166250379Strociny return (HAST_ROLE_PRIMARY); 167250379Strociny if (strcmp(str, "secondary") == 0) 168250379Strociny return (HAST_ROLE_SECONDARY); 169250379Strociny return (HAST_ROLE_UNDEF); 170250379Strociny} 171250379Strociny 172250379Strocinystatic int 173250379Strocinystr2replication(const char *str) 174250379Strociny{ 175250379Strociny 176250379Strociny if (strcmp(str, "fullsync") == 0) 177250379Strociny return (HAST_REPLICATION_FULLSYNC); 178250379Strociny if (strcmp(str, "memsync") == 0) 179250379Strociny return (HAST_REPLICATION_MEMSYNC); 180250379Strociny if (strcmp(str, "async") == 0) 181250379Strociny return (HAST_REPLICATION_ASYNC); 182250379Strociny return (-1); 183250379Strociny} 184250379Strociny 185250379Strocinystatic int 186250379Strocinystr2status(const char *str) 187250379Strociny{ 188250379Strociny 189250379Strociny if (strcmp(str, "complete") == 0) 190250379Strociny return (0); 191250379Strociny if (strcmp(str, "degraded") == 0) 192250379Strociny return (1); 193250379Strociny return (-1); 194250379Strociny} 195250379Strociny 196250379Strocinystatic int 197250379Strocinyhastctl(struct nv *nvin, struct nv **nvout) 198250379Strociny{ 199250379Strociny struct hastd_config *cfg; 200250379Strociny struct proto_conn *conn; 201250379Strociny struct nv *nv; 202250379Strociny int error; 203250379Strociny 204250379Strociny cfg = yy_config_parse(cfgpath, true); 205250379Strociny if (cfg == NULL) 206250379Strociny return (-1); 207250379Strociny 208250379Strociny /* Setup control connection... */ 209250379Strociny if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) { 210250379Strociny pjdlog_error("Unable to setup control connection to %s", 211250379Strociny cfg->hc_controladdr); 212250379Strociny return (-1); 213250379Strociny } 214250379Strociny /* ...and connect to hastd. */ 215250379Strociny if (proto_connect(conn, HAST_TIMEOUT) == -1) { 216250379Strociny pjdlog_error("Unable to connect to hastd via %s", 217250379Strociny cfg->hc_controladdr); 218250379Strociny proto_close(conn); 219250379Strociny return (-1); 220250379Strociny } 221250379Strociny /* Send the command to the server... */ 222250379Strociny if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) { 223250379Strociny pjdlog_error("Unable to send command to hastd via %s", 224250379Strociny cfg->hc_controladdr); 225250379Strociny proto_close(conn); 226250379Strociny return (-1); 227250379Strociny } 228250379Strociny /* ...and receive reply. */ 229250379Strociny if (hast_proto_recv_hdr(conn, &nv) == -1) { 230250379Strociny pjdlog_error("cannot receive reply from hastd via %s", 231250379Strociny cfg->hc_controladdr); 232250379Strociny proto_close(conn); 233250379Strociny return (-1); 234250379Strociny } 235250379Strociny proto_close(conn); 236250379Strociny error = nv_get_int16(nv, "error"); 237250379Strociny if (error != 0) { 238250379Strociny pjdlog_error("Error %d received from hastd.", error); 239250379Strociny nv_free(nv); 240250379Strociny return (-1); 241250379Strociny } 242250379Strociny nv_set_error(nv, 0); 243250379Strociny *nvout = nv; 244250379Strociny return (0); 245250379Strociny} 246250379Strociny 247250379Strocinystatic int 248250379Strocinyset_role(const char *resource, int role) 249250379Strociny{ 250250379Strociny struct nv *nvin, *nvout; 251250379Strociny int error; 252250379Strociny 253250379Strociny nvin = nv_alloc(); 254250379Strociny nv_add_string(nvin, resource, "resource%d", 0); 255250379Strociny nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd"); 256250379Strociny nv_add_uint8(nvin, role, "role"); 257250379Strociny error = hastctl(nvin, &nvout); 258250379Strociny nv_free(nvin); 259250379Strociny if (error != 0) 260250379Strociny return (-1); 261250379Strociny nv_free(nvout); 262250379Strociny return (SNMP_ERR_NOERROR); 263250379Strociny} 264250379Strociny 265250379Strocinystatic int 266250379Strocinyupdate_resources(void) 267250379Strociny{ 268250379Strociny struct hast_snmp_resource *res; 269250379Strociny struct nv *nvin, *nvout; 270250379Strociny static uint64_t now; 271250379Strociny unsigned int i; 272250379Strociny const char *str; 273250379Strociny int error; 274250379Strociny 275250379Strociny now = get_ticks(); 276250379Strociny if (now - last_resources_update < UPDATE_INTERVAL) 277250379Strociny return (0); 278250379Strociny 279250379Strociny last_resources_update = now; 280250379Strociny 281250379Strociny free_resources(); 282250379Strociny 283250379Strociny nvin = nv_alloc(); 284250379Strociny nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd"); 285250379Strociny nv_add_string(nvin, "all", "resource%d", 0); 286250379Strociny error = hastctl(nvin, &nvout); 287250379Strociny nv_free(nvin); 288250379Strociny if (error != 0) 289250379Strociny return (-1); 290250379Strociny 291250379Strociny for (i = 0; ; i++) { 292250379Strociny str = nv_get_string(nvout, "resource%u", i); 293250379Strociny if (str == NULL) 294250379Strociny break; 295250379Strociny res = calloc(1, sizeof(*res)); 296250379Strociny if (res == NULL) { 297250379Strociny pjdlog_error("Unable to allocate %zu bytes for " 298250379Strociny "resource", sizeof(*res)); 299250379Strociny return (-1); 300250379Strociny } 301250379Strociny res->index = i + 1; 302250379Strociny strncpy(res->name, str, sizeof(res->name) - 1); 303250379Strociny error = nv_get_int16(nvout, "error%u", i); 304250379Strociny if (error != 0) 305250379Strociny continue; 306250379Strociny str = nv_get_string(nvout, "role%u", i); 307250379Strociny res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF; 308250379Strociny str = nv_get_string(nvout, "provname%u", i); 309250379Strociny if (str != NULL) 310250379Strociny strncpy(res->provname, str, sizeof(res->provname) - 1); 311250379Strociny str = nv_get_string(nvout, "localpath%u", i); 312250379Strociny if (str != NULL) { 313250379Strociny strncpy(res->localpath, str, 314250379Strociny sizeof(res->localpath) - 1); 315250379Strociny } 316250379Strociny res->extentsize = nv_get_uint32(nvout, "extentsize%u", i); 317250379Strociny res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i); 318250379Strociny str = nv_get_string(nvout, "remoteaddr%u", i); 319250379Strociny if (str != NULL) { 320250379Strociny strncpy(res->remoteaddr, str, 321250379Strociny sizeof(res->remoteaddr) - 1); 322250379Strociny } 323250379Strociny str = nv_get_string(nvout, "sourceaddr%u", i); 324250379Strociny if (str != NULL) { 325250379Strociny strncpy(res->sourceaddr, str, 326250379Strociny sizeof(res->sourceaddr) - 1); 327250379Strociny } 328250379Strociny str = nv_get_string(nvout, "replication%u", i); 329250379Strociny res->replication = str != NULL ? str2replication(str) : -1; 330250379Strociny str = nv_get_string(nvout, "status%u", i); 331250379Strociny res->status = str != NULL ? str2status(str) : -1; 332250379Strociny res->dirty = nv_get_uint64(nvout, "dirty%u", i); 333250379Strociny res->reads = nv_get_uint64(nvout, "stat_read%u", i); 334250379Strociny res->writes = nv_get_uint64(nvout, "stat_write%u", i); 335250379Strociny res->deletes = nv_get_uint64(nvout, "stat_delete%u", i); 336250379Strociny res->flushes = nv_get_uint64(nvout, "stat_flush%u", i); 337250379Strociny res->activemap_updates = 338250379Strociny nv_get_uint64(nvout, "stat_activemap_update%u", i); 339250379Strociny res->read_errors = 340250379Strociny nv_get_uint64(nvout, "stat_read_error%u", i); 341250379Strociny res->write_errors = 342250379Strociny nv_get_uint64(nvout, "stat_write_error%u", i); 343250379Strociny res->delete_errors = 344250379Strociny nv_get_uint64(nvout, "stat_delete_error%u", i); 345250379Strociny res->flush_errors = 346250379Strociny nv_get_uint64(nvout, "stat_flush_error%u", i); 347252516Strociny res->workerpid = nv_get_int32(nvout, "workerpid%u", i); 348250379Strociny TAILQ_INSERT_TAIL(&resources, res, link); 349250379Strociny } 350250379Strociny nv_free(nvout); 351250379Strociny return (0); 352250379Strociny} 353250379Strociny 354250379Strocinyint 355250379Strocinyop_hastConfig(struct snmp_context *context, struct snmp_value *value, 356250379Strociny u_int sub, u_int iidx __unused, enum snmp_op op) 357250379Strociny{ 358250379Strociny asn_subid_t which; 359250379Strociny 360250379Strociny which = value->var.subs[sub - 1]; 361250379Strociny 362250379Strociny switch (op) { 363250379Strociny case SNMP_OP_GET: 364250379Strociny switch (which) { 365250379Strociny case LEAF_hastConfigFile: 366250379Strociny return (string_get(value, cfgpath, -1)); 367250379Strociny default: 368250379Strociny return (SNMP_ERR_RES_UNAVAIL); 369250379Strociny } 370250379Strociny case SNMP_OP_SET: 371250379Strociny switch (which) { 372250379Strociny case LEAF_hastConfigFile: 373250379Strociny return (string_save(value, context, -1, 374250379Strociny (u_char **)&cfgpath)); 375250379Strociny default: 376250379Strociny return (SNMP_ERR_RES_UNAVAIL); 377250379Strociny } 378250379Strociny case SNMP_OP_GETNEXT: 379250379Strociny case SNMP_OP_ROLLBACK: 380250379Strociny case SNMP_OP_COMMIT: 381250379Strociny return (SNMP_ERR_NOERROR); 382250379Strociny default: 383250379Strociny return (SNMP_ERR_RES_UNAVAIL); 384250379Strociny } 385250379Strociny} 386250379Strociny 387250379Strocinyint 388250379Strocinyop_hastResourceTable(struct snmp_context *context __unused, 389250379Strociny struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) 390250379Strociny{ 391250379Strociny struct hast_snmp_resource *res; 392250379Strociny asn_subid_t which; 393250379Strociny int ret; 394250379Strociny 395250379Strociny if (update_resources() == -1) 396250379Strociny return (SNMP_ERR_RES_UNAVAIL); 397250379Strociny 398250379Strociny which = value->var.subs[sub - 1]; 399250379Strociny 400250379Strociny switch (op) { 401250379Strociny case SNMP_OP_GETNEXT: 402250379Strociny res = NEXT_OBJECT_INT(&resources, &value->var, sub); 403250379Strociny if (res == NULL) 404250379Strociny return (SNMP_ERR_NOSUCHNAME); 405250379Strociny value->var.len = sub + 1; 406250379Strociny value->var.subs[sub] = res->index; 407250379Strociny break; 408250379Strociny case SNMP_OP_GET: 409250379Strociny if (value->var.len - sub != 1) 410250379Strociny return (SNMP_ERR_NOSUCHNAME); 411250379Strociny res = FIND_OBJECT_INT(&resources, &value->var, sub); 412250379Strociny if (res == NULL) 413250379Strociny return (SNMP_ERR_NOSUCHNAME); 414250379Strociny break; 415250379Strociny case SNMP_OP_SET: 416250379Strociny res = FIND_OBJECT_INT(&resources, &value->var, sub); 417250379Strociny if (res == NULL) 418250379Strociny return (SNMP_ERR_NOSUCHNAME); 419250379Strociny switch (which) { 420250379Strociny case LEAF_hastResourceRole: 421250379Strociny ret = set_role(res->name, value->v.integer); 422250379Strociny /* force update on next run */ 423250379Strociny last_resources_update = 0; 424250379Strociny break; 425250379Strociny default: 426250379Strociny ret = SNMP_ERR_NOT_WRITEABLE; 427250379Strociny break; 428250379Strociny } 429250379Strociny return ret; 430250379Strociny case SNMP_OP_ROLLBACK: 431250379Strociny case SNMP_OP_COMMIT: 432250379Strociny return (SNMP_ERR_NOERROR); 433250379Strociny default: 434250379Strociny return (SNMP_ERR_RES_UNAVAIL); 435250379Strociny } 436250379Strociny 437250379Strociny ret = SNMP_ERR_NOERROR; 438250379Strociny 439250379Strociny switch (which) { 440250379Strociny case LEAF_hastResourceIndex: 441250379Strociny value->v.integer = res->index; 442250379Strociny break; 443250379Strociny case LEAF_hastResourceName: 444250379Strociny ret = string_get(value, res->name, -1); 445250379Strociny break; 446250379Strociny case LEAF_hastResourceRole: 447250379Strociny value->v.integer = res->role; 448250379Strociny break; 449250379Strociny case LEAF_hastResourceProvName: 450250379Strociny ret = string_get(value, res->provname, -1); 451250379Strociny break; 452250379Strociny case LEAF_hastResourceLocalPath: 453250379Strociny ret = string_get(value, res->localpath, -1); 454250379Strociny break; 455250379Strociny case LEAF_hastResourceExtentSize: 456250379Strociny value->v.integer = res->extentsize; 457250379Strociny break; 458250379Strociny case LEAF_hastResourceKeepDirty: 459250379Strociny value->v.integer = res->keepdirty; 460250379Strociny break; 461250379Strociny case LEAF_hastResourceRemoteAddr: 462250379Strociny ret = string_get(value, res->remoteaddr, -1); 463250379Strociny break; 464250379Strociny case LEAF_hastResourceSourceAddr: 465250379Strociny ret = string_get(value, res->sourceaddr, -1); 466250379Strociny break; 467250379Strociny case LEAF_hastResourceReplication: 468250379Strociny value->v.integer = res->replication; 469250379Strociny break; 470250379Strociny case LEAF_hastResourceStatus: 471250379Strociny value->v.integer = res->status; 472250379Strociny break; 473250379Strociny case LEAF_hastResourceDirty: 474250379Strociny value->v.counter64 = res->dirty; 475250379Strociny break; 476250379Strociny case LEAF_hastResourceReads: 477250379Strociny value->v.counter64 = res->reads; 478250379Strociny break; 479250379Strociny case LEAF_hastResourceWrites: 480250379Strociny value->v.counter64 = res->writes; 481250379Strociny break; 482250379Strociny case LEAF_hastResourceDeletes: 483250379Strociny value->v.counter64 = res->deletes; 484250379Strociny break; 485250379Strociny case LEAF_hastResourceFlushes: 486250379Strociny value->v.counter64 = res->flushes; 487250379Strociny break; 488250379Strociny case LEAF_hastResourceActivemapUpdates: 489250379Strociny value->v.counter64 = res->activemap_updates; 490250379Strociny break; 491250379Strociny case LEAF_hastResourceReadErrors: 492250379Strociny value->v.counter64 = res->read_errors; 493250379Strociny break; 494250379Strociny case LEAF_hastResourceWriteErrors: 495250379Strociny value->v.counter64 = res->write_errors; 496250379Strociny break; 497250379Strociny case LEAF_hastResourceDeleteErrors: 498250379Strociny value->v.counter64 = res->delete_errors; 499250379Strociny break; 500250379Strociny case LEAF_hastResourceFlushErrors: 501250379Strociny value->v.counter64 = res->flush_errors; 502250379Strociny break; 503252516Strociny case LEAF_hastResourceWorkerPid: 504252516Strociny value->v.integer = res->workerpid; 505252516Strociny break; 506250379Strociny default: 507250379Strociny ret = SNMP_ERR_RES_UNAVAIL; 508250379Strociny break; 509250379Strociny } 510250379Strociny return (ret); 511250379Strociny} 512