chiark / gitweb /
debugging for thing that crashed
[innduct.git] / innd / util.c
1 /*  $Id: util.c 6138 2003-01-19 04:13:51Z rra $
2 **
3 **  Various miscellaneous utility functions for innd internal use.
4 */
5
6 #include "config.h"
7 #include "clibrary.h"
8
9 #include "inn/innconf.h"
10 #include "libinn.h"
11
12 #include "innd.h"
13
14 /*
15 **  Sprintf a long into a buffer with enough leading zero's so that it
16 **  takes up width characters.  Don't add trailing NUL.  Return true
17 **  if it fit.  Used for updating high-water marks in the active file
18 **  in-place.
19 */
20 bool
21 FormatLong(char *p, unsigned long value, int width)
22 {
23     for (p += width - 1; width-- > 0; ) {
24         *p-- = (int)(value % 10) + '0';
25         value /= 10;
26     }
27     return value == 0;
28 }
29
30
31 /*
32 **  Glue a string, a char, and a string together.  Useful for making
33 **  filenames.
34 */
35 void
36 FileGlue(char *p, const char *n1, char c,
37          const char *n2)
38 {
39     p += strlen(strcpy(p, n1));
40     *p++ = c;
41     strcpy(p, n2);
42 }
43
44
45 /*
46 **  Turn any \r or \n in text into spaces.  Used to splice back multi-line
47 **  headers into a single line.
48 */
49 static char *
50 Join(char *text)
51 {
52     char       *p;
53
54     for (p = text; *p; p++)
55         if (*p == '\n' || *p == '\r')
56             *p = ' ';
57     return text;
58 }
59
60
61 /*
62 **  Return a short name that won't overrun our bufer or syslog's buffer.
63 **  q should either be p, or point into p where the "interesting" part is.
64 */
65 char *
66 MaxLength(const char *p, const char *q)
67 {
68     static char buff[80];
69     unsigned int i;
70
71     /* Already short enough? */
72     i = strlen(p);
73     if (i < sizeof buff - 1) {
74         strlcpy(buff, p, sizeof(buff));
75         return Join(buff);
76     }
77
78     /* Simple case of just want the begining? */
79     if ((unsigned)(q - p) < sizeof buff - 4) {
80         strlcpy(buff, p, sizeof(buff) - 3);
81         strlcat(buff, "...", sizeof(buff));
82     }
83     /* Is getting last 10 characters good enough? */
84     else if ((p + i) - q < 10) {
85         strlcpy(buff, p, sizeof(buff) - 13);
86         strlcat(buff, "...", sizeof(buff) - 10);
87         strlcat(buff, &p[i - 10], sizeof(buff));
88     }
89     else {
90         /* Not in last 10 bytes, so use double elipses. */
91         strlcpy(buff, p, sizeof(buff) - 16);
92         strlcat(buff, "...", sizeof(buff) - 13);
93         strlcat(buff, &q[-5], sizeof(buff) - 3);
94         strlcat(buff, "...", sizeof(buff));
95     }
96     return Join(buff);
97 }
98
99
100 /*
101 **  Split text into comma-separated fields.  Return an allocated
102 **  NULL-terminated array of the fields within the modified argument that
103 **  the caller is expected to save or free.  We don't use strchr() since
104 **  the text is expected to be either relatively short or "comma-dense."
105 */
106 char **
107 CommaSplit(char *text)
108 {
109     int i;
110     char *p;
111     char **av;
112     char **save;
113
114     /* How much space do we need? */
115     for (i = 2, p = text; *p; p++)
116         if (*p == ',')
117             i++;
118
119     for (av = save = xmalloc(i * sizeof(char *)), *av++ = p = text; *p; )
120         if (*p == ',') {
121             *p++ = '\0';
122             *av++ = p;
123         }
124         else
125             p++;
126     *av = NULL;
127     return save;
128 }
129
130
131 /*
132 **  Set up LISTBUFFER so that data will be put into array
133 **  it allocates buffer and array for data if needed, otherwise use already
134 **  allocated one
135 */
136 void
137 SetupListBuffer(int size, LISTBUFFER *list)
138 {
139   /* get space for data to be splitted */
140   if (list->Data == NULL) {
141     list->DataLength = size;
142     list->Data = xmalloc(list->DataLength + 1);
143   } else if (list->DataLength < size) {
144     list->DataLength = size;
145     list->Data = xrealloc(list->Data, list->DataLength + 1);
146   }
147   /* get an array of character pointers. */
148   if (list->List == NULL) {
149     list->ListLength = DEFAULTNGBOXSIZE;
150     list->List = xmalloc(list->ListLength * sizeof(char *));
151   }
152 }
153
154
155 /*
156 **  Do we need a shell for the command?  If not, av is filled in with
157 **  the individual words of the command and the command is modified to
158 **  have NUL's inserted.
159 */
160 bool
161 NeedShell(char *p, const char **av, const char **end)
162 {
163     static const char Metachars[] = ";<>|*?[]{}()#$&=`'\"\\~\n";
164     const char *q;
165
166     /* We don't use execvp(); works for users, fails out of /etc/rc. */
167     if (*p != '/')
168         return true;
169     for (q = p; *q; q++)
170         if (strchr(Metachars, *q) != NULL)
171             return true;
172
173     for (end--; av < end; ) {
174         /* Mark this word, check for shell meta-characters. */
175         for (*av++ = p; *p && !ISWHITE(*p); p++)
176             continue;
177
178         /* If end of list, we're done. */
179         if (*p == '\0') {
180             *av = NULL;
181             return false;
182         }
183
184         /* Skip whitespace, find next word. */
185         for (*p++ = '\0'; ISWHITE(*p); p++)
186             continue;
187         if (*p == '\0') {
188             *av = NULL;
189             return false;
190         }
191     }
192
193     /* Didn't fit. */
194     return true;
195 }
196
197
198 /*
199 **  Spawn a process, with I/O redirected as needed.  Return the PID or -1
200 **  (and a syslog'd message) on error.
201 */
202 pid_t
203 Spawn(int niceval, int fd0, int fd1, int fd2, char * const av[])
204 {
205     static char NOCLOSE[] = "%s cant close %d in %s %m";
206     static char NODUP2[] = "%s cant dup2 %d to %d in %s %m";
207     pid_t       i;
208
209     /* Fork; on error, give up.  If not using the patched dbz, make
210      * this call fork! */
211     i = fork();
212     if (i == -1) {
213         syslog(L_ERROR, "%s cant fork %s %m", LogName, av[0]);
214         return -1;
215     }
216
217     /* If parent, do nothing. */
218     if (i > 0)
219         return i;
220
221     /* Child -- do any I/O redirection. */
222     if (fd0 != 0) {
223         if (dup2(fd0, 0) < 0) {
224             syslog(L_FATAL, NODUP2, LogName, fd0, 0, av[0]);
225             _exit(1);
226         }
227         if (fd0 != fd1 && fd0 != fd2 && close(fd0) < 0)
228             syslog(L_ERROR, NOCLOSE, LogName, fd0, av[0]);
229     }
230     if (fd1 != 1) {
231         if (dup2(fd1, 1) < 0) {
232             syslog(L_FATAL, NODUP2, LogName, fd1, 1, av[0]);
233             _exit(1);
234         }
235         if (fd1 != fd2 && close(fd1) < 0)
236             syslog(L_ERROR, NOCLOSE, LogName, fd1, av[0]);
237     }
238     if (fd2 != 2) {
239         if (dup2(fd2, 2) < 0) {
240             syslog(L_FATAL, NODUP2, LogName, fd2, 2, av[0]);
241             _exit(1);
242         }
243         if (close(fd2) < 0)
244             syslog(L_ERROR, NOCLOSE, LogName, fd2, av[0]);
245     }
246     close_on_exec(0, false);
247     close_on_exec(1, false);
248     close_on_exec(2, false);
249
250     /* Nice our child if we're supposed to. */
251     if (niceval != 0 && nice(niceval) == -1)
252         syslog(L_ERROR, "SERVER cant nice child to %d: %m", niceval);
253
254     /* Start the desired process (finally!). */
255     execv(av[0], av);
256     syslog(L_FATAL, "%s cant exec in %s %m", LogName, av[0]);
257     _exit(1);
258
259     /* Not reached. */
260     return -1;
261 }
262
263 /*
264 **  We ran out of space or other I/O error, throttle ourselves.
265 */
266 void
267 ThrottleIOError(const char *when)
268 {
269     char         buff[SMBUF];
270     const char * p;
271     int          oerrno;
272
273     if (Mode == OMrunning) {
274         oerrno = errno;
275         if (Reservation) {
276             free(Reservation);
277             Reservation = NULL;
278         }
279         snprintf(buff, sizeof(buff), "%s writing %s file -- throttling",
280                  strerror(oerrno), when);
281         if ((p = CCblock(OMthrottled, buff)) != NULL)
282             syslog(L_ERROR, "%s cant throttle %s", LogName, p);
283         syslog(L_FATAL, "%s throttle %s", LogName, buff);
284         errno = oerrno;
285         ThrottledbyIOError = true;
286     }
287 }
288
289 /*
290 **  No matching storage.conf, throttle ourselves.
291 */
292 void
293 ThrottleNoMatchError(void)
294 {
295     char buff[SMBUF];
296     const char *p;
297
298     if (Mode == OMrunning) {
299         if (Reservation) {
300             free(Reservation);
301             Reservation = NULL;
302         }
303         snprintf(buff, sizeof(buff), "%s storing article -- throttling",
304                  SMerrorstr);
305         if ((p = CCblock(OMthrottled, buff)) != NULL)
306             syslog(L_ERROR, "%s cant throttle %s", LogName, p);
307         syslog(L_FATAL, "%s throttle %s", LogName, buff);
308         ThrottledbyIOError = true;
309     }
310 }
311
312 void
313 InndHisOpen(void)
314 {
315     char *histpath;
316     int flags;
317     size_t synccount;
318
319     histpath = concatpath(innconf->pathdb, _PATH_HISTORY);
320     if (innconf->hismethod == NULL) {
321         sysdie("hismethod is not defined");
322         /*NOTREACHED*/
323     }
324
325     flags = HIS_RDWR | (INND_DBZINCORE ? HIS_MMAP : HIS_ONDISK);
326     History = HISopen(histpath, innconf->hismethod, flags);
327     if (!History) {
328         sysdie("SERVER can't open history %s", histpath);
329         /*NOTREACHED*/
330     }
331     free(histpath);
332     HISsetcache(History, 1024 * innconf->hiscachesize);
333     synccount = innconf->icdsynccount;
334     HISctl(History, HISCTLS_SYNCCOUNT, &synccount);
335 }
336
337 void
338 InndHisClose(void)
339 {
340     if (History == NULL)
341         return;
342     if (!HISclose(History)) {
343         char *histpath;
344
345         histpath = concatpath(innconf->pathdb, _PATH_HISTORY);
346         sysdie("SERVER can't close history %s", histpath);
347         free(histpath);
348     }   
349     History = NULL;
350 }
351
352 bool
353 InndHisWrite(const char *key, time_t arrived, time_t posted, time_t expires,
354              TOKEN *token)
355 {
356     bool r = HISwrite(History, key, arrived, posted, expires, token);
357
358     if (r != true)
359         IOError("history write", errno);
360     return r;
361 }
362
363 bool
364 InndHisRemember(const char *key)
365 {
366     bool r = HISremember(History, key, Now.time);
367
368     if (r != true)
369         IOError("history remember", errno);
370     return r;
371 }
372
373 void
374 InndHisLogStats(void)
375 {
376     struct histstats stats = HISstats(History);
377
378     syslog(L_NOTICE, "ME HISstats %d hitpos %d hitneg %d missed %d dne",
379            stats.hitpos, stats.hitneg, stats.misses, stats.dne);
380 }
381
382