1 /* $OpenBSD: stat_ramstat.c,v 1.11 2018/05/31 21:06:12 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/queue.h> 22 #include <sys/tree.h> 23 24 #include <event.h> 25 #include <imsg.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <limits.h> 30 31 #include "smtpd.h" 32 #include "log.h" 33 34 35 static void ramstat_init(void); 36 static void ramstat_close(void); 37 static void ramstat_increment(const char *, size_t); 38 static void ramstat_decrement(const char *, size_t); 39 static void ramstat_set(const char *, const struct stat_value *); 40 static int ramstat_iter(void **, char **, struct stat_value *); 41 42 struct ramstat_entry { 43 RB_ENTRY(ramstat_entry) entry; 44 char key[STAT_KEY_SIZE]; 45 struct stat_value value; 46 }; 47 RB_HEAD(stats_tree, ramstat_entry) stats; 48 RB_PROTOTYPE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp); 49 50 struct stat_backend stat_backend_ramstat = { 51 ramstat_init, 52 ramstat_close, 53 ramstat_increment, 54 ramstat_decrement, 55 ramstat_set, 56 ramstat_iter 57 }; 58 59 static void 60 ramstat_init(void) 61 { 62 log_trace(TRACE_STAT, "ramstat: init"); 63 64 RB_INIT(&stats); 65 66 /* ramstat_set() should be called for each key we want 67 * to have displayed by smtpctl show stats at startup. 68 */ 69 ramstat_set("uptime", stat_timestamp(env->sc_uptime)); 70 } 71 72 static void 73 ramstat_close(void) 74 { 75 log_trace(TRACE_STAT, "ramstat: close"); 76 } 77 78 static void 79 ramstat_increment(const char *name, size_t val) 80 { 81 struct ramstat_entry *np, lk; 82 83 log_trace(TRACE_STAT, "ramstat: increment: %s", name); 84 (void)strlcpy(lk.key, name, sizeof (lk.key)); 85 np = RB_FIND(stats_tree, &stats, &lk); 86 if (np == NULL) { 87 np = xcalloc(1, sizeof *np); 88 (void)strlcpy(np->key, name, sizeof (np->key)); 89 RB_INSERT(stats_tree, &stats, np); 90 } 91 log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd", 92 name, name, np->value.u.counter, np->value.u.counter + val); 93 np->value.u.counter += val; 94 } 95 96 static void 97 ramstat_decrement(const char *name, size_t val) 98 { 99 struct ramstat_entry *np, lk; 100 101 log_trace(TRACE_STAT, "ramstat: decrement: %s", name); 102 (void)strlcpy(lk.key, name, sizeof (lk.key)); 103 np = RB_FIND(stats_tree, &stats, &lk); 104 if (np == NULL) { 105 np = xcalloc(1, sizeof *np); 106 (void)strlcpy(np->key, name, sizeof (np->key)); 107 RB_INSERT(stats_tree, &stats, np); 108 } 109 log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd", 110 name, name, np->value.u.counter, np->value.u.counter - val); 111 np->value.u.counter -= val; 112 } 113 114 static void 115 ramstat_set(const char *name, const struct stat_value *val) 116 { 117 struct ramstat_entry *np, lk; 118 119 log_trace(TRACE_STAT, "ramstat: set: %s", name); 120 (void)strlcpy(lk.key, name, sizeof (lk.key)); 121 np = RB_FIND(stats_tree, &stats, &lk); 122 if (np == NULL) { 123 np = xcalloc(1, sizeof *np); 124 (void)strlcpy(np->key, name, sizeof (np->key)); 125 RB_INSERT(stats_tree, &stats, np); 126 } 127 log_trace(TRACE_STAT, "ramstat: %s: n/a -> n/a", name); 128 np->value = *val; 129 } 130 131 static int 132 ramstat_iter(void **iter, char **name, struct stat_value *val) 133 { 134 struct ramstat_entry *np; 135 136 log_trace(TRACE_STAT, "ramstat: iter"); 137 if (RB_EMPTY(&stats)) 138 return 0; 139 140 if (*iter == NULL) 141 np = RB_MIN(stats_tree, &stats); 142 else 143 np = RB_NEXT(stats_tree, &stats, *iter); 144 145 *iter = np; 146 if (np == NULL) 147 return 0; 148 149 *name = np->key; 150 *val = np->value; 151 return 1; 152 } 153 154 155 static int 156 ramstat_entry_cmp(struct ramstat_entry *e1, struct ramstat_entry *e2) 157 { 158 return strcmp(e1->key, e2->key); 159 } 160 161 RB_GENERATE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp); 162