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