/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Mikolaj Golub * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include "hast.h" #include "hast_oid.h" #include "hast_proto.h" #include "hast_tree.h" #include "nv.h" #include "pjdlog.h" #include "proto.h" #define UPDATE_INTERVAL 500 /* update interval in ticks */ static struct lmodule *module; static const struct asn_oid oid_hast = OIDX_begemotHast; /* the Object Resource registration index */ static u_int hast_index = 0; /* * Structure that describes single resource. */ struct hast_snmp_resource { TAILQ_ENTRY(hast_snmp_resource) link; int32_t index; char name[NAME_MAX]; int error; int role; char provname[NAME_MAX]; char localpath[PATH_MAX]; int32_t extentsize; int32_t keepdirty; char remoteaddr[HAST_ADDRSIZE]; char sourceaddr[HAST_ADDRSIZE]; int replication; int status; uint64_t dirty; uint64_t reads; uint64_t writes; uint64_t deletes; uint64_t flushes; uint64_t activemap_updates; uint64_t read_errors; uint64_t write_errors; uint64_t delete_errors; uint64_t flush_errors; pid_t workerpid; uint32_t local_queue; uint32_t send_queue; uint32_t recv_queue; uint32_t done_queue; uint32_t idle_queue; }; static TAILQ_HEAD(, hast_snmp_resource) resources = TAILQ_HEAD_INITIALIZER(resources); /* Path to configuration file. */ static u_char *cfgpath; /* Ticks of the last hast resources update. */ static uint64_t last_resources_update; static void free_resources(void); static int hastctl(struct nv *nvin, struct nv **nvout); static int hast_fini(void); static int hast_init(struct lmodule *mod, int argc, char *argv[]); static void hast_start(void); static int set_role(const char *resource, int role); static int str2role(const char *str); static int str2replication(const char *str); static int str2status(const char *str); static int update_resources(void); const struct snmp_module config = { .comment = "This module implements the BEGEMOT MIB for HAST.", .init = hast_init, .start = hast_start, .fini = hast_fini, .tree = hast_ctree, .tree_size = hast_CTREE_SIZE, }; static int hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { module = mod; pjdlog_init(PJDLOG_MODE_SYSLOG); pjdlog_debug_set(0); cfgpath = malloc(sizeof(HAST_CONFIG)); if (cfgpath == NULL) { pjdlog_error("Unable to allocate %zu bytes for cfgpath", sizeof(HAST_CONFIG)); return (-1); } strcpy(cfgpath, HAST_CONFIG); return(0); } static void hast_start(void) { hast_index = or_register(&oid_hast, "The MIB module for BEGEMOT-HAST-MIB.", module); } static int hast_fini(void) { or_unregister(hast_index); free_resources(); free(cfgpath); return (0); } static void free_resources(void) { struct hast_snmp_resource *res; while ((res = TAILQ_FIRST(&resources)) != NULL) { TAILQ_REMOVE(&resources, res, link); free(res); } } static int str2role(const char *str) { if (strcmp(str, "init") == 0) return (HAST_ROLE_INIT); if (strcmp(str, "primary") == 0) return (HAST_ROLE_PRIMARY); if (strcmp(str, "secondary") == 0) return (HAST_ROLE_SECONDARY); return (HAST_ROLE_UNDEF); } static int str2replication(const char *str) { if (strcmp(str, "fullsync") == 0) return (HAST_REPLICATION_FULLSYNC); if (strcmp(str, "memsync") == 0) return (HAST_REPLICATION_MEMSYNC); if (strcmp(str, "async") == 0) return (HAST_REPLICATION_ASYNC); return (-1); } static int str2status(const char *str) { if (strcmp(str, "complete") == 0) return (0); if (strcmp(str, "degraded") == 0) return (1); return (-1); } static int hastctl(struct nv *nvin, struct nv **nvout) { struct hastd_config *cfg; struct proto_conn *conn; struct nv *nv; int error; cfg = yy_config_parse(cfgpath, true); if (cfg == NULL) return (-1); /* Setup control connection... */ if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) { pjdlog_error("Unable to setup control connection to %s", cfg->hc_controladdr); return (-1); } /* ...and connect to hastd. */ if (proto_connect(conn, HAST_TIMEOUT) == -1) { pjdlog_error("Unable to connect to hastd via %s", cfg->hc_controladdr); proto_close(conn); return (-1); } /* Send the command to the server... */ if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) { pjdlog_error("Unable to send command to hastd via %s", cfg->hc_controladdr); proto_close(conn); return (-1); } /* ...and receive reply. */ if (hast_proto_recv_hdr(conn, &nv) == -1) { pjdlog_error("cannot receive reply from hastd via %s", cfg->hc_controladdr); proto_close(conn); return (-1); } proto_close(conn); error = nv_get_int16(nv, "error"); if (error != 0) { pjdlog_error("Error %d received from hastd.", error); nv_free(nv); return (-1); } nv_set_error(nv, 0); *nvout = nv; return (0); } static int set_role(const char *resource, int role) { struct nv *nvin, *nvout; int error; nvin = nv_alloc(); nv_add_string(nvin, resource, "resource%d", 0); nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd"); nv_add_uint8(nvin, role, "role"); error = hastctl(nvin, &nvout); nv_free(nvin); if (error != 0) return (-1); nv_free(nvout); return (SNMP_ERR_NOERROR); } static int update_resources(void) { struct hast_snmp_resource *res; struct nv *nvin, *nvout; static uint64_t now; unsigned int i; const char *str; int error; now = get_ticks(); if (now - last_resources_update < UPDATE_INTERVAL) return (0); last_resources_update = now; free_resources(); nvin = nv_alloc(); nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd"); nv_add_string(nvin, "all", "resource%d", 0); error = hastctl(nvin, &nvout); nv_free(nvin); if (error != 0) return (-1); for (i = 0; ; i++) { str = nv_get_string(nvout, "resource%u", i); if (str == NULL) break; res = calloc(1, sizeof(*res)); if (res == NULL) { pjdlog_error("Unable to allocate %zu bytes for " "resource", sizeof(*res)); return (-1); } res->index = i + 1; strncpy(res->name, str, sizeof(res->name) - 1); error = nv_get_int16(nvout, "error%u", i); if (error != 0) continue; str = nv_get_string(nvout, "role%u", i); res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF; str = nv_get_string(nvout, "provname%u", i); if (str != NULL) strncpy(res->provname, str, sizeof(res->provname) - 1); str = nv_get_string(nvout, "localpath%u", i); if (str != NULL) { strncpy(res->localpath, str, sizeof(res->localpath) - 1); } res->extentsize = nv_get_uint32(nvout, "extentsize%u", i); res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i); str = nv_get_string(nvout, "remoteaddr%u", i); if (str != NULL) { strncpy(res->remoteaddr, str, sizeof(res->remoteaddr) - 1); } str = nv_get_string(nvout, "sourceaddr%u", i); if (str != NULL) { strncpy(res->sourceaddr, str, sizeof(res->sourceaddr) - 1); } str = nv_get_string(nvout, "replication%u", i); res->replication = str != NULL ? str2replication(str) : -1; str = nv_get_string(nvout, "status%u", i); res->status = str != NULL ? str2status(str) : -1; res->dirty = nv_get_uint64(nvout, "dirty%u", i); res->reads = nv_get_uint64(nvout, "stat_read%u", i); res->writes = nv_get_uint64(nvout, "stat_write%u", i); res->deletes = nv_get_uint64(nvout, "stat_delete%u", i); res->flushes = nv_get_uint64(nvout, "stat_flush%u", i); res->activemap_updates = nv_get_uint64(nvout, "stat_activemap_update%u", i); res->read_errors = nv_get_uint64(nvout, "stat_read_error%u", i); res->write_errors = nv_get_uint64(nvout, "stat_write_error%u", i); res->delete_errors = nv_get_uint64(nvout, "stat_delete_error%u", i); res->flush_errors = nv_get_uint64(nvout, "stat_flush_error%u", i); res->workerpid = nv_get_int32(nvout, "workerpid%u", i); res->local_queue = nv_get_uint64(nvout, "local_queue_size%u", i); res->send_queue = nv_get_uint64(nvout, "send_queue_size%u", i); res->recv_queue = nv_get_uint64(nvout, "recv_queue_size%u", i); res->done_queue = nv_get_uint64(nvout, "done_queue_size%u", i); res->idle_queue = nv_get_uint64(nvout, "idle_queue_size%u", i); TAILQ_INSERT_TAIL(&resources, res, link); } nv_free(nvout); return (0); } int op_hastConfig(struct snmp_context *context, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which; which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GET: switch (which) { case LEAF_hastConfigFile: return (string_get(value, cfgpath, -1)); default: return (SNMP_ERR_RES_UNAVAIL); } case SNMP_OP_SET: switch (which) { case LEAF_hastConfigFile: return (string_save(value, context, -1, (u_char **)&cfgpath)); default: return (SNMP_ERR_RES_UNAVAIL); } case SNMP_OP_GETNEXT: case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_RES_UNAVAIL); } } int op_hastResourceTable(struct snmp_context *context __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct hast_snmp_resource *res; asn_subid_t which; int ret; if (update_resources() == -1) return (SNMP_ERR_RES_UNAVAIL); which = value->var.subs[sub - 1]; switch (op) { case SNMP_OP_GETNEXT: res = NEXT_OBJECT_INT(&resources, &value->var, sub); if (res == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = res->index; break; case SNMP_OP_GET: if (value->var.len - sub != 1) return (SNMP_ERR_NOSUCHNAME); res = FIND_OBJECT_INT(&resources, &value->var, sub); if (res == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_SET: res = FIND_OBJECT_INT(&resources, &value->var, sub); if (res == NULL) return (SNMP_ERR_NOSUCHNAME); switch (which) { case LEAF_hastResourceRole: ret = set_role(res->name, value->v.integer); /* force update on next run */ last_resources_update = 0; break; default: ret = SNMP_ERR_NOT_WRITEABLE; break; } return ret; case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); default: return (SNMP_ERR_RES_UNAVAIL); } ret = SNMP_ERR_NOERROR; switch (which) { case LEAF_hastResourceIndex: value->v.integer = res->index; break; case LEAF_hastResourceName: ret = string_get(value, res->name, -1); break; case LEAF_hastResourceRole: value->v.integer = res->role; break; case LEAF_hastResourceProvName: ret = string_get(value, res->provname, -1); break; case LEAF_hastResourceLocalPath: ret = string_get(value, res->localpath, -1); break; case LEAF_hastResourceExtentSize: value->v.integer = res->extentsize; break; case LEAF_hastResourceKeepDirty: value->v.integer = res->keepdirty; break; case LEAF_hastResourceRemoteAddr: ret = string_get(value, res->remoteaddr, -1); break; case LEAF_hastResourceSourceAddr: ret = string_get(value, res->sourceaddr, -1); break; case LEAF_hastResourceReplication: value->v.integer = res->replication; break; case LEAF_hastResourceStatus: value->v.integer = res->status; break; case LEAF_hastResourceDirty: value->v.counter64 = res->dirty; break; case LEAF_hastResourceReads: value->v.counter64 = res->reads; break; case LEAF_hastResourceWrites: value->v.counter64 = res->writes; break; case LEAF_hastResourceDeletes: value->v.counter64 = res->deletes; break; case LEAF_hastResourceFlushes: value->v.counter64 = res->flushes; break; case LEAF_hastResourceActivemapUpdates: value->v.counter64 = res->activemap_updates; break; case LEAF_hastResourceReadErrors: value->v.counter64 = res->read_errors; break; case LEAF_hastResourceWriteErrors: value->v.counter64 = res->write_errors; break; case LEAF_hastResourceDeleteErrors: value->v.counter64 = res->delete_errors; break; case LEAF_hastResourceFlushErrors: value->v.counter64 = res->flush_errors; break; case LEAF_hastResourceWorkerPid: value->v.integer = res->workerpid; break; case LEAF_hastResourceLocalQueue: value->v.uint32 = res->local_queue; break; case LEAF_hastResourceSendQueue: value->v.uint32 = res->send_queue; break; case LEAF_hastResourceRecvQueue: value->v.uint32 = res->recv_queue; break; case LEAF_hastResourceDoneQueue: value->v.uint32 = res->done_queue; break; case LEAF_hastResourceIdleQueue: value->v.uint32 = res->idle_queue; break; default: ret = SNMP_ERR_RES_UNAVAIL; break; } return (ret); }