Implement support for DLT_RAW.
[darkstat] / conv.c
1 /* darkstat 3
2 * copyright (c) 2001-2007 Emil Mikulic.
3 *
4 * conv.c: convenience functions.
5 *
6 * Permission to use, copy, modify, and distribute this file for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "darkstat.h"
20 #include "conv.h"
21
22 #include <sys/wait.h>
23 #include <assert.h>
24 #include <ctype.h>
25 #include "err.h"
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #define PATH_DEVNULL "/dev/null"
36
37 /* malloc() that exits on failure. */
38 void *
39 xmalloc(const size_t size)
40 {
41 void *ptr = malloc(size);
42
43 if (ptr == NULL)
44 errx(1, "malloc(): out of memory");
45 return (ptr);
46 }
47
48 /* calloc() that exits on failure. */
49 void *
50 xcalloc(const size_t num, const size_t size)
51 {
52 void *ptr = calloc(num, size);
53
54 if (ptr == NULL)
55 errx(1, "calloc(): out of memory");
56 return (ptr);
57 }
58
59 /* realloc() that exits on failure. */
60 void *
61 xrealloc(void *original, const size_t size)
62 {
63 void *ptr = realloc(original, size);
64
65 if (ptr == NULL)
66 errx(1, "realloc(): out of memory");
67 return (ptr);
68 }
69
70 /* strdup() that exits on failure. */
71 char *
72 xstrdup(const char *s)
73 {
74 char *tmp = strdup(s);
75
76 if (tmp == NULL)
77 errx(1, "strdup(): out of memory");
78 return (tmp);
79 }
80
81 /* ---------------------------------------------------------------------------
82 * Split string out of src with range [left:right-1]
83 */
84 char *
85 split_string(const char *src, const size_t left, const size_t right)
86 {
87 char *dest;
88 assert(left <= right);
89 assert(left < strlen(src)); /* [left means must be smaller */
90 assert(right <= strlen(src)); /* right) means can be equal or smaller */
91
92 dest = xmalloc(right - left + 1);
93 memcpy(dest, src+left, right-left);
94 dest[right-left] = '\0';
95 return (dest);
96 }
97
98 /* ---------------------------------------------------------------------------
99 * Uppercasify all characters in a string of given length.
100 */
101 void
102 strntoupper(char *str, const size_t length)
103 {
104 size_t i;
105
106 for (i=0; i<length; i++)
107 str[i] = toupper(str[i]);
108 }
109
110 /* ---------------------------------------------------------------------------
111 * Returns non-zero if haystack starts with needle.
112 */
113 int
114 str_starts_with(const char *haystack, const char *needle)
115 {
116 int i = 0;
117
118 while (needle[i] != '\0') {
119 if ((haystack[i] == '\0') || (haystack[i] != needle[i]))
120 return (0);
121 i++;
122 }
123 return (1);
124 }
125
126 /* split - splits a string by a delimiter character into an array of
127 * string chunks.
128 *
129 * The chunks and the array are dynamically allocated using xmalloc() so
130 * it will errx() if it runs out of memory.
131 *
132 * int num_chunks;
133 * char **chunks = split('.', "..one...two....", &num_chunks);
134 *
135 * num_chunks = 2, chunks = { "one", "two", NULL }
136 */
137 char **
138 split(const char delimiter, const char *str, int *num_chunks)
139 {
140 int num = 0;
141 char **chunks = NULL;
142 size_t left, right = 0;
143
144 #define PUSH(c) do { num++; chunks = (char**) xrealloc(chunks, \
145 sizeof(*chunks) * num); chunks[num-1] = c; } while(0)
146
147 for(;;) {
148 /* find first non-delimiter */
149 for (left = right; str[left] == delimiter; left++)
150 ;
151
152 if (str[left] == '\0')
153 break; /* ran out of string */
154
155 /* find first delimiter or end of string */
156 for (right=left+1;
157 str[right] != delimiter && str[right] != '\0';
158 right++)
159 ;
160
161 /* split chunk out */
162 PUSH( split_string(str, left, right) );
163
164 if (str[right] == '\0')
165 break; /* ran out of string */
166 else
167 right++;
168 }
169
170 /* return */
171 PUSH(NULL);
172 if (num_chunks != NULL)
173 *num_chunks = num-1; /* NULL doesn't count */
174 return (chunks);
175 #undef PUSH
176 }
177
178 /* Given an HTTP query string and a key to search for, return the value
179 * associated with it, or NULL if there is no such key or qs is NULL.
180 * The returned string needs to be freed.
181 *
182 * e.g.:
183 * qs = "sort=in&start=20";
184 * qs_get(sq, "sort") returns "in"
185 * qs_get(sq, "end") returns NULL
186 */
187 char *
188 qs_get(const char *qs, const char *key)
189 {
190 size_t pos, qslen, keylen;
191
192 if (qs == NULL) return NULL;
193
194 qslen = strlen(qs);
195 keylen = strlen(key);
196 pos = 0;
197 while (pos < qslen) {
198 if (!(pos + keylen + 1 < qslen))
199 /* not enough room for "key" + "=" */
200 return NULL;
201 else {
202 if (str_starts_with(qs+pos, key) && qs[pos+keylen] == '=') {
203 /* found key= */
204 size_t start, end;
205
206 start = pos + keylen + 1;
207 for (end=start; end<qslen && qs[end] != '&'; end++)
208 ;
209 return split_string(qs, start, end);
210 } else {
211 /* didn't find key, skip to next & */
212 do { pos++; } while ((pos < qslen) && (qs[pos] != '&'));
213 pos++; /* skip the ampersand */
214 }
215 }
216 }
217 return NULL; /* not found */
218 }
219
220 static int lifeline[2] = { -1, -1 };
221 static int fd_null = -1;
222
223 void
224 daemonize_start(void)
225 {
226 pid_t f, w;
227
228 if (pipe(lifeline) == -1)
229 err(1, "pipe(lifeline)");
230
231 fd_null = open(PATH_DEVNULL, O_RDWR, 0);
232 if (fd_null == -1)
233 err(1, "open(" PATH_DEVNULL ")");
234
235 f = fork();
236 if (f == -1)
237 err(1, "fork");
238 else if (f != 0) {
239 /* parent: wait for child */
240 char tmp[1];
241 int status;
242
243 verbosef("parent waiting");
244 if (close(lifeline[1]) == -1)
245 warn("close lifeline in parent");
246 read(lifeline[0], tmp, sizeof(tmp));
247 verbosef("parent done reading, calling waitpid");
248 w = waitpid(f, &status, WNOHANG);
249 verbosef("waitpid ret %d, status is %d", w, status);
250 if (w == -1)
251 err(1, "waitpid");
252 else if (w == 0)
253 /* child is running happily */
254 exit(EXIT_SUCCESS);
255 else
256 /* child init failed, pass on its exit status */
257 exit(WEXITSTATUS(status));
258 }
259 /* else we are the child: continue initializing */
260 }
261
262 void
263 daemonize_finish(void)
264 {
265 if (fd_null == -1)
266 return; /* didn't daemonize_start(), i.e. we're not daemonizing */
267
268 if (setsid() == -1)
269 err(1, "setsid");
270 if (close(lifeline[0]) == -1)
271 warn("close read end of lifeline in child");
272 if (close(lifeline[1]) == -1)
273 warn("couldn't cut the lifeline");
274
275 /* close all our std fds */
276 if (dup2(fd_null, STDIN_FILENO) == -1)
277 warn("dup2(stdin)");
278 if (dup2(fd_null, STDOUT_FILENO) == -1)
279 warn("dup2(stdout)");
280 if (dup2(fd_null, STDERR_FILENO) == -1)
281 warn("dup2(stderr)");
282 if (fd_null > 2)
283 close(fd_null);
284 }
285
286 /*
287 * For security, chroot (optionally) and drop privileges.
288 * Pass a NULL chroot_dir to disable chroot() behaviour.
289 */
290 void
291 privdrop(const char *chroot_dir, const char *privdrop_user)
292 {
293 struct passwd *pw;
294
295 errno = 0;
296 pw = getpwnam(privdrop_user);
297
298 if (pw == NULL) {
299 if (errno == 0)
300 errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user);
301 else
302 err(1, "getpwnam(\"%s\") failed", privdrop_user);
303 }
304 if (chroot_dir != NULL) {
305 tzset(); /* read /etc/localtime before we chroot */
306 if (chdir(chroot_dir) == -1)
307 err(1, "chdir(\"%s\") failed", chroot_dir);
308 if (chroot(chroot_dir) == -1)
309 err(1, "chroot(\"%s\") failed", chroot_dir);
310 verbosef("chrooted into: %s", chroot_dir);
311 }
312 if (setgid(pw->pw_gid) == -1)
313 err(1, "setgid");
314 if (setuid(pw->pw_uid) == -1)
315 err(1, "setuid");
316 verbosef("set uid/gid to %d/%d", (int)pw->pw_uid, (int)pw->pw_gid);
317 }
318
319 /* Make the specified file descriptor non-blocking. */
320 void
321 fd_set_nonblock(const int fd)
322 {
323 int flags;
324
325 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
326 err(1, "fcntl(fd %d) to get flags", fd);
327 flags |= O_NONBLOCK;
328 if (fcntl(fd, F_SETFL, flags) == -1)
329 err(1, "fcntl(fd %d) to set O_NONBLOCK", fd);
330 assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == O_NONBLOCK );
331 }
332
333 /* Make the specified file descriptor blocking. */
334 void
335 fd_set_block(const int fd)
336 {
337 int flags;
338
339 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
340 err(1, "fcntl(fd %d) to get flags", fd);
341 flags &= ~O_NONBLOCK;
342 if (fcntl(fd, F_SETFL, flags) == -1)
343 err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd);
344 assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == 0 );
345 }
346
347 /* strlcpy() and strlcat() are derived from:
348 *
349 * $OpenBSD: strlcpy.c,v 1.4
350 * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.8
351 *
352 * $OpenBSD: strlcat.c,v 1.2
353 * $FreeBSD: src/lib/libc/string/strlcat.c,v 1.10
354 *
355 * under the following license:
356 *
357 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
358 * All rights reserved.
359 *
360 * Redistribution and use in source and binary forms, with or without
361 * modification, are permitted provided that the following conditions
362 * are met:
363 * 1. Redistributions of source code must retain the above copyright
364 * notice, this list of conditions and the following disclaimer.
365 * 2. Redistributions in binary form must reproduce the above copyright
366 * notice, this list of conditions and the following disclaimer in the
367 * documentation and/or other materials provided with the distribution.
368 * 3. The name of the author may not be used to endorse or promote products
369 * derived from this software without specific prior written permission.
370 *
371 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
372 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
373 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
374 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
375 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
376 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
377 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
378 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
379 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
380 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
381 */
382
383 #ifndef HAVE_STRLCPY
384 /*
385 * Copy src to string dst of size siz. At most siz-1 characters
386 * will be copied. Always NUL terminates (unless siz == 0).
387 * Returns strlen(src); if retval >= siz, truncation occurred.
388 */
389 size_t strlcpy(dst, src, siz)
390 char *dst;
391 const char *src;
392 size_t siz;
393 {
394 char *d = dst;
395 const char *s = src;
396 size_t n = siz;
397
398 /* Copy as many bytes as will fit */
399 if (n != 0 && --n != 0) {
400 do {
401 if ((*d++ = *s++) == 0)
402 break;
403 } while (--n != 0);
404 }
405
406 /* Not enough room in dst, add NUL and traverse rest of src */
407 if (n == 0) {
408 if (siz != 0)
409 *d = '\0'; /* NUL-terminate dst */
410 while (*s++)
411 ;
412 }
413
414 return(s - src - 1); /* count does not include NUL */
415 }
416 #endif
417
418 #ifndef HAVE_STRLCAT
419 /*
420 * Appends src to string dst of size siz (unlike strncat, siz is the
421 * full size of dst, not space left). At most siz-1 characters
422 * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
423 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
424 * If retval >= siz, truncation occurred.
425 */
426 size_t
427 strlcat(dst, src, siz)
428 char *dst;
429 const char *src;
430 size_t siz;
431 {
432 char *d = dst;
433 const char *s = src;
434 size_t n = siz;
435 size_t dlen;
436
437 /* Find the end of dst and adjust bytes left but don't go past end */
438 while (n-- != 0 && *d != '\0')
439 d++;
440 dlen = d - dst;
441 n = siz - dlen;
442
443 if (n == 0)
444 return(dlen + strlen(s));
445 while (*s != '\0') {
446 if (n != 1) {
447 *d++ = *s;
448 n--;
449 }
450 s++;
451 }
452 *d = '\0';
453
454 return(dlen + (s - src)); /* count does not include NUL */
455 }
456 #endif
457
458 /* vim:set ts=3 sw=3 tw=78 expandtab: */