initial commit

This commit is contained in:
Olaf Rempel 2011-04-17 12:32:40 +02:00
commit 84d2bc1014
20 changed files with 2573 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build
snmp2rrd
snmp2rrd.pid
snmp2rrd.log

52
Makefile Normal file
View File

@ -0,0 +1,52 @@
DESTDIR =
BINARY_DIR = /usr/local/bin
CONFIG_DIR = /usr/local/etc
DATA_DIR = /var/lib/snmp2rrd
LOG_DIR = /var/log
PID_DIR = /var/run
# ############################
SRC = $(wildcard *.c)
TARGET = snmp2rrd
BUILD_DIR = build
CFLAGS = -O2 -g -pipe -Wall -Wno-unused-result
CFLAGS += -MMD -MF $(BUILD_DIR)/$(*D)/$(*F).d
CFLAGS += $(shell net-snmp-config --cflags)
LDFLAGS = -ldl -lrrd $(shell net-snmp-config --libs)
# ############################
ifeq ($(strip $(wildcard $(DESTDIR)$(CONFIG_DIR)/$(TARGET).conf)),)
NEWCONF=$(TARGET).conf
else
NEWCONF=$(TARGET).conf.dist
endif
# ############################
all: $(TARGET)
$(TARGET): $(patsubst %,$(BUILD_DIR)/%, $(SRC:.c=.o))
@echo " Linking file: $@"
@$(CC) $(LDFLAGS) $^ -o $@
$(BUILD_DIR)/%.o: %.c $(MAKEFILE_LIST)
@echo " Building file: $<"
@$(shell test -d $(BUILD_DIR)/$(*D) || mkdir -p $(BUILD_DIR)/$(*D))
@$(CC) $(CFLAGS) -o $@ -c $<
install: $(TARGET)
install -D -m 755 -s $(TARGET) $(DESTDIR)$(BINARY_DIR)/$(TARGET)
install -D -m 644 $(TARGET).conf $(DESTDIR)$(CONFIG_DIR)/$(NEWCONF)
sed -i -e "s:^logfile .*$$:logfile $(LOG_DIR)/$(TARGET).log:" \
-e "s:^pidfile .*$$:pidfile $(PID_DIR)/$(TARGET).pid:" \
-e "s:^rrd_dir .*$$:rrd_dir $(DATA_DIR):" \
$(DESTDIR)$(CONFIG_DIR)/$(NEWCONF)
install -d -m 755 $(DESTDIR)$(LOG_DIR)
install -d -m 755 $(DESTDIR)$(DATA_DIR)
clean:
rm -rf $(BUILD_DIR) $(TARGET)
include $(shell find $(BUILD_DIR) -name \*.d 2> /dev/null)

288
configfile.c Normal file
View File

@ -0,0 +1,288 @@
/***************************************************************************
* Copyright (C) 03/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "configfile.h"
#include "list.h"
#include "logging.h"
#define BUFSIZE 1024
struct conf_section {
struct list_head list;
struct list_head tupel_list;
const char *name;
};
struct conf_tupel {
struct list_head list;
const char *option;
const char *parameter;
};
static LIST_HEAD(config_list);
static struct conf_section * config_add_section(const char *name)
{
struct conf_section *section;
section = malloc(sizeof(struct conf_section) + strlen(name));
if (section == NULL)
return NULL;
INIT_LIST_HEAD(&section->list);
INIT_LIST_HEAD(&section->tupel_list);
section->name = strdup(name);
if (section->name == NULL) {
free(section);
return NULL;
}
list_add_tail(&section->list, &config_list);
return section;
}
static int config_add_tupel(struct conf_section *section, const char *option, const char *parameter)
{
struct conf_tupel *tupel = malloc(sizeof(struct conf_tupel));
if (tupel == NULL)
return -1;
INIT_LIST_HEAD(&tupel->list);
tupel->option = strdup(option);
tupel->parameter = strdup(parameter);
if (tupel->option == NULL || tupel->parameter == NULL) {
free(tupel);
return -1;
}
list_add_tail(&tupel->list, &section->tupel_list);
return 0;
}
int config_parse(const char *config)
{
FILE *fz = fopen(config, "r");
if (fz == NULL) {
log_print(LOG_ERROR, "config_parse(): %s", config);
return -1;
}
char *line = malloc(BUFSIZE);
if (line == NULL) {
log_print(LOG_ERROR, "config_parse(): out of memory");
fclose(fz);
return -1;
}
int linenum = 0;
struct conf_section *section = NULL;
while (fgets(line, BUFSIZE, fz) != NULL) {
linenum++;
if (line[0] == '#' || line[0] <= ' ') {
continue;
} else if (line[0] == '[') {
char *tok = strtok(line +1, " ]\n");
if (tok == NULL || (section = config_add_section(tok)) == NULL) {
log_print(LOG_WARN, "config_parse(): invalid section in row %d", linenum);
free(line);
fclose(fz);
return -1;
}
continue;
} else if (section == NULL) {
log_print(LOG_WARN, "config_parse(): missing section in row %d", linenum);
free(line);
fclose(fz);
return -1;
}
char *tmp, *tok = strtok_r(line, " \t\n", &tmp);
if (tok != NULL) {
char *tok2;
while ((tok2 = strtok_r(NULL, " \n", &tmp))) {
if (config_add_tupel(section, tok, tok2) != 0)
log_print(LOG_WARN, "config_parse(): invalid row %d", linenum);
}
}
}
fclose(fz);
free(line);
return 0;
}
void config_free(void)
{
struct conf_section *section, *section_tmp;
struct conf_tupel *tupel, *tupel_tmp;
list_for_each_entry_safe(section, section_tmp, &config_list, list) {
list_for_each_entry_safe(tupel, tupel_tmp, &section->tupel_list, list) {
list_del(&tupel->list);
free((char *)tupel->option);
free((char *)tupel->parameter);
free(tupel);
}
list_del(&section->list);
free(section);
}
}
static struct conf_section * config_get_section(const char *name)
{
struct conf_section *section;
list_for_each_entry(section, &config_list, list) {
if (!strcmp(section->name, name))
return section;
}
return NULL;
}
const char * config_get_string(const char *section_str, const char *option, const char *def)
{
struct conf_section *section = config_get_section(section_str);
if (section != NULL) {
struct conf_tupel *tupel;
list_for_each_entry(tupel, &section->tupel_list, list) {
if (!strcmp(tupel->option, option))
return tupel->parameter;
}
}
if (def != NULL)
log_print(LOG_WARN, "config [%s:%s] not found, using default: '%s'",
section_str, option, def);
return def;
}
int config_get_int(const char *section, const char *option, int *value, int def)
{
const char *ret = config_get_string(section, option, NULL);
if (ret == NULL) {
log_print(LOG_WARN, "config [%s:%s] not found, using default: '%d'",
section, option, def);
*value = def;
return -1;
}
char *tmp;
*value = strtol(ret, &tmp, 0);
if (*tmp != '\0' && !isspace(*tmp)) {
log_print(LOG_WARN, "config [%s:%s] not an integer: '%s', using default '%d'",
section, option, ret, def);
*value = def;
return -1;
}
return 0;
}
int config_get_strings(const char *section_str, const char *option,
int (*callback)(const char *value, void *privdata),
void *privdata)
{
struct conf_section *section = config_get_section(section_str);
if (section == NULL)
return -1;
int cnt = 0;
struct conf_tupel *tupel;
list_for_each_entry(tupel, &section->tupel_list, list) {
if (!strcmp(tupel->option, option))
if (callback(tupel->parameter, privdata) == 0)
cnt++;
}
return cnt;
}
struct strtoken * strtokenize(const char *input, const char *delim, int maxfields)
{
struct strtoken *tokens = malloc(sizeof(struct strtoken) +
(maxfields +1) * sizeof(char *) +
strlen(input));
if (tokens == NULL)
return NULL;
char *ptr = (char *)&tokens->field[maxfields];
strcpy(ptr, input);
int i;
char *tmp;
tokens->count = 0;
for (i = 0; i < maxfields; i++) {
tokens->field[i] = strtok_r(ptr, delim, &tmp);
ptr = NULL;
if (tokens->field[i] != NULL)
tokens->count++;
}
return tokens;
}
struct strtoken * config_get_strtoken(const char *section, const char *option, const char *delim, int maxfields)
{
const char *ret = config_get_string(section, option, NULL);
if (ret == NULL) {
log_print(LOG_WARN, "config [%s:%s] not found", section, option);
return NULL;
}
return strtokenize(ret, delim, maxfields);
}
int config_get_strtokens(const char *section_str, const char *option, const char *delim, int maxfields,
int (*callback)(struct strtoken *data, void *privdata),
void *privdata)
{
struct conf_section *section = config_get_section(section_str);
if (section == NULL)
return -1;
int cnt = 0;
struct conf_tupel *tupel;
list_for_each_entry(tupel, &section->tupel_list, list) {
if (!strcmp(tupel->option, option)) {
struct strtoken *tokens = strtokenize(tupel->parameter, delim, maxfields);
if (tokens != NULL) {
if (callback(tokens, privdata) == 0)
cnt++;
free(tokens);
}
}
}
return cnt;
}

28
configfile.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
int config_parse(const char *config);
void config_free(void);
const char * config_get_string(const char *section_str, const char *option, const char *def);
int config_get_int(const char *section, const char *option, int *value, int def);
int config_get_strings(const char *section_str, const char *option,
int (*callback)(const char *value, void *privdata),
void *privdata);
struct strtoken {
int count;
char *field[0];
};
struct strtoken * strtokenize(const char *input, const char *delim, int maxfields);
struct strtoken * config_get_strtoken(const char *section_str, const char *option, const char *delim, int maxfields);
int config_get_strtokens(const char *section_str, const char *option, const char *delim, int maxfields,
int (*callback)(struct strtoken *tokens, void *privdata),
void *privdata);
#endif /* _CONFIG_H_ */

310
event.c Normal file
View File

@ -0,0 +1,310 @@
/***************************************************************************
* Copyright (C) 03/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include "list.h"
#include "logging.h"
#include "event.h"
static LIST_HEAD(event_fd_list);
static LIST_HEAD(event_timeout_list);
struct event_fd {
struct list_head list;
unsigned int flags;
int fd;
int (*read_cb)(int fd, void *privdata);
int (*write_cb)(int fd, void *privdata);
void *read_priv;
void *write_priv;
};
struct event_timeout {
struct list_head list;
unsigned int flags;
struct timeval intervall;
struct timeval nextrun;
int (*callback)(void *privdata);
void *privdata;
};
struct event_fd * event_add_fd(
struct event_fd *entry,
int fd,
int type,
int (*callback)(int fd, void *privdata),
void *privdata)
{
/* check valid filediskriptor */
if (fd < 0 || fd > FD_SETSIZE) {
log_print(LOG_ERROR, "event_add_fd(): invalid fd");
return NULL;
}
/* check valid type (read/write) */
if (!(type & FD_TYPES)) {
log_print(LOG_ERROR, "event_add_fd(): invalid type");
return NULL;
}
/* create new entry */
if (entry == NULL) {
entry = malloc(sizeof(struct event_fd));
if (entry == NULL) {
log_print(LOG_ERROR, "event_add_fd(): out of memory");
return NULL;
}
memset(entry, 0, sizeof(struct event_fd));
entry->flags |= EVENT_NEW;
entry->fd = fd;
/* put it on the list */
list_add_tail(&entry->list, &event_fd_list);
}
if (type & FD_READ) {
entry->flags = (callback != NULL) ? (entry->flags | FD_READ | EVENT_NEW) : (entry->flags & ~FD_READ);
entry->read_cb = callback;
entry->read_priv = privdata;
} else if (type & FD_WRITE) {
entry->flags = (callback != NULL) ? (entry->flags | FD_WRITE | EVENT_NEW) : (entry->flags & ~FD_WRITE);
entry->write_cb = callback;
entry->write_priv = privdata;
}
return entry;
}
int event_get_fd(struct event_fd *entry)
{
return (entry != NULL) ? entry->fd: -1;
}
void event_remove_fd(struct event_fd *entry)
{
/* mark the event as deleted -> remove in select() loop */
entry->flags |= EVENT_DELETE;
}
static void add_timeval(struct timeval *ret, struct timeval *a, struct timeval *b)
{
ret->tv_usec = a->tv_usec + b->tv_usec;
ret->tv_sec = a->tv_sec + b->tv_sec;
if (ret->tv_usec >= 1000000) {
ret->tv_usec -= 1000000;
ret->tv_sec++;
}
}
static void sub_timeval(struct timeval *ret, struct timeval *a, struct timeval *b)
{
ret->tv_usec = a->tv_usec - b->tv_usec;
ret->tv_sec = a->tv_sec - b->tv_sec;
if (ret->tv_usec < 0) {
ret->tv_usec += 1000000;
ret->tv_sec--;
}
}
static int cmp_timeval(struct timeval *a, struct timeval *b)
{
if (a->tv_sec > b->tv_sec)
return -1;
if (a->tv_sec < b->tv_sec)
return 1;
if (a->tv_usec > b->tv_usec)
return -1;
if (a->tv_usec < b->tv_usec)
return 1;
return 0;
}
static void schedule_nextrun(struct event_timeout *entry, struct timeval *now)
{
add_timeval(&entry->nextrun, now, &entry->intervall);
struct event_timeout *search;
list_for_each_entry(search, &event_timeout_list, list) {
if (search->nextrun.tv_sec > entry->nextrun.tv_sec) {
list_add_tail(&entry->list, &search->list);
return;
} else if (search->nextrun.tv_sec == entry->nextrun.tv_sec &&
search->nextrun.tv_usec > entry->nextrun.tv_usec) {
list_add_tail(&entry->list, &search->list);
return;
}
}
list_add_tail(&entry->list, &event_timeout_list);
}
struct event_timeout * event_add_timeout(
struct timeval *timeout,
int (*callback)(void *privdata),
void *privdata)
{
struct event_timeout *entry;
entry = malloc(sizeof(struct event_timeout));
if (entry == NULL) {
log_print(LOG_ERROR, "event_add_timeout(): out of memory");
return NULL;
}
entry->flags = 0;
memcpy(&entry->intervall, timeout, sizeof(entry->intervall));
entry->callback = callback;
entry->privdata = privdata;
struct timeval now;
gettimeofday(&now, NULL);
schedule_nextrun(entry, &now);
return entry;
}
void event_remove_timeout(struct event_timeout *entry)
{
/* mark the event as deleted -> remove in select() loop */
entry->flags |= EVENT_DELETE;
}
int event_loop(int (*pre_select_cb)(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata),
int (*post_select_cb)(int retval, void *readfds, void *writefds, void *privdata),
void *privdata)
{
while (1) {
/* default value if no application timeout is present */
struct timeval timeout = {
.tv_sec = 3600,
.tv_usec = 0,
};
if (!list_empty(&event_timeout_list)) {
struct timeval now;
gettimeofday(&now, NULL);
struct event_timeout *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &event_timeout_list, list) {
if (entry->flags & EVENT_DELETE) {
list_del(&entry->list);
free(entry);
continue;
}
/* first timeout not elapsed, exit search (since list is sorted) */
if (cmp_timeval(&entry->nextrun, &now) == -1)
break;
/* remove event from list */
list_del(&entry->list);
/* execute callback, when callback returns 0 -> schedule event again */
if (entry->callback(entry->privdata)) {
free(entry);
} else {
schedule_nextrun(entry, &now);
}
}
if (!list_empty(&event_timeout_list)) {
entry = list_entry(event_timeout_list.next, typeof(*entry), list);
/* calc select() timeout */
sub_timeval(&timeout, &entry->nextrun, &now);
}
}
struct event_fd *entry, *tmp;
int maxfd = -1;
fd_set readfds, writefds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
list_for_each_entry_safe(entry, tmp, &event_fd_list, list) {
entry->flags &= ~EVENT_NEW;
if (entry->flags & EVENT_DELETE) {
list_del(&entry->list);
free(entry);
continue;
}
if (entry->flags & FD_READ)
FD_SET(entry->fd, &readfds);
if (entry->flags & FD_WRITE)
FD_SET(entry->fd, &writefds);
maxfd = (entry->fd > maxfd) ? entry->fd : maxfd;
}
maxfd++;
/* exit loop if callback returns true */
if (pre_select_cb != NULL && pre_select_cb(&maxfd, (void *)&readfds, (void *)&writefds, &timeout, privdata) != 0)
break;
int retval = select(maxfd, &readfds, &writefds, NULL, &timeout);
if (retval < 0 && errno == EINTR) {
errno = 0;
continue;
} else if (retval < 0) {
log_print(LOG_ERROR, "event_loop(): select():");
continue;
}
/* exit loop if callback returns true */
if (post_select_cb != NULL && post_select_cb(retval, (void *)&readfds, (void *)&writefds, privdata) != 0)
break;
/* timeout */
if (retval == 0)
continue;
list_for_each_entry(entry, &event_fd_list, list) {
if (((entry->flags & (FD_READ | EVENT_NEW)) == FD_READ) && FD_ISSET(entry->fd, &readfds))
if (entry->read_cb(entry->fd, entry->read_priv) != 0)
entry->flags |= EVENT_DELETE;
if (((entry->flags & (FD_WRITE | EVENT_NEW)) == FD_WRITE) && FD_ISSET(entry->fd, &writefds))
if (entry->write_cb(entry->fd, entry->write_priv) != 0)
entry->flags |= EVENT_DELETE;
}
}
return 0;
}

44
event.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef _EVENT_H_
#define _EVENT_H_
#include <sys/time.h>
#define EVENT_NEW 0x1000
#define EVENT_DELETE 0x2000
#define FD_READ 0x0001
#define FD_WRITE 0x0002
#define FD_TYPES (FD_READ | FD_WRITE)
#define event_add_readfd(entry, fd, callback, privdata) \
event_add_fd(entry, fd, FD_READ, callback, privdata)
#define event_add_writefd(entry, fd, callback, privdata) \
event_add_fd(entry, fd, FD_WRITE, callback, privdata)
/* inner details are not visible to external users (TODO: size unknown) */
struct event_fd;
struct event_timeout;
struct event_fd * event_add_fd(
struct event_fd *entry,
int fd,
int type,
int (*callback)(int fd, void *privdata),
void *privdata);
int event_get_fd(struct event_fd *entry);
void event_remove_fd(struct event_fd *entry);
struct event_timeout * event_add_timeout(
struct timeval *timeout,
int (*callback)(void *privdata),
void *privdata);
void event_remove_timeout(struct event_timeout *entry);
int event_loop(int (*pre_select_cb)(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata),
int (*post_select_cb)(int retval, void *readfds, void *writefds, void *privdata),
void *privdata);
#endif /* _EVENT_H_ */

239
linebuffer.c Normal file
View File

@ -0,0 +1,239 @@
/***************************************************************************
* Copyright (C) 03/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include "linebuffer.h"
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
struct lbuf {
size_t size;
size_t pos;
char *token;
char data[0];
};
/*
* creates linebuffer with given size
*/
struct lbuf * lbuf_create(size_t size)
{
struct lbuf *buf = malloc(sizeof(struct lbuf) + size);
if (buf == NULL)
return NULL;
buf->size = size;
buf->pos = 0;
return buf;
}
/*
* frees the linebuffer and all data in it
*/
void lbuf_free(struct lbuf *buf)
{
free(buf);
}
/*
* clears the linebuffer
* - returns the datasize it was holding
*/
int lbuf_clear(struct lbuf *buf)
{
int oldpos = buf->pos;
buf->pos = 0;
return oldpos;
}
/*
* get pointer to internal data
* - returns pointer to datasize
*/
char * lbuf_getdata(struct lbuf *buf, size_t *len)
{
if (len != NULL)
*len = buf->pos;
return buf->data;
}
/*
* appends data to the buffer
* - returns the number of bytes copied
*/
int lbuf_append(struct lbuf *buf, const char *src, size_t size)
{
int len = MIN(buf->size - buf->pos, size);
memcpy(buf->data + buf->pos, src, len);
buf->pos += len;
return len;
}
/*
* reads as much data as it can get from a FD
* - returns the number of bytes read, or -1 on error
*/
int lbuf_readfd(struct lbuf *buf, int fd)
{
int len = read(fd, buf->data + buf->pos, buf->size - buf->pos);
if (len <= 0)
return -1;
buf->pos += len;
return len;
}
/*
* parses as much data as it can get from a FD
* parses means: backspaces remove one byte from the end of the buffer
* - returns 0 on success, or -1 on error
*/
int lbuf_parsefd(struct lbuf *buf, int fd)
{
char tmp[32];
int len = read(fd, tmp, sizeof(tmp));
if (len <= 0)
return -1;
int i;
for (i = 0; i < len; i++) {
/* "understand" backspace */
if (tmp[i] == 0x08 && buf->pos > 0) {
buf->pos--;
/* copy */
} else if (tmp[i] >= ' ' || tmp[i] == '\n') {
*(buf->data + buf->pos++) = tmp[i];
}
if (buf->pos > buf->size)
return -1;
}
/* TODO: return bytes appended to buffer? */
return 0;
}
/*
* writes as much data as it can to a FD
* - returns the number of bytes written, or -1 on error
*/
int lbuf_writefd(struct lbuf *buf, int fd)
{
int len = write(fd, buf->data, buf->pos);
if (len <= 0)
return -1;
/* handle partial writes */
if (len != buf->pos)
memmove(buf->data, buf->data + len, buf->pos - len);
buf->pos -= len;
return len;
}
/*
* append va_list to buffer
* - returns number ob bytes appended, or -1 on error
*/
int lbuf_vprintf(struct lbuf *buf, const char *fmt, va_list az)
{
int len = vsnprintf(buf->data + buf->pos, buf->size - buf->pos, fmt, az);
if (len < 0 || len >= (buf->size - buf->pos))
return -1;
buf->pos += len;
return len;
}
/*
* printf into buffer
* - returns number of bytes appended, or -1 on error
*/
int lbuf_printf(struct lbuf *buf, const char *fmt, ...)
{
va_list az;
va_start(az, fmt);
int ret = lbuf_vprintf(buf, fmt, az);
va_end(az);
return ret;
}
/*
* get next non-empty token
* - returns pointer to next token
* - returns NULL if no delimter is found in buffer
*/
char * lbuf_gettok(struct lbuf *buf, const char *delim)
{
char *start = buf->data;
char *end = buf->data + buf->pos;
int dlen = strlen(delim);
while (1) {
buf->token = NULL;
/* find first delimiter in buffer */
int i;
for (i = 0; i < dlen; i++) {
char *tok = memchr(start, delim[i], end - start);
if (tok != NULL && (tok < buf->token || buf->token == NULL))
buf->token = tok;
}
/* nothing found */
if (buf->token == NULL)
return NULL;
/* delimter found on start pos -> skip empty token */
if (buf->token == start) {
start++;
if (start >= end)
return NULL;
} else {
/* non-empty token found, exit */
break;
}
}
/* overwrite token, return start ptr */
*(buf->token) = '\0';
return start;
}
/*
* release previous fetched line
* - returns number of remaining bytes in buffer
*/
int lbuf_freetok(struct lbuf *buf)
{
if (buf->token != NULL) {
buf->pos -= (buf->token - buf->data) +1;
memmove(buf->data, buf->token +1, buf->pos);
buf->token = NULL;
}
return buf->pos;
}

27
linebuffer.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef _LINEBUFFER_H_
#define _LINEBUFFER_H_
#include <stdarg.h>
/* hide details */
struct lbuf;
struct lbuf * lbuf_create(size_t size);
void lbuf_free(struct lbuf *buf);
int lbuf_clear(struct lbuf *buf);
int lbuf_readfd(struct lbuf *buf, int fd);
int lbuf_parsefd(struct lbuf *buf, int fd);
int lbuf_writefd(struct lbuf *buf, int fd);
char * lbuf_getdata(struct lbuf *buf, size_t *len);
int lbuf_append(struct lbuf *buf, const char *src, size_t size);
int lbuf_vprintf(struct lbuf *buf, const char *fmt, va_list ap);
int lbuf_printf(struct lbuf *buf, const char *fmt, ...);
char * lbuf_gettok(struct lbuf *buf, const char *delim);
int lbuf_freetok(struct lbuf *buf);
#endif /* _LINEBUFFER_H_ */

268
list.h Normal file
View File

@ -0,0 +1,268 @@
#ifndef _LIST_H_
#define _LIST_H_
/*
* stolen from linux kernel 2.6.11 (http://kernel.org/)
* linux/include/linux/stddef.h (offsetoff)
* linux/include/linux/kernel.h (container_of)
* linux/include/linux/list.h (*list*)
* linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND)
*
* modified by Olaf Rempel <razzor@kopf-tisch.de>
*/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/*
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/*
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/*
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = NULL;
entry->prev = NULL;
}
/*
* list_del_init - deletes entry from list and reinitialize it.
* entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
/*
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
/*
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/*
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/*
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
/*
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}
/*
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/*
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/*
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/*
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/*
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/*
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
/*
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/* Return pointer to first true entry, if any, or NULL. A macro
required to allow inlining of cmpfn. */
#define LIST_FIND(head, cmpfn, type, args...) \
({ \
const struct list_head *__i, *__j = NULL; \
\
list_for_each(__i, (head)) \
if (cmpfn((const type)__i , ## args)) { \
__j = __i; \
break; \
} \
(type)__j; \
})
#endif /* _LIST_H_ */

114
logging.c Normal file
View File

@ -0,0 +1,114 @@
/***************************************************************************
* Copyright (C) 07/2007 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include "logging.h"
#define BUFSIZE 8192
static FILE *log_fd = NULL;
static int log_prio = LOG_EVERYTIME;
static char *buffer = NULL;
int log_print(int prio, const char *fmt, ...)
{
va_list az;
int len = 0, retval;
if (prio < log_prio)
return 0;
if (buffer == NULL) {
buffer = malloc(BUFSIZE);
if (buffer == NULL) {
fprintf(stderr, "log_print(): out of memory\n");
return -1;
}
}
if (log_fd != NULL) {
time_t tzgr;
time(&tzgr);
len += strftime(buffer, BUFSIZE, "%b %d %H:%M:%S :", localtime(&tzgr));
}
va_start(az, fmt);
len += vsnprintf(buffer + len, BUFSIZE - len, fmt, az);
va_end(az);
if (len < 0 || len >= BUFSIZE) {
errno = 0;
return log_print(LOG_ERROR, "log_print: arguments too long");
}
if (errno) {
len += snprintf(buffer + len, BUFSIZE - len, ": %s", strerror(errno));
errno = 0;
}
retval = fprintf((log_fd ? log_fd : stderr), "%s\n", buffer);
fflush(log_fd);
return retval;
}
void log_close(void)
{
if (buffer) {
free(buffer);
buffer = NULL;
}
if (log_fd) {
fclose(log_fd);
log_fd = NULL;
}
}
int log_init(const char *logfile)
{
if (log_fd != NULL)
log_close();
log_fd = fopen(logfile, "a");
if (log_fd == NULL) {
fprintf(stderr, "log_init(): can not open logfile");
return -1;
}
if (fcntl(fileno(log_fd), F_SETFD, FD_CLOEXEC) < 0) {
fprintf(stderr, "log_init(): fcntl(FD_CLOEXEC)");
return -1;
}
log_prio = LOG_EVERYTIME;
return 0;
}
void log_setprio(int prio)
{
log_prio = prio;
}

23
logging.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _LOGGING_H_
#define _LOGGING_H_
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
#define LOG_EVERYTIME LOG_EMERG
#define LOG_ERROR LOG_ERR
#define LOG_WARN LOG_WARNING
int log_init(const char *logfile);
void log_close(void);
void log_setprio(int prio);
int log_print(int prio, const char *fmt, ... );
#endif /* _LOGGING_H_ */

73
pidfile.c Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include "logging.h"
int pidfile_create(const char *filename)
{
int fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644);
if (fd < 0)
return -1;
char buf[8];
int len = snprintf(buf, sizeof(buf), "%d", getpid());
write(fd, buf, len);
close(fd);
return 0;
}
int pidfile_remove(const char *filename)
{
return unlink(filename);
}
pid_t pidfile_check(const char *filename, int remove_stale)
{
int fd = open(filename, O_RDWR);
if (fd < 0) {
if (errno == ENOENT) {
errno = 0;
return 0;
}
return -1;
}
char buf[9];
int len = read(fd, buf, sizeof(buf) -1);
buf[len] = '\0';
close(fd);
char *tmp;
pid_t pid = strtol(buf, &tmp, 10);
if (len == 0 || tmp == buf)
pid = -1;
/* just return the pid */
if (!remove_stale)
return pid;
/* invalid pid, remove stale file */
if (pid == -1) {
pidfile_remove(filename);
return 0;
}
/* check if pid is still running */
if (kill(pid, 0) == 0 || errno != ESRCH) {
errno = 0;
return pid;
}
errno = 0;
pidfile_remove(filename);
return 0;
}

11
pidfile.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _PIDFILE_H_
#define _PIDFILE_H_
#include <sys/types.h>
int pidfile_create(const char *filename);
int pidfile_remove(const char *filename);
pid_t pidfile_check(const char *filename, int remove_stale);
#endif // _PIDFILE_H_

228
rrdtool.c Normal file
View File

@ -0,0 +1,228 @@
/***************************************************************************
* Copyright (C) 12/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <rrd.h>
#include "configfile.h"
#include "linebuffer.h"
#include "list.h"
#include "logging.h"
#define DEFAULT_STEP 10
#define ARGCMAX 64
#define ARGVSIZE 1024
#define BUFSIZE 1024
static const char *rrd_dir;
int my_rrd_init(void)
{
rrd_dir = config_get_string("global", "rrd_dir", ".");
return 0;
}
static int strsplit(char *string, char *delim, char **fields, int size)
{
int i = 0;
char *tmp, *ptr = string;
while ((fields[i] = strtok_r(ptr, delim, &tmp)) != NULL) {
ptr = NULL;
i++;
if (i >= size)
break;
}
return i;
}
static int do_rrd(int (*rrd_func)(int, char **), struct lbuf *buffer)
{
size_t len;
char *cmd = lbuf_getdata(buffer, &len);
if (len < ARGVSIZE -1)
cmd[len] = '\0';
char *argv[ARGCMAX];
int argc = strsplit(cmd, " \t\n", argv, ARGCMAX -1);
argv[argc] = NULL;
optind = 0;
rrd_clear_error();
int retval = rrd_func(argc, argv);
if (retval == -1) {
errno = 0;
log_print(LOG_ERROR, "rrd_func failed: %s: %s", argv[1], rrd_get_error());
}
return retval;
}
static int rrd_create_file(const char *filename, const char *ds_define, int interval)
{
struct lbuf *buffer = lbuf_create(ARGVSIZE);
if (buffer == NULL) {
log_print(LOG_ERROR, "rrd_create_file: out of memory");
return -1;
}
int ret = lbuf_printf(buffer, "create %s -s %d %s ", filename, interval, ds_define);
if (ret == -1) {
log_print(LOG_ERROR, "rrd_create_file: arguments too long");
lbuf_free(buffer);
return -1;
}
ret = do_rrd(&rrd_create, buffer);
lbuf_free(buffer);
return ret;
}
static int rrd_update_file(const char *filename, const char *values)
{
struct lbuf *buffer = lbuf_create(ARGVSIZE);
if (buffer == NULL) {
log_print(LOG_ERROR, "append_ds_config: out of memory");
return -1;
}
int ret = lbuf_printf(buffer, "update %s %s", filename, values);
if (ret == -1) {
log_print(LOG_ERROR, "rrd_update_file: arguments too long");
lbuf_free(buffer);
return -1;
}
ret = do_rrd(&rrd_update, buffer);
lbuf_free(buffer);
return ret;
}
static int check_create_dir(const char *dir)
{
struct stat statbuf;
if (stat(dir, &statbuf) == -1) {
if (errno == ENOENT) {
errno = 0;
if (mkdir(dir, 0755) == -1) {
log_print(LOG_ERROR, "check_create_dir: mkdir(%s)", dir);
return -1;
}
} else {
log_print(LOG_ERROR, "check_create_dir: stat(%s)", dir);
return -1;
}
} else if (!S_ISDIR (statbuf.st_mode)) {
log_print(LOG_ERROR, "check_create_dir: stat(%s): Not a directory!", dir);
return -1;
}
return 0;
}
static int create_parent_dirs(char *filename)
{
char *nextslash = filename;
char *lastslash = strrchr(filename, '/');
if (lastslash == NULL) {
log_print(LOG_ERROR, "create_parent_dirs: invalid file name");
return -1;
}
*lastslash = '\0';
while (1) {
if (strchr(nextslash, '/') == NULL) {
if (*lastslash == '\0')
*lastslash = '/';
else
break;
}
nextslash = strchr(nextslash, '/');
*nextslash = '\0';
if (filename != nextslash) {
if (check_create_dir(filename) == -1) {
*nextslash = '/';
*lastslash = '/';
return -1;
}
}
*nextslash++ = '/';
}
return 0;
}
int rrd_submit(const char *filename, const char *ds_define, int interval, const char *data)
{
char *fullfile = malloc(BUFSIZE);
if (fullfile == NULL) {
log_print(LOG_ERROR, "rrd_submit: out of memory");
return -1;
}
int len = snprintf(fullfile, BUFSIZE, "%s/%s", rrd_dir, filename);
if (len < 0 || len >= BUFSIZE) {
log_print(LOG_ERROR, "rrd_submit: arguments too long");
free(fullfile);
return -1;
}
struct stat statbuf;
if (stat(fullfile, &statbuf) == -1) {
if (errno == ENOENT) {
errno = 0;
if (create_parent_dirs(fullfile) == -1) {
free(fullfile);
return -1;
}
if (rrd_create_file(fullfile, ds_define, interval) == -1) {
free(fullfile);
return -1;
}
} else {
log_print(LOG_ERROR, "rrd_submit: stat(%s):", fullfile);
free(fullfile);
return -1;
}
} else if (!S_ISREG (statbuf.st_mode)) {
log_print(LOG_ERROR, "rrd_submit: stat(%s): Not a regular file!", fullfile);
free(fullfile);
return -1;
}
rrd_update_file(fullfile, data);
free(fullfile);
return 0;
}

7
rrdtool.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _RRDTOOL_H_
#define _RRDTOOL_H_
int my_rrd_init(void);
int rrd_submit(const char *filename, const char *ds_define, int interval, const char *data);
#endif /* _RRDTOOL_H_ */

499
snmp.c Normal file
View File

@ -0,0 +1,499 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "configfile.h"
#include "event.h"
#include "linebuffer.h"
#include "list.h"
#include "logging.h"
#include "rrdtool.h"
#define MAX_PORT_VALUES 8
#define MAX_RRD_DEFINE_SIZE 512
#define MAX_RRD_RESULT_SIZE 128
struct sw_porttype {
struct list_head list; // global list of sw_porttype(s)
const char *name; // "es2024port"
const char *oid_base[MAX_PORT_VALUES]; // "IF-MIB::IfInOctets"
int value_count; // number of snmp values
struct lbuf *rrd_definition; // rrd/rra definition
};
struct sw_port {
struct list_head list; // per sw_hosttype list of sw_port(s)
const char *name; // "port01"
struct sw_porttype *porttype;
const char *oid_ext; // "1"
};
struct sw_hosttype {
struct list_head list; // global list of sw_hosttype(s)
const char *name; // "es2024"
struct list_head ports; // list of sw_port(s)
int port_count; // number of ports
};
struct sw_host {
struct list_head list; // global list of sw_host(s)
const char *name; // "router01"
struct sw_hosttype *hosttype;
int active;
struct sw_port *current_port; // current port
int current_value; // current value/oid
struct lbuf *result;
struct snmp_session *session; // snmp private data
};
static int snmp_interval;
static LIST_HEAD(porttype_list);
static LIST_HEAD(hosttype_list);
static LIST_HEAD(host_list);
static void log_snmp_error(struct snmp_session *session, const char *prefix)
{
// char *snmp_error_str = NULL;
// snmp_perror("read_objid");
// snmp_error(session, &errno, &snmp_errno, &snmp_error_str);
log_print(LOG_INFO, "%s: '%s'", prefix, snmp_api_errstring(snmp_errno));
snmp_errno = 0;
}
int snmp_pre_select_cb(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata)
{
int block = 1;
snmp_select_info(maxfd, (fd_set *)readfds, timeout, &block);
return 0;
}
int snmp_post_select_cb(int retval, void *readfds, void *writefds, void *privdata)
{
if (retval != 0) {
snmp_read((fd_set *)readfds);
} else {
snmp_timeout();
}
return 0;
}
static int add_portvalue_cb(const char *parameter, void *privdata)
{
struct sw_porttype *porttype = (struct sw_porttype *)privdata;
struct strtoken *tokens = strtokenize(parameter, ",", 2);
if (tokens == NULL) {
log_print(LOG_ERROR, "add_portvalue_cb(): out of memory(1)");
return 1;
}
if (tokens->count != 2) {
log_print(LOG_ERROR, "add_portvalue_cb(): invalid value line");
free(tokens);
return 1;
}
if (porttype->value_count < MAX_PORT_VALUES -1) {
porttype->oid_base[porttype->value_count++] = strdup(tokens->field[0]);
} else {
log_print(LOG_WARN, "add_portvalue_cb(): too many oids for %s", porttype->name);
free(tokens);
return 1;
}
if (lbuf_printf(porttype->rrd_definition, "%s ", tokens->field[1]) < 0) {
log_print(LOG_WARN, "add_portvalue_cb(): truncated rrd definition for %s", porttype->name);
free(tokens);
return 1;
}
free(tokens);
return 0;
}
static int add_portdef_cb(const char *parameter, void *privdata)
{
struct sw_porttype *porttype = (struct sw_porttype *)privdata;
if (lbuf_printf(porttype->rrd_definition, "%s ", parameter) < 0) {
log_print(LOG_WARN, "add_portdef_cb(): truncated rrd definition for %s", porttype->name);
return 1;
}
return 0;
}
static int add_porttype_cb(const char *parameter, void *privdata)
{
struct sw_porttype *porttype = malloc(sizeof(struct sw_porttype));
if (porttype == NULL) {
log_print(LOG_WARN, "add_porttype_cb(): out of memory");
return 1;
}
memset(porttype, 0, sizeof(struct sw_porttype));
porttype->name = strdup(parameter);
porttype->rrd_definition = lbuf_create(MAX_RRD_DEFINE_SIZE);
char section[32];
snprintf(section, sizeof(section), "porttype_%s", parameter);
config_get_strings(section, "value", add_portvalue_cb, (void *)porttype);
config_get_strings(section, "rra", add_portdef_cb, (void *)porttype);
if (porttype->value_count > 0) {
list_add_tail(&porttype->list, &porttype_list);
log_print(LOG_INFO, "adding porttype '%s' with %d snmp values", porttype->name, porttype->value_count);
} else {
lbuf_free(porttype->rrd_definition);
free((char *)porttype->name);
free(porttype);
}
return 0;
}
static struct sw_porttype * find_porttype_by_name(const char *name)
{
struct sw_porttype *porttype;
list_for_each_entry(porttype, &porttype_list, list) {
if (strcmp(porttype->name, name) == 0)
return porttype;
}
return NULL;
}
static int add_port_cb(const char *parameter, void *privdata)
{
struct sw_hosttype *hosttype = (struct sw_hosttype *)privdata;
struct strtoken *tokens = strtokenize(parameter, ",", 3);
if (tokens == NULL) {
log_print(LOG_ERROR, "add_port_cb(): out of memory(1)");
return 1;
}
if (tokens->count != 3) {
log_print(LOG_ERROR, "add_port_cb(): invalid port line");
free(tokens);
return 1;
}
struct sw_port *port = malloc(sizeof(struct sw_port));
if (port == NULL) {
log_print(LOG_WARN, "add_port_cb(): out of memory(2)");
free(tokens);
return 1;
}
port->porttype = find_porttype_by_name(tokens->field[1]);
if (port->porttype == NULL) {
log_print(LOG_WARN, "add_port_cb(): invalid porttype '%s'", tokens->field[1]);
free(port);
free(tokens);
return 1;
}
port->name = strdup(tokens->field[0]);
port->oid_ext = strdup(tokens->field[2]);
list_add_tail(&port->list, &hosttype->ports);
hosttype->port_count++;
free(tokens);
return 0;
}
static int add_hosttype_cb(const char *parameter, void *privdata)
{
struct sw_hosttype *hosttype = malloc(sizeof(struct sw_hosttype));
if (hosttype == NULL) {
log_print(LOG_WARN, "add_hosttype_cb(): out of memory");
return 1;
}
memset(hosttype, 0, sizeof(struct sw_hosttype));
hosttype->name = strdup(parameter);
INIT_LIST_HEAD(&hosttype->ports);
char section[32];
snprintf(section, sizeof(section), "hosttype_%s", parameter);
config_get_strings(section, "port", add_port_cb, (void *)hosttype);
if (hosttype->port_count > 0) {
list_add_tail(&hosttype->list, &hosttype_list);
log_print(LOG_INFO, "adding hosttype '%s' with %d ports", hosttype->name, hosttype->port_count);
} else {
free((char *)hosttype->name);
free(hosttype);
}
return 0;
}
static struct sw_hosttype * find_hosttype_by_name(const char *name)
{
struct sw_hosttype *hosttype;
list_for_each_entry(hosttype, &hosttype_list, list) {
if (strcmp(hosttype->name, name) == 0)
return hosttype;
}
return NULL;
}
static int snmp_async_cb(int operation, struct snmp_session *sp, int reqid, struct snmp_pdu *pdu, void *magic);
static int add_host_cb(const char *parameter, void *privdata)
{
struct strtoken *tokens = strtokenize(parameter, ",", 4);
if (tokens == NULL) {
log_print(LOG_ERROR, "add_host_cb(): out of memory(1)");
return 1;
}
if (tokens->count != 4) {
log_print(LOG_ERROR, "add_host_cb(): invalid host line");
return 1;
}
struct sw_host *host = malloc(sizeof(struct sw_host));
if (host == NULL) {
log_print(LOG_ERROR, "add_host_cb(): out of memory(2)");
free(tokens);
return 1;
}
host->hosttype = find_hosttype_by_name(tokens->field[1]);
if (host->hosttype == NULL) {
log_print(LOG_ERROR, "add_host_cb(): invalid hosttype '%s'", tokens->field[1]);
free(host);
free(tokens);
return 1;
}
host->result = lbuf_create(MAX_RRD_RESULT_SIZE);
if (host->result == NULL) {
log_print(LOG_ERROR, "add_host_cb(): out of memory(3)");
free(host);
free(tokens);
return 1;
}
host->name = strdup(tokens->field[0]);
struct snmp_session sess;
snmp_sess_init(&sess);
sess.version = SNMP_VERSION_2c;
sess.peername = strdup(tokens->field[2]);
sess.community = (unsigned char *)strdup(tokens->field[3]);
sess.community_len = strlen((char *)sess.community);
sess.callback = snmp_async_cb;
sess.callback_magic = host;
if ((host->session = snmp_open(&sess)) == NULL) {
log_snmp_error(&sess, "add_host_cb(): snmp_open()");
free((char *)sess.community);
free((char *)host->name);
lbuf_free(host->result);
free(host->hosttype);
free(host);
free(tokens);
return 1;
}
host->active = 0;
host->current_port = NULL;
host->current_value = 0;
log_print(LOG_INFO, "adding host %s", host->session->peername);
list_add_tail(&host->list, &host_list);
free((char *)sess.community);
return 0;
}
static int do_snmp_poll(void *privdata);
int snmp_init(void)
{
init_snmp("snmp2rrd");
errno = 0;
config_get_strings("global", "porttype", add_porttype_cb, NULL);
config_get_strings("global", "hosttype", add_hosttype_cb, NULL);
config_get_strings("global", "host", add_host_cb, NULL);
config_get_int("global", "interval", &snmp_interval, 60);
struct timeval tv_interval = {
.tv_sec = snmp_interval,
.tv_usec = 0,
};
event_add_timeout(&tv_interval, do_snmp_poll, NULL);
do_snmp_poll(NULL);
return 0;
}
static int send_next_request(struct sw_host *host)
{
/* first port */
if (host->current_port == NULL) {
host->current_port = list_entry(host->hosttype->ports.next, struct sw_port, list);
host->current_value = 0;
// use "Now" as timespec in RRD commit
lbuf_clear(host->result);
lbuf_printf(host->result, "N");
} else {
host->current_value++;
if (host->current_value >= host->current_port->porttype->value_count) {
char filename[64];
snprintf(filename, sizeof(filename), "%s/%s.rrd", host->name, host->current_port->name);
char *rrd_definition = lbuf_getdata(host->current_port->porttype->rrd_definition, NULL);
char *result = lbuf_getdata(host->result, NULL);
rrd_submit(filename, rrd_definition, snmp_interval, result);
// log_print(LOG_INFO, "%s %s %d %s", filename, rrd_definition, snmp_interval, result);
// check if this is the last port
if (host->current_port->list.next == &host->hosttype->ports) {
host->current_port = NULL;
host->active = 0;
return -1;
}
// get next port
host->current_port = list_entry(host->current_port->list.next, struct sw_port, list);
host->current_value = 0;
// use "Now" as timespec in RRD commit
lbuf_clear(host->result);
lbuf_printf(host->result, "N");
}
}
char buf[128];
snprintf(buf, sizeof(buf), "%s.%s", host->current_port->porttype->oid_base[host->current_value], host->current_port->oid_ext);
// log_print(LOG_INFO, "polling: %-10s %-6s %2d => %s", host->name, host->current_port->name, host->current_value, buf);
oid oidx[MAX_OID_LEN];
size_t oid_len = MAX_OID_LEN;
if (!read_objid(buf, oidx, &oid_len)) {
log_snmp_error(host->session, "send_next_request(): read_objid()");
return -1;
}
struct snmp_pdu *req = snmp_pdu_create(SNMP_MSG_GET);
snmp_add_null_var(req, oidx, oid_len);
if (!snmp_send(host->session, req)) {
log_snmp_error(host->session, "send_next_request(): read_objid()");
snmp_free_pdu(req);
return -1;
}
return 0;
}
static const char * snmp_get_op_str(int operation)
{
switch (operation) {
case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE: return "OP_RECEIVED_MESSAGE";
case NETSNMP_CALLBACK_OP_TIMED_OUT: return "OP_TIMED_OUT";
case NETSNMP_CALLBACK_OP_SEND_FAILED: return "OP_SEND_FAILED";
case NETSNMP_CALLBACK_OP_CONNECT: return "OP_CONNECT";
case NETSNMP_CALLBACK_OP_DISCONNECT: return "OP_DISCONNECT";
default: return "<unknown>";
}
}
static int snmp_async_cb(int operation, struct snmp_session *sp, int reqid, struct snmp_pdu *pdu, void *magic)
{
struct sw_host *host = (struct sw_host *)magic;
if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
struct variable_list *vp = pdu->variables;
if (pdu->errstat == SNMP_ERR_NOERROR) {
// print_variable(vp->name, vp->name_length, vp);
switch (vp->type) {
case ASN_GAUGE:
case ASN_COUNTER:
lbuf_printf(host->result, ":%d", *vp->val.integer);
break;
case ASN_COUNTER64:
lbuf_printf(host->result, ":%lld", (uint64_t)vp->val.counter64->high * 0x100000000ULL + (uint64_t)vp->val.counter64->low);
break;
default:
lbuf_printf(host->result, ":U");
break;
}
} else {
lbuf_printf(host->result, ":U");
}
} else if (operation == NETSNMP_CALLBACK_OP_TIMED_OUT) {
log_print(LOG_WARN, "snmp_async_cb(%s): on host %s", snmp_get_op_str(operation), host->name);
host->current_port = NULL;
host->active = 0;
} else {
log_print(LOG_WARN, "snmp_async_cb(%s) on host %s", snmp_get_op_str(operation), host->name);
}
if (host->active)
send_next_request(host);
return 1;
}
static int do_snmp_poll(void *privdata)
{
struct sw_host *host;
list_for_each_entry(host, &host_list, list) {
if (host->active) {
log_print(LOG_WARN, "host '%s' still active", host->name);
continue;
}
host->active = 1;
send_next_request(host);
}
return 0;
}

124
snmp2rrd.c Normal file
View File

@ -0,0 +1,124 @@
/***************************************************************************
* Copyright (C) 12/2010 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include "configfile.h"
#include "event.h"
#include "logging.h"
#include "rrdtool.h"
#include "pidfile.h"
#define DEFAULT_CONFIG "snmp2rrd.conf"
#define DEFAULT_LOGFILE "snmp2rrd.log"
#define DEFAULT_PIDFILE "snmp2rrd.pid"
int snmp_init(void);
int snmp_pre_select_cb(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata);
int snmp_post_select_cb(int retval, void *readfds, void *writefds, void *privdata);
static struct option opts[] = {
{"config", 1, 0, 'c'},
{"debug", 0, 0, 'd'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
int main(int argc, char *argv[])
{
char *config = DEFAULT_CONFIG;
int code, arg = 0, debug = 0;
do {
code = getopt_long(argc, argv, "c:dh", opts, &arg);
switch (code) {
case 'c': /* config */
config = optarg;
break;
case 'd': /* debug */
debug = 1;
break;
case 'h': /* help */
printf("Usage: snmp2rrd [options]\n"
"Options: \n"
" --config -c configfile use this configfile\n"
" --debug -d do not fork and log to stderr\n"
" --help -h this help\n"
"\n");
exit(0);
break;
case '?': /* error */
exit(1);
break;
default: /* unknown / all options parsed */
break;
}
} while (code != -1);
/* parse config file */
if (config_parse(config) < 0)
exit(1);
if (!debug) {
/* check pidfile */
const char *pidfile = config_get_string("global", "pidfile", DEFAULT_PIDFILE);
if (pidfile_check(pidfile, 1) != 0) {
log_print(LOG_ERROR, "snmp2rrd already running");
exit(1);
}
/* start logging */
const char *logfile = config_get_string("global", "logfile", DEFAULT_LOGFILE);
if (log_init(logfile) < 0)
exit(1);
/* mutate to daemon */
if (daemon(-1, 0) < 0) {
log_print(LOG_ERROR, "failed to daemonize");
exit(1);
}
/* create pidfile */
if (pidfile_create(pidfile) < 0) {
log_print(LOG_ERROR, "failed to create pidfile %s", pidfile);
exit(1);
}
}
my_rrd_init();
snmp_init();
log_print(LOG_EVERYTIME, "snmp2rrd started (pid:%d)", getpid());
event_loop(snmp_pre_select_cb, snmp_post_select_cb, NULL);
log_close();
config_free();
return 0;
}

103
snmp2rrd.conf Normal file
View File

@ -0,0 +1,103 @@
[global]
logfile snmp2rrd.log
pidfile snmp2rrd.pid
rrd_dir rrd
interval 10
hosttype es2024
porttype es2024port
#host ES-2024,es2024,172.16.250.101:161,public
#host ES-2024A,es2024,172.16.250.102:161,public
#host GS-1524,es2024,172.16.250.103:161,public
#host XGS-4528,xgs4528,172.16.250.104:161,public
hosttype xgs4528
porttype xgs4528port
host Router01,xgs4528,172.16.250.1,public
host Router02,xgs4528,172.16.250.2,public
host Router03,xgs4528,172.16.250.10,public
host Router04,xgs4528,172.16.250.18,public
host Router05,xgs4528,172.16.250.26,public
host Router06,xgs4528,172.16.250.34,public
host Router07,xgs4528,172.16.250.42,public
host Router08,xgs4528,172.16.240.253,public
host Router09,xgs4528,172.16.240.251,public
host Router10,xgs4528,172.16.240.252,public
[hosttype_es2024]
port port01,es2024port,1
port port02,es2024port,2
port port03,es2024port,3
port port04,es2024port,4
port port05,es2024port,5
port port06,es2024port,6
port port07,es2024port,7
port port08,es2024port,8
port port09,es2024port,9
port port10,es2024port,10
port port11,es2024port,11
port port12,es2024port,12
port port13,es2024port,13
port port14,es2024port,14
port port15,es2024port,15
port port16,es2024port,16
port port17,es2024port,17
port port18,es2024port,18
port port19,es2024port,19
port port20,es2024port,20
port port21,es2024port,21
port port22,es2024port,22
port port23,es2024port,23
port port24,es2024port,24
[porttype_es2024port]
value IF-MIB::ifInOctets,DS:rx:COUNTER:90:0:U
value IF-MIB::ifOutOctets,DS:tx:COUNTER:90:0:U
value IF-MIB::ifSpeed,DS:speed:GAUGE:90:0:U
# 6h(10s) 2d(60s) 10d(5min)
rra RRA:MIN:0.5:1:2160 RRA:AVERAGE:0.5:1:2160 RRA:MAX:0.5:1:2160
rra RRA:MIN:0.5:6:2880 RRA:AVERAGE:0.5:6:2880 RRA:MAX:0.5:1:2880
rra RRA:MIN:0.5:30:2880 RRA:AVERAGE:0.5:30:2880 RRA:MAX:0.5:30:2880
[hosttype_xgs4528]
port port01,xgs4528port,1
port port02,xgs4528port,2
port port03,xgs4528port,3
port port04,xgs4528port,4
port port05,xgs4528port,5
port port06,xgs4528port,6
port port07,xgs4528port,7
port port08,xgs4528port,8
port port09,xgs4528port,9
port port10,xgs4528port,10
port port11,xgs4528port,11
port port12,xgs4528port,12
port port13,xgs4528port,13
port port14,xgs4528port,14
port port15,xgs4528port,15
port port16,xgs4528port,16
port port17,xgs4528port,17
port port18,xgs4528port,18
port port19,xgs4528port,19
port port20,xgs4528port,20
port port21,xgs4528port,21
port port22,xgs4528port,22
port port23,xgs4528port,23
port port24,xgs4528port,24
port port25,xgs4528port,25
port port26,xgs4528port,26
port port27,xgs4528port,27
port port28,xgs4528port,28
[porttype_xgs4528port]
value IF-MIB::ifHCInOctets,DS:rx:COUNTER:90:0:U
value IF-MIB::ifHCOutOctets,DS:tx:COUNTER:90:0:U
value IF-MIB::ifSpeed,DS:speed:GAUGE:90:0:U
# 6h(10s) 2d(60s) 10d(5min)
rra RRA:MIN:0.5:1:2160 RRA:AVERAGE:0.5:1:2160 RRA:MAX:0.5:1:2160
rra RRA:MIN:0.5:6:2880 RRA:AVERAGE:0.5:6:2880 RRA:MAX:0.5:1:2880
rra RRA:MIN:0.5:30:2880 RRA:AVERAGE:0.5:30:2880 RRA:MAX:0.5:30:2880

117
sockaddr.c Normal file
View File

@ -0,0 +1,117 @@
/***************************************************************************
* Copyright (C) 07/2007 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int parse_sockaddr(const char *addr, struct sockaddr_in *sa)
{
char *buf = strdup(addr);
if (buf == NULL)
return -1;
char *tmp;
char *ipstr = strtok_r(buf, ":", &tmp);
if (ipstr == NULL) {
free(buf);
return -2;
}
sa->sin_family = AF_INET;
if (inet_pton(AF_INET, ipstr, &sa->sin_addr) <= 0) {
free(buf);
return -3;
}
char *portstr = strtok_r(NULL, " \r\n", &tmp);
if (portstr == NULL) {
free(buf);
return -4;
}
int port = atoi(portstr);
if (port < 0 || port > 65535) {
free(buf);
return -5;
}
sa->sin_port = htons(port);
free(buf);
return 0;
}
int parse_subnet(const char *addr, struct in_addr *net, struct in_addr *mask)
{
char *buf = strdup(addr);
if (buf == NULL)
return -1;
char *tmp;
char *netstr = strtok_r(buf, "/", &tmp);
if (netstr == NULL) {
free(buf);
return -2;
}
if (inet_pton(AF_INET, netstr, net) <= 0) {
free(buf);
return -3;
}
char *maskstr = strtok_r(NULL, " \r\n", &tmp);
if (maskstr == NULL) {
mask->s_addr = ~0;
} else if (inet_pton(AF_INET, maskstr, mask) <= 0) {
int maskbits = atoi(maskstr);
if (maskbits < 0 || maskbits > 32) {
free(buf);
return -4;
}
mask->s_addr = htonl(~0 << (32 - maskbits));
}
free(buf);
return 0;
}
int get_sockaddr(char *buf, int size, struct sockaddr_in *addr)
{
return snprintf(buf, size, "%s:%d", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
char * get_sockaddr_buf(struct sockaddr_in *addr)
{
static char ret[24];
get_sockaddr(ret, sizeof(ret), addr);
return ret;
}
int same_sockaddr(struct sockaddr_in *a, struct sockaddr_in *b)
{
return !((a->sin_family ^ b->sin_family) |
(a->sin_addr.s_addr ^ b->sin_addr.s_addr) |
(a->sin_port ^ b->sin_port));
}

14
sockaddr.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef _SOCKADDR_H_
#define _SOCKADDR_H_
#include <netinet/in.h>
int parse_sockaddr(const char *addr, struct sockaddr_in *sa);
int parse_subnet(const char *addr, struct in_addr *net, struct in_addr *mask);
int get_sockaddr(char *buf, int size, struct sockaddr_in *addr);
char * get_sockaddr_buf(struct sockaddr_in *addr);
int same_sockaddr(struct sockaddr_in *a, struct sockaddr_in *b);
#endif /* _SOCKADDR_H_ */