1 /* $Id: util.c 6138 2003-01-19 04:13:51Z rra $
3 ** Various miscellaneous utility functions for innd internal use.
9 #include "inn/innconf.h"
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
21 FormatLong(char *p, unsigned long value, int width)
23 for (p += width - 1; width-- > 0; ) {
24 *p-- = (int)(value % 10) + '0';
32 ** Glue a string, a char, and a string together. Useful for making
36 FileGlue(char *p, const char *n1, char c,
39 p += strlen(strcpy(p, n1));
46 ** Turn any \r or \n in text into spaces. Used to splice back multi-line
47 ** headers into a single line.
54 for (p = text; *p; p++)
55 if (*p == '\n' || *p == '\r')
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.
66 MaxLength(const char *p, const char *q)
71 /* Already short enough? */
73 if (i < sizeof buff - 1) {
74 strlcpy(buff, p, sizeof(buff));
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));
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));
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));
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."
107 CommaSplit(char *text)
114 /* How much space do we need? */
115 for (i = 2, p = text; *p; p++)
119 for (av = save = xmalloc(i * sizeof(char *)), *av++ = p = text; *p; )
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
137 SetupListBuffer(int size, LISTBUFFER *list)
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);
147 /* get an array of character pointers. */
148 if (list->List == NULL) {
149 list->ListLength = DEFAULTNGBOXSIZE;
150 list->List = xmalloc(list->ListLength * sizeof(char *));
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.
161 NeedShell(char *p, const char **av, const char **end)
163 static const char Metachars[] = ";<>|*?[]{}()#$&=`'\"\\~\n";
166 /* We don't use execvp(); works for users, fails out of /etc/rc. */
170 if (strchr(Metachars, *q) != NULL)
173 for (end--; av < end; ) {
174 /* Mark this word, check for shell meta-characters. */
175 for (*av++ = p; *p && !ISWHITE(*p); p++)
178 /* If end of list, we're done. */
184 /* Skip whitespace, find next word. */
185 for (*p++ = '\0'; ISWHITE(*p); p++)
199 ** Spawn a process, with I/O redirected as needed. Return the PID or -1
200 ** (and a syslog'd message) on error.
203 Spawn(int niceval, int fd0, int fd1, int fd2, char * const av[])
205 static char NOCLOSE[] = "%s cant close %d in %s %m";
206 static char NODUP2[] = "%s cant dup2 %d to %d in %s %m";
209 /* Fork; on error, give up. If not using the patched dbz, make
213 syslog(L_ERROR, "%s cant fork %s %m", LogName, av[0]);
217 /* If parent, do nothing. */
221 /* Child -- do any I/O redirection. */
223 if (dup2(fd0, 0) < 0) {
224 syslog(L_FATAL, NODUP2, LogName, fd0, 0, av[0]);
227 if (fd0 != fd1 && fd0 != fd2 && close(fd0) < 0)
228 syslog(L_ERROR, NOCLOSE, LogName, fd0, av[0]);
231 if (dup2(fd1, 1) < 0) {
232 syslog(L_FATAL, NODUP2, LogName, fd1, 1, av[0]);
235 if (fd1 != fd2 && close(fd1) < 0)
236 syslog(L_ERROR, NOCLOSE, LogName, fd1, av[0]);
239 if (dup2(fd2, 2) < 0) {
240 syslog(L_FATAL, NODUP2, LogName, fd2, 2, av[0]);
244 syslog(L_ERROR, NOCLOSE, LogName, fd2, av[0]);
246 close_on_exec(0, false);
247 close_on_exec(1, false);
248 close_on_exec(2, false);
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);
254 /* Start the desired process (finally!). */
256 syslog(L_FATAL, "%s cant exec in %s %m", LogName, av[0]);
264 ** We ran out of space or other I/O error, throttle ourselves.
267 ThrottleIOError(const char *when)
273 if (Mode == OMrunning) {
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);
285 ThrottledbyIOError = true;
290 ** No matching storage.conf, throttle ourselves.
293 ThrottleNoMatchError(void)
298 if (Mode == OMrunning) {
303 snprintf(buff, sizeof(buff), "%s storing article -- throttling",
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;
319 histpath = concatpath(innconf->pathdb, _PATH_HISTORY);
320 if (innconf->hismethod == NULL) {
321 sysdie("hismethod is not defined");
325 flags = HIS_RDWR | (INND_DBZINCORE ? HIS_MMAP : HIS_ONDISK);
326 History = HISopen(histpath, innconf->hismethod, flags);
328 sysdie("SERVER can't open history %s", histpath);
332 HISsetcache(History, 1024 * innconf->hiscachesize);
333 synccount = innconf->icdsynccount;
334 HISctl(History, HISCTLS_SYNCCOUNT, &synccount);
342 if (!HISclose(History)) {
345 histpath = concatpath(innconf->pathdb, _PATH_HISTORY);
346 sysdie("SERVER can't close history %s", histpath);
353 InndHisWrite(const char *key, time_t arrived, time_t posted, time_t expires,
356 bool r = HISwrite(History, key, arrived, posted, expires, token);
359 IOError("history write", errno);
364 InndHisRemember(const char *key)
366 bool r = HISremember(History, key, Now.time);
369 IOError("history remember", errno);
374 InndHisLogStats(void)
376 struct histstats stats = HISstats(History);
378 syslog(L_NOTICE, "ME HISstats %d hitpos %d hitneg %d missed %d dne",
379 stats.hitpos, stats.hitneg, stats.misses, stats.dne);