#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <signal.h>
#include <string.h>
#include "notifyd.h"
#include "exitcodes.h"
#include "global.h"
#include "libconfig.h"
#include "xmalloc.h"
const int config_need_data = 0;
static int soc = 0;
static notifymethod_t *default_method;
void shut_down(int code) __attribute__ ((noreturn));
void shut_down(int code)
{
cyrus_done();
exit(code);
}
char *fetch_arg(char *head, char* tail)
{
char *cp;
for (cp = head; *cp && cp < tail; cp++);
return (cp == tail ? NULL : cp + 1);
}
#define NOTIFY_MAXSIZE 8192
int do_notify()
{
struct sockaddr_un sun_data;
socklen_t sunlen = sizeof(sun_data);
char buf[NOTIFY_MAXSIZE+1], *cp, *tail;
int r, i;
char *method, *class, *priority, *user, *mailbox, *message;
char **options = NULL;
long nopt;
char *reply;
notifymethod_t *nmethod;
while (1) {
method = class = priority = user = mailbox = message = reply = NULL;
nopt = 0;
if (signals_poll() == SIGHUP) {
return 0;
}
r = recvfrom(soc, buf, NOTIFY_MAXSIZE, 0,
(struct sockaddr *) &sun_data, &sunlen);
if (r == -1) {
return (errno);
}
buf[r] = '\0';
tail = buf + r - 1;
method = (cp = buf);
if (cp) class = (cp = fetch_arg(cp, tail));
if (cp) priority = (cp = fetch_arg(cp, tail));
if (cp) user = (cp = fetch_arg(cp, tail));
if (cp) mailbox = (cp = fetch_arg(cp, tail));
if (cp) cp = fetch_arg(cp, tail);
if (cp) nopt = strtol(cp, NULL, 10);
if (nopt < 0 || errno == ERANGE) cp = NULL;
if (cp && nopt &&
!(options = (char**) xrealloc(options, nopt * sizeof(char*)))) {
fatal("xmalloc(): can't allocate options", EC_OSERR);
}
for (i = 0; cp && i < nopt; i++) {
options[i] = (cp = fetch_arg(cp, tail));
}
if (cp) message = (cp = fetch_arg(cp, tail));
if (!message) {
syslog(LOG_ERR, "malformed notify request");
return 0;
}
if (!*method)
nmethod = default_method;
else {
nmethod = methods;
while (nmethod->name) {
if (!strcasecmp(nmethod->name, method)) break;
nmethod++;
}
}
syslog(LOG_DEBUG, "do_notify using method '%s'",
nmethod->name ? nmethod->name: "unknown");
if (nmethod->name) {
reply = nmethod->notify(class, priority, user, mailbox,
nopt, options, message);
}
#if 0
else {
reply = strdup("NO unknown notification method");
if (!reply) {
fatal("strdup failed", EC_OSERR);
}
}
#endif
if (reply) free(reply);
}
}
void fatal(const char *s, int code)
{
static int recurse_code = 0;
if (recurse_code) {
exit(recurse_code);
}
recurse_code = code;
syslog(LOG_ERR, "Fatal error: %s", s);
shut_down(code);
}
void printstring(const char *s __attribute__((unused)))
{
fatal("printstring() executed, but its not used for notifyd!",
EC_SOFTWARE);
}
void usage(void)
{
syslog(LOG_ERR, "usage: notifyd [-C <alt_config>]");
exit(EC_USAGE);
}
int service_init(int argc, char **argv, char **envp __attribute__((unused)))
{
int opt;
char *method = "null";
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
while ((opt = getopt(argc, argv, "m:")) != EOF) {
switch(opt) {
case 'm':
method = optarg;
break;
default:
usage();
}
}
default_method = methods;
while (default_method->name) {
if (!strcasecmp(default_method->name, method)) break;
default_method++;
}
if (!default_method) fatal("unknown notification method %s", EC_USAGE);
signals_set_shutdown(&shut_down);
return 0;
}
void service_abort(int error)
{
shut_down(error);
}
int service_main(int argc __attribute__((unused)),
char **argv __attribute__((unused)),
char **envp __attribute__((unused)))
{
int r = 0;
r = do_notify();
shut_down(r);
return 0;
}