chiark / gitweb /
144ab86f7298f9c2a2689534ab86855ce18755a8
[inn-innduct.git] / help.c
1 /*
2  *  innduct
3  *  tailing reliable realtime streaming feeder for inn
4  *  logging and utility functions
5  *
6  *  Copyright (C) 2010 Ian Jackson <ijackson@chiark.greenend.org.uk>
7  * 
8  *  This program is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  * 
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  * 
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  *  (I believe that when you compile and link this as part of the inn2
22  *  build, with the Makefile runes I have provided, all the libraries
23  *  and files which end up included in innduct are licence-compatible
24  *  with GPLv3.  If not then please let me know.  -Ian Jackson.)
25  */
26
27 #include "innduct.h"
28
29
30 /* for logging, simulation, debugging, etc. */
31 int simulate_flush= -1;
32 int logv_use_syslog;
33 const char *logv_prefix="";
34
35 /*========== logging ==========*/
36
37 static void logcore(int sysloglevel, const char *fmt, ...) PRINTF(2,3);
38 static void logcore(int sysloglevel, const char *fmt, ...) {
39   VA;
40   if (logv_use_syslog) {
41     vsyslog(sysloglevel,fmt,al);
42   } else {
43     if (self_pid) fprintf(stderr,"[%lu] ",(unsigned long)self_pid);
44     vfprintf(stderr,fmt,al);
45     putc('\n',stderr);
46   }
47   va_end(al);
48 }
49
50 void logv(int sysloglevel, const char *pfx, int errnoval,
51           const char *fmt, va_list al) {
52   char msgbuf[1024]; /* NB do not call xvasprintf here or you'll recurse */
53   vsnprintf(msgbuf,sizeof(msgbuf), fmt,al);
54   msgbuf[sizeof(msgbuf)-1]= 0;
55
56   if (sysloglevel >= LOG_ERR && (errnoval==EACCES || errnoval==EPERM))
57     sysloglevel= LOG_ERR; /* run by wrong user, probably */
58
59   logcore(sysloglevel, "%s%s: %s%s%s",
60           logv_prefix, pfx, msgbuf,
61           errnoval>=0 ? ": " : "",
62           errnoval>=0 ? strerror(errnoval) : "");
63 }
64
65 #define DEFFATAL(fn, pfx, sysloglevel, err, estatus)    \
66   void fn(const char *fmt, ...) {                       \
67     preterminate();                                     \
68     VA;                                                 \
69     logv(sysloglevel, pfx, err, fmt, al);               \
70     exit(estatus);                                      \
71   }
72
73 #define DEFLOG(fn, pfx, sysloglevel, err)               \
74   static void fn(const char *fmt, ...) {                \
75     VA;                                                 \
76     logv(sysloglevel, pfx, err, fmt, al);               \
77     va_end(al);                                         \
78   }
79
80 #define INNLOGSET_DECLARE(fn, pfx, sysloglevel)                               \
81   static void duct_log_##fn(int l, const char *fmt, va_list al, int errval)   \
82      PRINTF(3,0);                                                             \
83   static void duct_log_##fn(int l, const char *fmt, va_list al, int errval) { \
84     logv(sysloglevel, pfx, errval ? errval : -1, fmt, al);                    \
85   }
86 #define INNLOGSET_CALL(fn, pfx, sysloglevel)    \
87   message_handlers_##fn(1, duct_log_##fn);
88
89
90 static int innduct_fatal_cleanup(void) { return 12; } /* used for libinn die */
91
92 /* We want to extend the set of logging functions from inn, and we
93  * want to prepend the site name to all our messages. */
94
95 DEFFATAL(syscrash,    "critical", LOG_CRIT,    errno, 16);
96 DEFFATAL(crash,       "critical", LOG_CRIT,    -1,    16);
97
98 #define INNLOGSETS(INNLOGSET)                   \
99   INNLOGSET(die,      "fatal",    LOG_ERR)      \
100   INNLOGSET(warn,     "warning",  LOG_WARNING)  \
101   INNLOGSET(notice,   "notice",   LOG_NOTICE)   \
102   INNLOGSET(trace,    "trace",    LOG_NOTICE)
103 INNLOGSETS(INNLOGSET_DECLARE)
104
105 DEFLOG(info,          "info",     LOG_INFO,    -1)
106 DEFLOG(dbg,           "debug",    LOG_DEBUG,   -1)
107
108
109 /*========== utility functions etc. ==========*/
110
111 char *xvasprintf(const char *fmt, va_list al) {
112   char *str;
113   int rc= vasprintf(&str,fmt,al);
114   if (rc<0) sysdie("vasprintf(\"%s\",...) failed", fmt);
115   return str;
116 }
117
118 char *xasprintf(const char *fmt, ...) {
119   VA;
120   char *str= xvasprintf(fmt,al);
121   va_end(al);
122   return str;
123 }
124
125 int close_perhaps(int *fd) {
126   if (*fd <= 0) return 0;
127   int r= close(*fd);
128   *fd=0;
129   return r;
130 }
131 void xclose(int fd, const char *what, const char *what2) {
132   int r= close(fd);
133   if (r) syscrash("close %s%s",what,what2?what2:"");
134 }
135 void xclose_perhaps(int *fd, const char *what, const char *what2) {
136   if (*fd <= 0) return;
137   xclose(*fd,what,what2);
138   *fd=0;
139 }
140
141 pid_t xfork(const char *what) {
142   pid_t child;
143
144   child= fork();
145   if (child==-1) sysdie("cannot fork for %s",what);
146   dbg("forked %s %ld", what, (unsigned long)child);
147   if (!child) postfork();
148   return child;
149 }
150
151 void on_fd_read_except(int fd, oop_call_fd callback) {
152   loop->on_fd(loop, fd, OOP_READ,      callback, 0);
153   loop->on_fd(loop, fd, OOP_EXCEPTION, callback, 0);
154 }
155 void cancel_fd_read_except(int fd) {
156   loop->cancel_fd(loop, fd, OOP_READ);
157   loop->cancel_fd(loop, fd, OOP_EXCEPTION);
158 }
159
160 void report_child_status(const char *what, int status) {
161   if (WIFEXITED(status)) {
162     int es= WEXITSTATUS(status);
163     if (es)
164       warn("%s: child died with error exit status %d", what, es);
165   } else if (WIFSIGNALED(status)) {
166     int sig= WTERMSIG(status);
167     const char *sigstr= strsignal(sig);
168     const char *coredump= WCOREDUMP(status) ? " (core dumped)" : "";
169     if (sigstr)
170       warn("%s: child died due to fatal signal %s%s", what, sigstr, coredump);
171     else
172       warn("%s: child died due to unknown fatal signal %d%s",
173            what, sig, coredump);
174   } else {
175     warn("%s: child died with unknown wait status %d", what,status);
176   }
177 }
178
179 int xwaitpid(pid_t *pid, const char *what) {
180   int status;
181
182   int r= kill(*pid, SIGKILL);
183   if (r) syscrash("cannot kill %s child", what);
184
185   pid_t got= waitpid(*pid, &status, 0);
186   if (got==-1) syscrash("cannot reap %s child", what);
187   if (got==0) crash("cannot reap %s child", what);
188
189   *pid= 0;
190
191   return status;
192 }
193
194 void *zxmalloc(size_t sz) {
195   void *p= xmalloc(sz);
196   memset(p,0,sz);
197   return p;
198 }
199
200 void xunlink(const char *path, const char *what) {
201   int r= unlink(path);
202   if (r) syscrash("can't unlink %s %s", path, what);
203 }
204
205 time_t xtime(void) {
206   time_t now= time(0);
207   if (now==-1) syscrash("time(2) failed");
208   return now;
209 }
210
211 void xsigaction(int signo, const struct sigaction *sa) {
212   int r= sigaction(signo,sa,0);
213   if (r) syscrash("sigaction failed for \"%s\"", strsignal(signo));
214 }
215 void xsigsetdefault(int signo) {
216   struct sigaction sa;
217   memset(&sa,0,sizeof(sa));
218   sa.sa_handler= SIG_DFL;
219   xsigaction(signo,&sa);
220 }
221
222 void xgettimeofday(struct timeval *tv_r) {
223   int r= gettimeofday(tv_r,0);
224   if (r) syscrash("gettimeofday(2) failed");
225 }
226 void xsetnonblock(int fd, int nonb) {
227   int errnoval= oop_fd_nonblock(fd, nonb);
228   if (errnoval) { errno= errnoval; syscrash("setnonblocking"); }
229 }
230
231 void check_isreg(const struct stat *stab, const char *path,
232                  const char *what) {
233   if (!S_ISREG(stab->st_mode))
234     crash("%s %s not a plain file (mode 0%lo)",
235           what, path, (unsigned long)stab->st_mode);
236 }
237
238 static void xfstat(int fd, struct stat *stab_r, const char *what) {
239   int r= fstat(fd, stab_r);
240   if (r) syscrash("could not fstat %s", what);
241 }
242
243 static void xfstat_isreg(int fd, struct stat *stab_r,
244                          const char *path, const char *what) {
245   xfstat(fd, stab_r, what);
246   check_isreg(stab_r, path, what);
247 }
248
249 void xlstat_isreg(const char *path, struct stat *stab,
250                   int *enoent_r /* 0 means ENOENT is fatal */,
251                   const char *what) {
252   int r= lstat(path, stab);
253   if (r) {
254     if (errno==ENOENT && enoent_r) { *enoent_r=1; return; }
255     syscrash("could not lstat %s %s", what, path);
256   }
257   if (enoent_r) *enoent_r= 0;
258   check_isreg(stab, path, what);
259 }
260
261 int samefile(const struct stat *a, const struct stat *b) {
262   assert(S_ISREG(a->st_mode));
263   assert(S_ISREG(b->st_mode));
264   return (a->st_ino == b->st_ino &&
265           a->st_dev == b->st_dev);
266 }
267
268 char *sanitise(const char *input, int len) {
269   static char sanibuf[100]; /* returns pointer to this buffer! */
270
271   const char *p= input;
272   const char *endp= len>=0 ? input+len : 0;
273   char *q= sanibuf;
274   *q++= '`';
275   for (;;) {
276     if (q > sanibuf+sizeof(sanibuf)-8) { strcpy(q,"'.."); break; }
277     int c= (!endp || p<endp) ? *p++ : 0;
278     if (!c) { *q++= '\''; *q=0; break; }
279     if (c>=' ' && c<=126 && c!='\\') { *q++= c; continue; }
280     sprintf(q,"\\x%02x",c);
281     q += 4;
282   }
283   return sanibuf;
284 }