module for bsnmp to interface to sysctl
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

732 lines
17 KiB

  1. /*
  2. * Copyright (c) 2006, Stefan Walter
  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. *
  9. * * Redistributions of source code must retain the above
  10. * copyright notice, this list of conditions and the
  11. * following disclaimer.
  12. * * Redistributions in binary form must reproduce the
  13. * above copyright notice, this list of conditions and
  14. * the following disclaimer in the documentation and/or
  15. * other materials provided with the distribution.
  16. * * The names of contributors to this software may not be
  17. * used to endorse or promote products derived from this
  18. * software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  31. * DAMAGE.
  32. *
  33. *
  34. * CONTRIBUTORS
  35. * Stef Walter <stef@memberwebs.com>
  36. */
  37. #include "usuals.h"
  38. #include <sys/types.h>
  39. #include <sys/sysctl.h>
  40. #include <sys/socket.h>
  41. #include <sys/queue.h>
  42. #include <sys/select.h>
  43. #include <sys/stat.h>
  44. #include <sys/time.h>
  45. #include <sys/un.h>
  46. #include <syslog.h>
  47. #include <unistd.h>
  48. #include <stdarg.h>
  49. #include <ctype.h>
  50. #include <fcntl.h>
  51. #include <bsnmp/snmpmod.h>
  52. #include "sysctl_sup.h"
  53. #include "sysctl_tree.h"
  54. #include "sysctl_oid.h"
  55. #define DEFAULT_CONFIG "/usr/local/etc" "/bsnmp-sysctl.conf"
  56. #define DEFAULT_CACHE 10
  57. /* our module handle */
  58. static struct lmodule *module;
  59. /* OIDs */
  60. static const struct asn_oid oid_sysctl = OIDX_sysctl;
  61. /* the Object Resource registration index */
  62. static u_int reg_index = 0;
  63. struct data_entry {
  64. uint32_t index;
  65. TAILQ_ENTRY(data_entry) link;
  66. char *descr;
  67. char *fmt;
  68. u_int type;
  69. uint64_t cache;
  70. int error;
  71. char *sysctl;
  72. int *name;
  73. u_int namelen;
  74. uint64_t last_update;
  75. int64_t value_int;
  76. char *value_raw;
  77. size_t value_rawlen;
  78. char *value_str;
  79. size_t value_strlen;
  80. };
  81. TAILQ_HEAD(data_entry_list, data_entry);
  82. /* list of sysctls */
  83. static struct data_entry_list entries = TAILQ_HEAD_INITIALIZER(entries);
  84. static uint32_t entry_count = 0;
  85. /* configuration */
  86. static u_char *sysctl_config = NULL;
  87. static char *config_memory = NULL;
  88. static uint64_t option_cache = DEFAULT_CACHE;
  89. /* -----------------------------------------------------------------------------
  90. * HELPERS
  91. */
  92. static void
  93. emsg(const char *format, ...)
  94. {
  95. va_list va;
  96. va_start(va, format);
  97. vsyslog(LOG_ERR, format, va);
  98. va_end(va);
  99. }
  100. static void
  101. strcln (char* data, char ch)
  102. {
  103. char* p;
  104. for (p = data; *data; data++, p++) {
  105. while (*data == ch)
  106. data++;
  107. *p = *data;
  108. }
  109. /* null terminate */
  110. *p = 0;
  111. }
  112. static void
  113. stretrim (char* data)
  114. {
  115. char* t = data + strlen (data);
  116. while (t > data && isspace (*(t - 1))) {
  117. t--;
  118. *t = 0;
  119. }
  120. }
  121. static char*
  122. strbtrim (char* data)
  123. {
  124. while (*data && isspace (*data))
  125. ++data;
  126. return (char*)data;
  127. }
  128. static char*
  129. strtrim (char* data)
  130. {
  131. data = (char*)strbtrim (data);
  132. stretrim (data);
  133. return data;
  134. }
  135. static uint64_t
  136. getcurrticks (void)
  137. {
  138. return get_ticks ();
  139. }
  140. /* -----------------------------------------------------------------------------
  141. * CONFIG PARSING
  142. */
  143. static void
  144. config_free (struct data_entry *data)
  145. {
  146. free(data->name);
  147. free(data->fmt);
  148. free (data->value_str);
  149. free (data->value_raw);
  150. free (data);
  151. }
  152. static void
  153. config_free_all (void)
  154. {
  155. struct data_entry *data;
  156. while ((data = TAILQ_FIRST(&entries)) != NULL) {
  157. TAILQ_REMOVE (&entries, data, link);
  158. config_free (data);
  159. }
  160. }
  161. static int
  162. config_entry (struct data_entry *data, char *name, char *value,
  163. int line)
  164. {
  165. ASSERT (data);
  166. ASSERT (name);
  167. ASSERT (value);
  168. (void)value;
  169. /* The name */
  170. data->descr = name; /* XXX - from sysctl desc */
  171. data->sysctl = name;
  172. data->error = 0;
  173. /* Parse the sysctl */
  174. data->namelen = CTL_MAXNAME;
  175. data->name = malloc(sizeof *data->name * data->namelen);
  176. data->fmt = NULL;
  177. data->value_str = NULL;
  178. data->value_raw = NULL;
  179. if (data->name == NULL) {
  180. emsg ("out of memory parsing config file");
  181. return -1;
  182. }
  183. do {
  184. if (sysctlnametomib(name, data->name, &data->namelen) == -1) {
  185. emsg ("[line %d] sysctlnametomib: %s", line, strerror(errno));
  186. break;
  187. }
  188. data->name = realloc(data->name, sizeof *data->name * data->namelen);
  189. if (sysctl_oidfmt(data->name, data->namelen, &data->fmt, &data->type)) {
  190. emsg ("[line %d] sysctl_oidfmt: %s", line, strerror(errno));
  191. break;
  192. }
  193. /* Options */
  194. data->cache = option_cache;
  195. return 0;
  196. } while (0);
  197. free(data->name);
  198. free(data->fmt);
  199. return -1;
  200. }
  201. static int
  202. config_line (char *name, char *value, int line)
  203. {
  204. struct data_entry *data;
  205. int r;
  206. /* config_parse trims this for us */
  207. ASSERT (!isspace(value[0]));
  208. /* Now populate an entry */
  209. data = (struct data_entry*)calloc(1, sizeof *data);
  210. if (!data) {
  211. emsg ("out of memory");
  212. return -1;
  213. }
  214. /* Now make an entry out of it all */
  215. r = config_entry (data, name, value, line);
  216. if (r < 0) {
  217. free (data);
  218. return r;
  219. }
  220. /* Put it in our table */
  221. data->index = entry_count++;
  222. INSERT_OBJECT_INT (data, &entries);
  223. return 0;
  224. }
  225. static void
  226. config_var (char *name, char *value, int line)
  227. {
  228. char *t2;
  229. int i;
  230. /* The cache variable */
  231. if (strcmp (name, "cache") == 0) {
  232. i = strtol (value, &t2, 10);
  233. if (i <= 0 || *t2)
  234. emsg("[line %d] invalid value for '%s' variable. ignoring: %s",
  235. line, name, value);
  236. else
  237. option_cache = i * 100;
  238. return;
  239. }
  240. emsg ("unknown or invalid variable. ignoring: %s", name);
  241. }
  242. static int
  243. config_read (void)
  244. {
  245. FILE *f = NULL;
  246. long len;
  247. ASSERT (sysctl_config && sysctl_config[0]);
  248. f = fopen (sysctl_config, "r");
  249. if (f == NULL) {
  250. emsg ("couldn't open config file: %s", sysctl_config);
  251. return -1;
  252. }
  253. /* Figure out size */
  254. if (fseek (f, 0, SEEK_END) == -1 || (len = ftell (f)) == -1 || fseek (f, 0, SEEK_SET) == -1) {
  255. emsg ("couldn't seek config file: %s", sysctl_config);
  256. return -1;
  257. }
  258. ASSERT (!config_memory);
  259. if ((config_memory = (char*)malloc (len + 2)) == NULL) {
  260. emsg ("out of memory");
  261. return -1;
  262. }
  263. /* And read in one block */
  264. if(fread(config_memory, 1, len, f) != (size_t)len) {
  265. emsg ("couldn't read config file: %s", sysctl_config);
  266. free (config_memory);
  267. config_memory = NULL;
  268. return -1;
  269. }
  270. fclose (f);
  271. /* Null terminate the data */
  272. config_memory[len] = '\n';
  273. config_memory[len + 1] = 0;
  274. return 0;
  275. }
  276. static int
  277. config_parse (void)
  278. {
  279. char* next;
  280. char* p;
  281. char* t;
  282. int line = 0;
  283. int var = 0;
  284. int r;
  285. config_free_all ();
  286. /* Reads raw config file into config_memory */
  287. if (config_read () < 0)
  288. return -1;
  289. ASSERT (config_memory);
  290. /* Remove nasty dos line endings */
  291. strcln(config_memory, '\r');
  292. next = config_memory;
  293. while ((t = strchr (next, '\n')) != NULL) {
  294. *t = 0;
  295. p = next;
  296. next = t + 1;
  297. t = strbtrim (p);
  298. line++;
  299. /* blank lines, comments */
  300. if (!*t || *t == '#')
  301. continue;
  302. t = t + strcspn (t, ":=");
  303. if (!*t) {
  304. emsg ("invalid config line: %s", p);
  305. return -1;
  306. }
  307. /* Equal sign denotes a variable */
  308. var = (*t == '=');
  309. *t = 0;
  310. t++;
  311. /* Pass variables to the appropriate place */
  312. if (var) {
  313. config_var (strtrim (p), strtrim (t), line);
  314. /* And config lines to the appropriate place */
  315. } else {
  316. r = config_line (strtrim (p), strtrim (t), line);
  317. if (r < 0) {
  318. /* If -2 was returned, no error message was printed */
  319. if (r == -2)
  320. emsg ("[line %d] invalid configuration file", line);
  321. return -1;
  322. }
  323. }
  324. }
  325. return 0;
  326. }
  327. /* -----------------------------------------------------------------------------
  328. * CALLBACKS
  329. */
  330. int
  331. op_sysctlconfig (struct snmp_context *ctx, struct snmp_value *value,
  332. u_int sub, u_int iidx, enum snmp_op op)
  333. {
  334. asn_subid_t which = value->var.subs[sub - 1];
  335. int r = SNMP_ERR_NOERROR;
  336. (void)iidx;
  337. switch (which) {
  338. case LEAF_sysctlConfig:
  339. if (op == SNMP_OP_GET)
  340. return string_get (value, sysctl_config, -1);
  341. /* Remainder only at initialization */
  342. if (community != COMM_INITIALIZE)
  343. return SNMP_ERR_NOT_WRITEABLE;
  344. switch (op) {
  345. case SNMP_OP_SET:
  346. if ((r = string_save (value, ctx, -1, &sysctl_config)) == SNMP_ERR_NOERROR) {
  347. if (!sysctl_config[0])
  348. r = SNMP_ERR_WRONG_VALUE;
  349. else if (config_parse () < 0)
  350. r = SNMP_ERR_GENERR;
  351. }
  352. if (r != SNMP_ERR_NOERROR)
  353. string_rollback (ctx, &sysctl_config);
  354. break;
  355. case SNMP_OP_COMMIT:
  356. string_commit (ctx);
  357. break;
  358. case SNMP_OP_ROLLBACK:
  359. string_rollback (ctx, &sysctl_config);
  360. break;
  361. default:
  362. ASSERT(0);
  363. break;
  364. }
  365. return r;
  366. }
  367. ASSERT(0);
  368. return -1;
  369. }
  370. int
  371. op_sysctl (struct snmp_context *ctx, struct snmp_value *value,
  372. u_int sub, u_int iidx, enum snmp_op op)
  373. {
  374. asn_subid_t which = value->var.subs[sub - 1];
  375. int stdval = 0x01234567;
  376. (void)ctx;
  377. (void)iidx;
  378. switch (op) {
  379. case SNMP_OP_GET:
  380. break;
  381. case SNMP_OP_SET:
  382. /* XXX - maybe in the future */
  383. return SNMP_ERR_NOT_WRITEABLE;
  384. case SNMP_OP_ROLLBACK:
  385. case SNMP_OP_COMMIT:
  386. return SNMP_ERR_NOERROR;
  387. default:
  388. ASSERT(0);
  389. break;
  390. }
  391. switch (which) {
  392. case LEAF_sysctlCount:
  393. value->v.integer = entry_count;
  394. break;
  395. case LEAF_sysctlEndian:
  396. return string_get(value, (char *)&stdval, sizeof stdval);
  397. break;
  398. default:
  399. ASSERT(0);
  400. break;
  401. }
  402. return SNMP_ERR_NOERROR;
  403. }
  404. #if 0
  405. static int
  406. strisprint(char *str, size_t buflen)
  407. {
  408. while (buflen--) {
  409. if (!isprint(*str) && !isspace(*str))
  410. return 0;
  411. str++;
  412. }
  413. return 1;
  414. }
  415. #endif
  416. static char *
  417. makehexstr(unsigned char *buf, size_t buflen)
  418. {
  419. char *ostr, *str;
  420. ostr = str = malloc(buflen * 2 + 1);
  421. while (buflen--) {
  422. sprintf(str, "%02x", (unsigned)*buf++);
  423. str += 2;
  424. }
  425. str[0] = '\x00';
  426. return ostr;
  427. }
  428. int
  429. op_sysctlentry (struct snmp_context *ctx, struct snmp_value *value,
  430. u_int sub, u_int iidx, enum snmp_op op)
  431. {
  432. char buffer[512];
  433. asn_subid_t which = value->var.subs[sub - 1];
  434. struct data_entry *data;
  435. uint64_t ticks;
  436. size_t buflen;
  437. int fetch = 1;
  438. (void)ctx;
  439. switch (op) {
  440. case SNMP_OP_GETNEXT:
  441. data = NEXT_OBJECT_INT (&entries, &value->var, sub);
  442. if (data == NULL)
  443. return SNMP_ERR_NOSUCHNAME;
  444. value->var.len = sub + 1;
  445. value->var.subs[sub] = data->index;
  446. break;
  447. case SNMP_OP_GET:
  448. data = FIND_OBJECT_INT (&entries, &value->var, sub);
  449. if (data == NULL)
  450. return SNMP_ERR_NOSUCHNAME;
  451. break;
  452. case SNMP_OP_SET:
  453. if (index_decode (&value->var, sub, iidx, &data))
  454. return SNMP_ERR_NO_CREATION;
  455. data = FIND_OBJECT_INT (&entries, &value->var, sub);
  456. if (data != NULL)
  457. return SNMP_ERR_NOT_WRITEABLE;
  458. return SNMP_ERR_NO_CREATION;
  459. default:
  460. ASSERT(0);
  461. break;
  462. }
  463. /* Figure out if we should fetch or not */
  464. /* XXX - only fetch on value fetches? */
  465. ticks = getcurrticks ();
  466. if (ticks == 0)
  467. return SNMP_ERR_GENERR;
  468. if (data->cache && data->last_update) {
  469. if (ticks < (data->last_update + data->cache))
  470. fetch = 0;
  471. }
  472. if (fetch) {
  473. free(data->value_raw);
  474. data->value_raw = NULL;
  475. data->value_rawlen = 0;
  476. free(data->value_str);
  477. data->value_str = NULL;
  478. data->value_strlen = 0;
  479. /* XXX - oidfmt and friends from sysctl */
  480. /* XXX - length */
  481. buflen = sizeof buffer;
  482. if (sysctl(data->name, data->namelen, buffer, &buflen, NULL, 0) == -1) {
  483. data->error = errno;
  484. data->value_int = 0;
  485. } else {
  486. data->last_update = getcurrticks();
  487. data->error = 0;
  488. data->value_raw = malloc(buflen);
  489. data->value_rawlen = buflen;
  490. memcpy(data->value_raw, buffer, buflen);
  491. data->value_int = 0;
  492. emsg("test: buflen %d, sysctl: %s, ticks: %llu, lu: %llu", buflen, data->descr, ticks, data->last_update);
  493. if (strcmp("A", data->fmt) == 0) {
  494. data->value_str = malloc(buflen);
  495. strlcpy(data->value_str, buffer, buflen);
  496. } else if (strcmp("I", data->fmt) == 0) {
  497. data->value_int = *(int32_t *)buffer;
  498. } else {
  499. data->value_str = makehexstr(buffer, buflen);
  500. }
  501. }
  502. }
  503. if (data->value_str == NULL)
  504. asprintf(&data->value_str, "%jd", (intmax_t)data->value_int);
  505. data->value_strlen = strlen(data->value_str);
  506. switch (which) {
  507. case LEAF_sysctlIndex:
  508. value->v.integer = data->index;
  509. break;
  510. case LEAF_sysctlDescr:
  511. return (string_get (value, data->descr, -1));
  512. case LEAF_sysctlType:
  513. value->v.uint32 = data->type;
  514. break;
  515. case LEAF_sysctlFmt:
  516. return (string_get (value, data->fmt, -1));
  517. case LEAF_sysctlErrno:
  518. value->v.uint32 = data->error;
  519. break;
  520. case LEAF_sysctlLast:
  521. if (data->last_update)
  522. value->v.uint32 = (ticks - data->last_update);
  523. else
  524. value->v.uint32 = 0;
  525. break;
  526. case LEAF_sysctlInteger:
  527. value->v.uint32 = data->value_int;
  528. break;
  529. case LEAF_sysctlValue:
  530. return string_get(value, data->value_str, data->value_strlen);
  531. case LEAF_sysctlRaw:
  532. return string_get(value, data->value_raw, data->value_rawlen);
  533. case LEAF_sysctlCounter:
  534. value->v.counter64 = data->value_int;
  535. break;
  536. default:
  537. ASSERT(0);
  538. break;
  539. };
  540. return SNMP_ERR_NOERROR;
  541. }
  542. /* -----------------------------------------------------------------------------
  543. * MODULE
  544. */
  545. /* the initialisation function */
  546. static int
  547. module_init(struct lmodule *mod, int argc, char *argv[])
  548. {
  549. (void)argv;
  550. module = mod;
  551. if (argc != 0) {
  552. syslog(LOG_ERR, "bad number of arguments for %s", __func__);
  553. return EINVAL;
  554. }
  555. sysctl_config = strdup(DEFAULT_CONFIG);
  556. if (!sysctl_config)
  557. return ENOMEM;
  558. return 0;
  559. }
  560. /* Module is started */
  561. static void
  562. module_start (void)
  563. {
  564. reg_index = or_register (&oid_sysctl, "The MIB for sysctl data.", module);
  565. }
  566. /* Called, when the module is to be unloaded after it was successfully loaded */
  567. static int
  568. module_fini (void)
  569. {
  570. if (reg_index)
  571. or_unregister (reg_index);
  572. ASSERT (sysctl_config);
  573. free (sysctl_config);
  574. config_free_all ();
  575. return 0;
  576. }
  577. const struct snmp_module config = {
  578. .comment = "This module implements SNMP listing of data from sysctl.",
  579. .init = module_init,
  580. .start = module_start,
  581. .fini = module_fini,
  582. .tree = sysctl_ctree,
  583. .tree_size = sysctl_CTREE_SIZE,
  584. };