/*
* This plugin communicates with a spam filter, a virus scanner or similar
* software using a UNIX socket and a very simple protocol:
- *
+ *
* e-mail type (e.g. ham, spam, virus, ...) and size
* e:<type>:<bytes>
*
#include "common.h"
#include "plugin.h"
-#include <pthread.h>
+#include "configfile.h"
+
+#if HAVE_LIBPTHREAD
+# include <pthread.h>
+# define EMAIL_HAVE_READ 1
+#else
+# define EMAIL_HAVE_READ 0
+#endif
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
# include <sys/un.h>
#endif /* HAVE_LINUX_UN_H | HAVE_SYS_UN_H */
+/* some systems (e.g. Darwin) seem to not define UNIX_PATH_MAX at all */
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif /* UNIX_PATH_MAX */
+
+#if HAVE_GRP_H
+# include <grp.h>
+#endif /* HAVE_GRP_H */
+
#define MODULE_NAME "email"
/* 256 bytes ought to be enough for anybody ;-) */
#define BUFSIZE 256
-#define SOCK_PATH "/tmp/.collectd-email"
+#ifndef COLLECTD_SOCKET_PREFIX
+# define COLLECTD_SOCKET_PREFIX "/tmp/.collectd-"
+#endif /* COLLECTD_SOCKET_PREFIX */
+
+#define SOCK_PATH COLLECTD_SOCKET_PREFIX"email"
#define MAX_CONNS 5
+#define MAX_CONNS_LIMIT 16384
+/*
+ * Private data structures
+ */
+#if EMAIL_HAVE_READ
/* linked list of email and check types */
typedef struct type {
char *name;
collector_t *head;
collector_t *tail;
} collector_list_t;
+#endif /* EMAIL_HAVE_READ */
+
+/*
+ * Private variables
+ */
+#if EMAIL_HAVE_READ
+/* valid configuration file keys */
+static char *config_keys[] =
+{
+ "SocketGroup",
+ "SocketPerms",
+ "MaxConns",
+ NULL
+};
+static int config_keys_num = 3;
+
+/* socket configuration */
+static char *sock_group = COLLECTD_GRP_NAME;
+static int sock_perms = S_IRWXU | S_IRWXG;
+static int max_conns = MAX_CONNS;
/* state of the plugin */
static int disabled = 0;
/* thread managing "client" connections */
static pthread_t connector;
+static int connector_socket;
/* tell the connector thread that a collector is available */
static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t available_mutex = PTHREAD_MUTEX_INITIALIZER;
static collector_list_t available;
+static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t count;
+
+static pthread_mutex_t size_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t size;
+
+static pthread_mutex_t score_mutex = PTHREAD_MUTEX_INITIALIZER;
+static double score;
+static int score_count;
+
+static pthread_mutex_t check_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t check;
+#endif /* EMAIL_HAVE_READ */
+
#define COUNT_FILE "email/email-%s.rrd"
static char *count_ds_def[] =
{
};
static int count_ds_num = 1;
-static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t count;
-
#define SIZE_FILE "email/email_size-%s.rrd"
-static char *size_ds_def[] =
+static char *size_ds_def[] =
{
"DS:size:GAUGE:"COLLECTD_HEARTBEAT":0:U",
NULL
};
static int size_ds_num = 1;
-static pthread_mutex_t size_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t size;
-
#define SCORE_FILE "email/spam_score.rrd"
static char *score_ds_def[] =
{
};
static int score_ds_num = 1;
-static pthread_mutex_t score_mutex = PTHREAD_MUTEX_INITIALIZER;
-static double score;
-static int score_count;
-
#define CHECK_FILE "email/spam_check-%s.rrd"
static char *check_ds_def[] =
{
};
static int check_ds_num = 1;
-static pthread_mutex_t check_mutex = PTHREAD_MUTEX_INITIALIZER;
-static type_list_t check;
+#if EMAIL_HAVE_READ
+static int email_config (char *key, char *value)
+{
+ if (0 == strcasecmp (key, "SocketGroup")) {
+ sock_group = sstrdup (value);
+ }
+ else if (0 == strcasecmp (key, "SocketPerms")) {
+ /* the user is responsible for providing reasonable values */
+ sock_perms = (int)strtol (value, NULL, 8);
+ }
+ else if (0 == strcasecmp (key, "MaxConns")) {
+ long int tmp = strtol (value, NULL, 0);
+
+ if (tmp < 1) {
+ fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+ "value %li, will use default %i.\n",
+ tmp, MAX_CONNS);
+ max_conns = MAX_CONNS;
+ }
+ else if (tmp > MAX_CONNS_LIMIT) {
+ fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+ "value %li, will use hardcoded limit %i.\n",
+ tmp, MAX_CONNS_LIMIT);
+ max_conns = MAX_CONNS_LIMIT;
+ }
+ else {
+ max_conns = (int)tmp;
+ }
+ }
+ else {
+ return -1;
+ }
+ return 0;
+} /* static int email_config (char *, char *) */
/* Increment the value of the given name in the given list by incr. */
static void type_list_incr (type_list_t *list, char *name, int incr)
break;
}
- if ('\n' != src->buffer[i]) {
+ if (i == src->idx) {
fd_set fdset;
ssize_t len = 0;
do {
errno = 0;
- if (0 > (len = read (src->socket,
- (void *)(&(src->buffer[0]) + src->idx),
+ if (0 > (len = read (src->socket,
+ (void *)(&(src->buffer[0]) + src->idx),
BUFSIZE - src->idx))) {
if (EINTR != errno) {
syslog (LOG_ERR, "read() failed: %s", strerror (errno));
break;
}
- if ('\n' != src->buffer[i]) {
+ if (i == src->idx) {
ret = (char *)smalloc (1);
ret[0] = '\0';
}
if ('e' == line[0]) { /* e:<type>:<bytes> */
- char *type = strtok (line + 2, ":");
- char *tmp = strtok (NULL, ":");
+ char *ptr = NULL;
+ char *type = strtok_r (line + 2, ":", &ptr);
+ char *tmp = strtok_r (NULL, ":", &ptr);
int bytes = 0;
if (NULL == tmp) {
}
else if ('s' == line[0]) { /* s:<value> */
pthread_mutex_lock (&score_mutex);
- score = (score * (double)score_count + atof (line + 2))
+ score = (score * (double)score_count + atof (line + 2))
/ (double)(score_count + 1);
++score_count;
pthread_mutex_unlock (&score_mutex);
}
else if ('c' == line[0]) { /* c:<type1>[,<type2>,...] */
- char *type = strtok (line + 2, ",");
+ char *ptr = NULL;
+ char *type = strtok_r (line + 2, ",", &ptr);
do {
pthread_mutex_lock (&check_mutex);
type_list_incr (&check, type, 1);
pthread_mutex_unlock (&check_mutex);
- } while (NULL != (type = strtok (NULL, ",")));
+ } while (NULL != (type = strtok_r (NULL, ",", &ptr)));
}
else {
syslog (LOG_ERR, "email: unknown type '%c'", line[0]);
static void *open_connection (void *arg)
{
- int local = 0;
-
struct sockaddr_un addr;
/* create UNIX socket */
errno = 0;
- if (-1 == (local = socket (PF_UNIX, SOCK_STREAM, 0))) {
+ if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
disabled = 1;
syslog (LOG_ERR, "socket() failed: %s", strerror (errno));
pthread_exit ((void *)1);
unlink (addr.sun_path);
errno = 0;
- if (-1 == bind (local, (struct sockaddr *)&addr,
- offsetof (struct sockaddr_un, sun_path)
+ if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
+ offsetof (struct sockaddr_un, sun_path)
+ strlen(addr.sun_path))) {
disabled = 1;
syslog (LOG_ERR, "bind() failed: %s", strerror (errno));
}
errno = 0;
- if (-1 == listen (local, 5)) {
+ if (-1 == listen (connector_socket, 5)) {
disabled = 1;
syslog (LOG_ERR, "listen() failed: %s", strerror (errno));
pthread_exit ((void *)1);
}
+ if ((uid_t)0 == geteuid ()) {
+ struct group *grp;
+
+ errno = 0;
+ if (NULL != (grp = getgrnam (sock_group))) {
+ errno = 0;
+ if (0 != chown (SOCK_PATH, (uid_t)-1, grp->gr_gid)) {
+ syslog (LOG_WARNING, "chown() failed: %s", strerror (errno));
+ }
+ }
+ else {
+ syslog (LOG_WARNING, "getgrnam() failed: %s", strerror (errno));
+ }
+ }
+ else {
+ syslog (LOG_WARNING, "not running as root");
+ }
+
+ errno = 0;
+ if (0 != chmod (SOCK_PATH, sock_perms)) {
+ syslog (LOG_WARNING, "chmod() failed: %s", strerror (errno));
+ }
+
{ /* initialize queue of available threads */
int i = 0;
last = available.head;
- for (i = 1; i < MAX_CONNS; ++i) {
+ for (i = 1; i < max_conns; ++i) {
last->next = (collector_t *)smalloc (sizeof (collector_t));
last = last->next;
available.tail = last;
do {
errno = 0;
- if (-1 == (remote = accept (local, NULL, NULL))) {
+ if (-1 == (remote = accept (connector_socket, NULL, NULL))) {
if (EINTR != errno) {
disabled = 1;
syslog (LOG_ERR, "accept() failed: %s", strerror (errno));
pthread_attr_init (&ptattr);
pthread_attr_setdetachstate (&ptattr, PTHREAD_CREATE_DETACHED);
- if (0 == (err = pthread_create (&collector->thread, &ptattr, collect,
+ if (0 == (err = pthread_create (&collector->thread, &ptattr, collect,
(void *)collector))) {
pthread_mutex_lock (&active_mutex);
}
pthread_exit ((void *)0);
} /* void *open_connection (void *) */
+#endif /* EMAIL_HAVE_READ */
static void email_init (void)
{
+#if EMAIL_HAVE_READ
int err = 0;
- if (0 != (err = pthread_create (&connector, NULL,
+ if (0 != (err = pthread_create (&connector, NULL,
open_connection, NULL))) {
disabled = 1;
syslog (LOG_ERR, "pthread_create() failed: %s", strerror (err));
return;
}
+#endif /* EMAIL_HAVE_READ */
return;
} /* static void email_init (void) */
+#if EMAIL_HAVE_READ
+static void email_shutdown (void)
+{
+ collector_t *ptr;
+
+ if (disabled)
+ return;
+
+ close (connector_socket);
+ pthread_kill (connector, SIGTERM);
+
+ pthread_mutex_lock (&active_mutex);
+
+ for (ptr = active.head; NULL != ptr; ptr = ptr->next) {
+ close (ptr->socket);
+ pthread_kill (ptr->thread, SIGTERM);
+ }
+
+ pthread_mutex_unlock (&active_mutex);
+
+ unlink (SOCK_PATH);
+ return;
+} /* static void email_shutdown (void) */
+#endif /* EMAIL_HAVE_READ */
+
static void count_write (char *host, char *inst, char *val)
{
char file[BUFSIZE] = "";
return;
} /* static void check_write (char *host, char *inst, char *val) */
+#if EMAIL_HAVE_READ
static void type_submit (char *plugin, char *inst, int value)
{
char buf[BUFSIZE] = "";
return;
plugin_submit (plugin, inst, buf);
-fprintf(stderr, "plugin_submit (\"%s\", \"%s\", \"%s\")\n", plugin, inst, buf);
return;
} /* static void type_submit (char *, char *, int) */
return;
plugin_submit ("email_spam_score", NULL, buf);
-fprintf(stderr, "plugin_submit (\"%s\", \"%s\", \"%s\")\n", "email_spam_score", "\0", buf);
return;
}
pthread_mutex_unlock (&check_mutex);
return;
} /* static void read (void) */
+#else /* if !EMAIL_HAVE_READ */
+# define email_read NULL
+#endif
void module_register (void)
{
plugin_register ("email_size", NULL, NULL, size_write);
plugin_register ("email_spam_score", NULL, NULL, score_write);
plugin_register ("email_spam_check", NULL, NULL, check_write);
+#if EMAIL_HAVE_READ
+ plugin_register_shutdown (MODULE_NAME, email_shutdown);
+ cf_register (MODULE_NAME, email_config, config_keys, config_keys_num);
+#endif /* EMAIL_HAVE_READ */
return;
} /* void module_register (void) */