3 * tailing reliable realtime streaming feeder for inn
4 * logging and utility functions
6 * Copyright (C) 2010 Ian Jackson <ijackson@chiark.greenend.org.uk>
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.
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.
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/>.
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.)
30 /* for logging, simulation, debugging, etc. */
31 int simulate_flush= -1;
33 const char *logv_prefix="";
35 /*========== logging ==========*/
37 static void logcore(int sysloglevel, const char *fmt, ...) PRINTF(2,3);
38 static void logcore(int sysloglevel, const char *fmt, ...) {
40 if (logv_use_syslog) {
41 vsyslog(sysloglevel,fmt,al);
43 if (self_pid) fprintf(stderr,"[%lu] ",(unsigned long)self_pid);
44 vfprintf(stderr,fmt,al);
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;
56 if (sysloglevel >= LOG_ERR && (errnoval==EACCES || errnoval==EPERM))
57 sysloglevel= LOG_ERR; /* run by wrong user, probably */
59 logcore(sysloglevel, "%s%s: %s%s%s",
60 logv_prefix, pfx, msgbuf,
61 errnoval>=0 ? ": " : "",
62 errnoval>=0 ? strerror(errnoval) : "");
65 #define DEFFATAL(fn, pfx, sysloglevel, err, estatus) \
66 void fn(const char *fmt, ...) { \
69 logv(sysloglevel, pfx, err, fmt, al); \
73 #define DEFLOG(fn, pfx, sysloglevel, err) \
74 static void fn(const char *fmt, ...) { \
76 logv(sysloglevel, pfx, err, fmt, al); \
80 #define INNLOGSET_DECLARE(fn, pfx, sysloglevel) \
81 static void duct_log_##fn(int l, const char *fmt, va_list al, int errval) \
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); \
86 #define INNLOGSET_CALL(fn, pfx, sysloglevel) \
87 message_handlers_##fn(1, duct_log_##fn);
90 static int innduct_fatal_cleanup(void) { return 12; } /* used for libinn die */
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. */
95 DEFFATAL(syscrash, "critical", LOG_CRIT, errno, 16);
96 DEFFATAL(crash, "critical", LOG_CRIT, -1, 16);
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)
105 DEFLOG(info, "info", LOG_INFO, -1)
106 DEFLOG(dbg, "debug", LOG_DEBUG, -1)
109 /*========== utility functions etc. ==========*/
111 char *xvasprintf(const char *fmt, va_list al) {
113 int rc= vasprintf(&str,fmt,al);
114 if (rc<0) sysdie("vasprintf(\"%s\",...) failed", fmt);
118 char *xasprintf(const char *fmt, ...) {
120 char *str= xvasprintf(fmt,al);
125 int close_perhaps(int *fd) {
126 if (*fd <= 0) return 0;
131 void xclose(int fd, const char *what, const char *what2) {
133 if (r) syscrash("close %s%s",what,what2?what2:"");
135 void xclose_perhaps(int *fd, const char *what, const char *what2) {
136 if (*fd <= 0) return;
137 xclose(*fd,what,what2);
141 pid_t xfork(const char *what) {
145 if (child==-1) sysdie("cannot fork for %s",what);
146 dbg("forked %s %ld", what, (unsigned long)child);
147 if (!child) postfork();
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);
155 void cancel_fd_read_except(int fd) {
156 loop->cancel_fd(loop, fd, OOP_READ);
157 loop->cancel_fd(loop, fd, OOP_EXCEPTION);
160 void report_child_status(const char *what, int status) {
161 if (WIFEXITED(status)) {
162 int es= WEXITSTATUS(status);
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)" : "";
170 warn("%s: child died due to fatal signal %s%s", what, sigstr, coredump);
172 warn("%s: child died due to unknown fatal signal %d%s",
173 what, sig, coredump);
175 warn("%s: child died with unknown wait status %d", what,status);
179 int xwaitpid(pid_t *pid, const char *what) {
182 int r= kill(*pid, SIGKILL);
183 if (r) syscrash("cannot kill %s child", what);
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);
194 void *zxmalloc(size_t sz) {
195 void *p= xmalloc(sz);
200 void xunlink(const char *path, const char *what) {
202 if (r) syscrash("can't unlink %s %s", path, what);
207 if (now==-1) syscrash("time(2) failed");
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));
215 void xsigsetdefault(int signo) {
217 memset(&sa,0,sizeof(sa));
218 sa.sa_handler= SIG_DFL;
219 xsigaction(signo,&sa);
222 void xgettimeofday(struct timeval *tv_r) {
223 int r= gettimeofday(tv_r,0);
224 if (r) syscrash("gettimeofday(2) failed");
226 void xsetnonblock(int fd, int nonb) {
227 int errnoval= oop_fd_nonblock(fd, nonb);
228 if (errnoval) { errno= errnoval; syscrash("setnonblocking"); }
231 void check_isreg(const struct stat *stab, const char *path,
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);
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);
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);
249 void xlstat_isreg(const char *path, struct stat *stab,
250 int *enoent_r /* 0 means ENOENT is fatal */,
252 int r= lstat(path, stab);
254 if (errno==ENOENT && enoent_r) { *enoent_r=1; return; }
255 syscrash("could not lstat %s %s", what, path);
257 if (enoent_r) *enoent_r= 0;
258 check_isreg(stab, path, what);
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);
268 char *sanitise(const char *input, int len) {
269 static char sanibuf[100]; /* returns pointer to this buffer! */
271 const char *p= input;
272 const char *endp= len>=0 ? input+len : 0;
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);