chiark / gitweb /
wip split into multiple files and make compile
[innduct.git] / help.c
diff --git a/help.c b/help.c
new file mode 100644 (file)
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 <ijackson@chiark.greenend.org.uk>
+ * 
+ *  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 <http://www.gnu.org/licenses/>.
+ *
+ *  (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<endp) ? *p++ : 0;
+    if (!c) { *q++= '\''; *q=0; break; }
+    if (c>=' ' && c<=126 && c!='\\') { *q++= c; continue; }
+    sprintf(q,"\\x%02x",c);
+    q += 4;
+  }
+  return sanibuf;
+}