initial commit
This commit is contained in:
commit
84d2bc1014
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
build
|
||||||
|
snmp2rrd
|
||||||
|
snmp2rrd.pid
|
||||||
|
snmp2rrd.log
|
52
Makefile
Normal file
52
Makefile
Normal 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
288
configfile.c
Normal 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(§ion->list);
|
||||||
|
INIT_LIST_HEAD(§ion->tupel_list);
|
||||||
|
|
||||||
|
section->name = strdup(name);
|
||||||
|
if (section->name == NULL) {
|
||||||
|
free(section);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(§ion->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, §ion->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, §ion->tupel_list, list) {
|
||||||
|
list_del(&tupel->list);
|
||||||
|
free((char *)tupel->option);
|
||||||
|
free((char *)tupel->parameter);
|
||||||
|
free(tupel);
|
||||||
|
}
|
||||||
|
list_del(§ion->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, §ion->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, §ion->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, §ion->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
28
configfile.h
Normal 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
310
event.c
Normal 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
44
event.h
Normal 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
239
linebuffer.c
Normal 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
27
linebuffer.h
Normal 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
268
list.h
Normal 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
114
logging.c
Normal 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
23
logging.h
Normal 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
73
pidfile.c
Normal 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
11
pidfile.h
Normal 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
228
rrdtool.c
Normal 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
7
rrdtool.h
Normal 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
499
snmp.c
Normal 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
124
snmp2rrd.c
Normal 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
103
snmp2rrd.conf
Normal 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
117
sockaddr.c
Normal 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
14
sockaddr.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user