X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;ds=sidebyside;f=help.c;fp=help.c;h=144ab86f7298f9c2a2689534ab86855ce18755a8;hb=f4aee95c41a0d6231d115386b8fbb23f6b8e349a;hp=0000000000000000000000000000000000000000;hpb=90d4051c0ca3db621c23331beb9c27ea51702948;p=innduct.git diff --git a/help.c b/help.c new file mode 100644 index 0000000..144ab86 --- /dev/null +++ b/help.c @@ -0,0 +1,284 @@ +/* + * innduct + * tailing reliable realtime streaming feeder for inn + * logging and utility functions + * + * Copyright (C) 2010 Ian Jackson + * + * 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 3 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, see . + * + * (I believe that when you compile and link this as part of the inn2 + * build, with the Makefile runes I have provided, all the libraries + * and files which end up included in innduct are licence-compatible + * with GPLv3. If not then please let me know. -Ian Jackson.) + */ + +#include "innduct.h" + + +/* for logging, simulation, debugging, etc. */ +int simulate_flush= -1; +int logv_use_syslog; +const char *logv_prefix=""; + +/*========== logging ==========*/ + +static void logcore(int sysloglevel, const char *fmt, ...) PRINTF(2,3); +static void logcore(int sysloglevel, const char *fmt, ...) { + VA; + if (logv_use_syslog) { + vsyslog(sysloglevel,fmt,al); + } else { + if (self_pid) fprintf(stderr,"[%lu] ",(unsigned long)self_pid); + vfprintf(stderr,fmt,al); + putc('\n',stderr); + } + va_end(al); +} + +void logv(int sysloglevel, const char *pfx, int errnoval, + const char *fmt, va_list al) { + char msgbuf[1024]; /* NB do not call xvasprintf here or you'll recurse */ + vsnprintf(msgbuf,sizeof(msgbuf), fmt,al); + msgbuf[sizeof(msgbuf)-1]= 0; + + if (sysloglevel >= LOG_ERR && (errnoval==EACCES || errnoval==EPERM)) + sysloglevel= LOG_ERR; /* run by wrong user, probably */ + + logcore(sysloglevel, "%s%s: %s%s%s", + logv_prefix, pfx, msgbuf, + errnoval>=0 ? ": " : "", + errnoval>=0 ? strerror(errnoval) : ""); +} + +#define DEFFATAL(fn, pfx, sysloglevel, err, estatus) \ + void fn(const char *fmt, ...) { \ + preterminate(); \ + VA; \ + logv(sysloglevel, pfx, err, fmt, al); \ + exit(estatus); \ + } + +#define DEFLOG(fn, pfx, sysloglevel, err) \ + static void fn(const char *fmt, ...) { \ + VA; \ + logv(sysloglevel, pfx, err, fmt, al); \ + va_end(al); \ + } + +#define INNLOGSET_DECLARE(fn, pfx, sysloglevel) \ + static void duct_log_##fn(int l, const char *fmt, va_list al, int errval) \ + PRINTF(3,0); \ + static void duct_log_##fn(int l, const char *fmt, va_list al, int errval) { \ + logv(sysloglevel, pfx, errval ? errval : -1, fmt, al); \ + } +#define INNLOGSET_CALL(fn, pfx, sysloglevel) \ + message_handlers_##fn(1, duct_log_##fn); + + +static int innduct_fatal_cleanup(void) { return 12; } /* used for libinn die */ + +/* We want to extend the set of logging functions from inn, and we + * want to prepend the site name to all our messages. */ + +DEFFATAL(syscrash, "critical", LOG_CRIT, errno, 16); +DEFFATAL(crash, "critical", LOG_CRIT, -1, 16); + +#define INNLOGSETS(INNLOGSET) \ + INNLOGSET(die, "fatal", LOG_ERR) \ + INNLOGSET(warn, "warning", LOG_WARNING) \ + INNLOGSET(notice, "notice", LOG_NOTICE) \ + INNLOGSET(trace, "trace", LOG_NOTICE) +INNLOGSETS(INNLOGSET_DECLARE) + +DEFLOG(info, "info", LOG_INFO, -1) +DEFLOG(dbg, "debug", LOG_DEBUG, -1) + + +/*========== utility functions etc. ==========*/ + +char *xvasprintf(const char *fmt, va_list al) { + char *str; + int rc= vasprintf(&str,fmt,al); + if (rc<0) sysdie("vasprintf(\"%s\",...) failed", fmt); + return str; +} + +char *xasprintf(const char *fmt, ...) { + VA; + char *str= xvasprintf(fmt,al); + va_end(al); + return str; +} + +int close_perhaps(int *fd) { + if (*fd <= 0) return 0; + int r= close(*fd); + *fd=0; + return r; +} +void xclose(int fd, const char *what, const char *what2) { + int r= close(fd); + if (r) syscrash("close %s%s",what,what2?what2:""); +} +void xclose_perhaps(int *fd, const char *what, const char *what2) { + if (*fd <= 0) return; + xclose(*fd,what,what2); + *fd=0; +} + +pid_t xfork(const char *what) { + pid_t child; + + child= fork(); + if (child==-1) sysdie("cannot fork for %s",what); + dbg("forked %s %ld", what, (unsigned long)child); + if (!child) postfork(); + return child; +} + +void on_fd_read_except(int fd, oop_call_fd callback) { + loop->on_fd(loop, fd, OOP_READ, callback, 0); + loop->on_fd(loop, fd, OOP_EXCEPTION, callback, 0); +} +void cancel_fd_read_except(int fd) { + loop->cancel_fd(loop, fd, OOP_READ); + loop->cancel_fd(loop, fd, OOP_EXCEPTION); +} + +void report_child_status(const char *what, int status) { + if (WIFEXITED(status)) { + int es= WEXITSTATUS(status); + if (es) + warn("%s: child died with error exit status %d", what, es); + } else if (WIFSIGNALED(status)) { + int sig= WTERMSIG(status); + const char *sigstr= strsignal(sig); + const char *coredump= WCOREDUMP(status) ? " (core dumped)" : ""; + if (sigstr) + warn("%s: child died due to fatal signal %s%s", what, sigstr, coredump); + else + warn("%s: child died due to unknown fatal signal %d%s", + what, sig, coredump); + } else { + warn("%s: child died with unknown wait status %d", what,status); + } +} + +int xwaitpid(pid_t *pid, const char *what) { + int status; + + int r= kill(*pid, SIGKILL); + if (r) syscrash("cannot kill %s child", what); + + pid_t got= waitpid(*pid, &status, 0); + if (got==-1) syscrash("cannot reap %s child", what); + if (got==0) crash("cannot reap %s child", what); + + *pid= 0; + + return status; +} + +void *zxmalloc(size_t sz) { + void *p= xmalloc(sz); + memset(p,0,sz); + return p; +} + +void xunlink(const char *path, const char *what) { + int r= unlink(path); + if (r) syscrash("can't unlink %s %s", path, what); +} + +time_t xtime(void) { + time_t now= time(0); + if (now==-1) syscrash("time(2) failed"); + return now; +} + +void xsigaction(int signo, const struct sigaction *sa) { + int r= sigaction(signo,sa,0); + if (r) syscrash("sigaction failed for \"%s\"", strsignal(signo)); +} +void xsigsetdefault(int signo) { + struct sigaction sa; + memset(&sa,0,sizeof(sa)); + sa.sa_handler= SIG_DFL; + xsigaction(signo,&sa); +} + +void xgettimeofday(struct timeval *tv_r) { + int r= gettimeofday(tv_r,0); + if (r) syscrash("gettimeofday(2) failed"); +} +void xsetnonblock(int fd, int nonb) { + int errnoval= oop_fd_nonblock(fd, nonb); + if (errnoval) { errno= errnoval; syscrash("setnonblocking"); } +} + +void check_isreg(const struct stat *stab, const char *path, + const char *what) { + if (!S_ISREG(stab->st_mode)) + crash("%s %s not a plain file (mode 0%lo)", + what, path, (unsigned long)stab->st_mode); +} + +static void xfstat(int fd, struct stat *stab_r, const char *what) { + int r= fstat(fd, stab_r); + if (r) syscrash("could not fstat %s", what); +} + +static void xfstat_isreg(int fd, struct stat *stab_r, + const char *path, const char *what) { + xfstat(fd, stab_r, what); + check_isreg(stab_r, path, what); +} + +void xlstat_isreg(const char *path, struct stat *stab, + int *enoent_r /* 0 means ENOENT is fatal */, + const char *what) { + int r= lstat(path, stab); + if (r) { + if (errno==ENOENT && enoent_r) { *enoent_r=1; return; } + syscrash("could not lstat %s %s", what, path); + } + if (enoent_r) *enoent_r= 0; + check_isreg(stab, path, what); +} + +int samefile(const struct stat *a, const struct stat *b) { + assert(S_ISREG(a->st_mode)); + assert(S_ISREG(b->st_mode)); + return (a->st_ino == b->st_ino && + a->st_dev == b->st_dev); +} + +char *sanitise(const char *input, int len) { + static char sanibuf[100]; /* returns pointer to this buffer! */ + + const char *p= input; + const char *endp= len>=0 ? input+len : 0; + char *q= sanibuf; + *q++= '`'; + for (;;) { + if (q > sanibuf+sizeof(sanibuf)-8) { strcpy(q,"'.."); break; } + int c= (!endp || p=' ' && c<=126 && c!='\\') { *q++= c; continue; } + sprintf(q,"\\x%02x",c); + q += 4; + } + return sanibuf; +}