2 * copyright (c) 2001-2011 Emil Mikulic.
4 * conv.c: convenience functions.
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.
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.
37 #define PATH_DEVNULL "/dev/null"
39 /* malloc() that exits on failure. */
41 xmalloc(const size_t size
)
43 void *ptr
= malloc(size
);
46 errx(1, "malloc(): out of memory");
50 /* calloc() that exits on failure. */
52 xcalloc(const size_t num
, const size_t size
)
54 void *ptr
= calloc(num
, size
);
57 errx(1, "calloc(): out of memory");
61 /* realloc() that exits on failure. */
63 xrealloc(void *original
, const size_t size
)
65 void *ptr
= realloc(original
, size
);
68 errx(1, "realloc(): out of memory");
72 /* strdup() that exits on failure. */
74 xstrdup(const char *s
)
76 char *tmp
= strdup(s
);
79 errx(1, "strdup(): out of memory");
83 /* ---------------------------------------------------------------------------
84 * Split string out of src with range [left:right-1]
87 split_string(const char *src
, const size_t left
, const size_t right
)
90 assert(left
<= right
);
91 assert(left
< strlen(src
)); /* [left means must be smaller */
92 assert(right
<= strlen(src
)); /* right) means can be equal or smaller */
94 dest
= xmalloc(right
- left
+ 1);
95 memcpy(dest
, src
+left
, right
-left
);
96 dest
[right
-left
] = '\0';
100 /* ---------------------------------------------------------------------------
101 * Uppercasify all characters in a string of given length.
104 strntoupper(char *str
, const size_t length
)
108 for (i
=0; i
<length
; i
++)
109 str
[i
] = toupper(str
[i
]);
112 /* ---------------------------------------------------------------------------
113 * Returns non-zero if haystack starts with needle.
116 str_starts_with(const char *haystack
, const char *needle
)
120 while (needle
[i
] != '\0') {
121 if ((haystack
[i
] == '\0') || (haystack
[i
] != needle
[i
]))
128 /* split - splits a string by a delimiter character into an array of
131 * The chunks and the array are dynamically allocated using xmalloc() so
132 * it will errx() if it runs out of memory.
135 * char **chunks = split('.', "..one...two....", &num_chunks);
137 * num_chunks = 2, chunks = { "one", "two", NULL }
140 split(const char delimiter
, const char *str
, unsigned int *num_chunks
)
142 unsigned int num
= 0;
143 char **chunks
= NULL
;
144 size_t left
, right
= 0;
146 #define PUSH(c) do { num++; chunks = (char**) xrealloc(chunks, \
147 sizeof(*chunks) * num); chunks[num-1] = c; } while(0)
150 /* find first non-delimiter */
151 for (left
= right
; str
[left
] == delimiter
; left
++)
154 if (str
[left
] == '\0')
155 break; /* ran out of string */
157 /* find first delimiter or end of string */
159 str
[right
] != delimiter
&& str
[right
] != '\0';
163 /* split chunk out */
164 PUSH( split_string(str
, left
, right
) );
166 if (str
[right
] == '\0')
167 break; /* ran out of string */
174 if (num_chunks
!= NULL
)
175 *num_chunks
= num
-1; /* NULL doesn't count */
180 /* Given an HTTP query string and a key to search for, return the value
181 * associated with it, or NULL if there is no such key or qs is NULL.
182 * The returned string needs to be freed.
185 * qs = "sort=in&start=20";
186 * qs_get(sq, "sort") returns "in"
187 * qs_get(sq, "end") returns NULL
190 qs_get(const char *qs
, const char *key
)
192 size_t pos
, qslen
, keylen
;
194 if (qs
== NULL
) return NULL
;
197 keylen
= strlen(key
);
199 while (pos
< qslen
) {
200 if (!(pos
+ keylen
+ 1 < qslen
))
201 /* not enough room for "key" + "=" */
204 if (str_starts_with(qs
+pos
, key
) && qs
[pos
+keylen
] == '=') {
208 start
= pos
+ keylen
+ 1;
209 for (end
=start
; end
<qslen
&& qs
[end
] != '&'; end
++)
211 return split_string(qs
, start
, end
);
213 /* didn't find key, skip to next & */
214 do { pos
++; } while ((pos
< qslen
) && (qs
[pos
] != '&'));
215 pos
++; /* skip the ampersand */
219 return NULL
; /* not found */
222 static int lifeline
[2] = { -1, -1 };
223 static int fd_null
= -1;
226 daemonize_start(void)
230 if (pipe(lifeline
) == -1)
231 err(1, "pipe(lifeline)");
233 fd_null
= open(PATH_DEVNULL
, O_RDWR
, 0);
235 err(1, "open(" PATH_DEVNULL
")");
241 /* parent: wait for child */
245 verbosef("parent waiting");
246 if (close(lifeline
[1]) == -1)
247 warn("close lifeline in parent");
248 if (read(lifeline
[0], tmp
, sizeof(tmp
)) != 0) /* expecting EOF */
249 err(1, "lifeline read() failed");
250 verbosef("parent done reading, calling waitpid");
251 w
= waitpid(f
, &status
, WNOHANG
);
252 verbosef("waitpid ret %d, status is %d", w
, status
);
256 /* child is running happily */
259 /* child init failed, pass on its exit status */
260 exit(WEXITSTATUS(status
));
262 /* else we are the child: continue initializing */
266 daemonize_finish(void)
269 return; /* didn't daemonize_start(), i.e. we're not daemonizing */
273 if (close(lifeline
[0]) == -1)
274 warn("close read end of lifeline in child");
275 if (close(lifeline
[1]) == -1)
276 warn("couldn't cut the lifeline");
278 /* close all our std fds */
279 if (dup2(fd_null
, STDIN_FILENO
) == -1)
281 if (dup2(fd_null
, STDOUT_FILENO
) == -1)
282 warn("dup2(stdout)");
283 if (dup2(fd_null
, STDERR_FILENO
) == -1)
284 warn("dup2(stderr)");
290 * For security, chroot (optionally) and drop privileges.
291 * Pass a NULL chroot_dir to disable chroot() behaviour.
294 privdrop(const char *chroot_dir
, const char *privdrop_user
)
299 pw
= getpwnam(privdrop_user
);
303 errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user
);
305 err(1, "getpwnam(\"%s\") failed", privdrop_user
);
307 if (chroot_dir
!= NULL
) {
308 tzset(); /* read /etc/localtime before we chroot */
309 if (chdir(chroot_dir
) == -1)
310 err(1, "chdir(\"%s\") failed", chroot_dir
);
311 if (chroot(chroot_dir
) == -1)
312 err(1, "chroot(\"%s\") failed", chroot_dir
);
313 verbosef("chrooted into: %s", chroot_dir
);
315 if (setgid(pw
->pw_gid
) == -1)
317 if (setuid(pw
->pw_uid
) == -1)
319 verbosef("set uid/gid to %d/%d", (int)pw
->pw_uid
, (int)pw
->pw_gid
);
322 /* Make the specified file descriptor non-blocking. */
324 fd_set_nonblock(const int fd
)
328 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
329 err(1, "fcntl(fd %d) to get flags", fd
);
331 if (fcntl(fd
, F_SETFL
, flags
) == -1)
332 err(1, "fcntl(fd %d) to set O_NONBLOCK", fd
);
333 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == O_NONBLOCK
);
336 /* Make the specified file descriptor blocking. */
338 fd_set_block(const int fd
)
342 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
343 err(1, "fcntl(fd %d) to get flags", fd
);
344 flags
&= ~O_NONBLOCK
;
345 if (fcntl(fd
, F_SETFL
, flags
) == -1)
346 err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd
);
347 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == 0 );
350 /* strlcpy() and strlcat() are derived from:
352 * $OpenBSD: strlcpy.c,v 1.4
353 * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.8
355 * $OpenBSD: strlcat.c,v 1.2
356 * $FreeBSD: src/lib/libc/string/strlcat.c,v 1.10
358 * under the following license:
360 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
361 * All rights reserved.
363 * Redistribution and use in source and binary forms, with or without
364 * modification, are permitted provided that the following conditions
366 * 1. Redistributions of source code must retain the above copyright
367 * notice, this list of conditions and the following disclaimer.
368 * 2. Redistributions in binary form must reproduce the above copyright
369 * notice, this list of conditions and the following disclaimer in the
370 * documentation and/or other materials provided with the distribution.
371 * 3. The name of the author may not be used to endorse or promote products
372 * derived from this software without specific prior written permission.
374 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
375 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
376 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
377 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
378 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
379 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
380 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
381 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
382 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
383 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
388 * Copy src to string dst of size siz. At most siz-1 characters
389 * will be copied. Always NUL terminates (unless siz == 0).
390 * Returns strlen(src); if retval >= siz, truncation occurred.
393 strlcpy(char * restrict dst
, const char * restrict src
, const size_t siz
)
399 /* Copy as many bytes as will fit */
400 if (n
!= 0 && --n
!= 0) {
402 if ((*d
++ = *s
++) == 0)
407 /* Not enough room in dst, add NUL and traverse rest of src */
410 *d
= '\0'; /* NUL-terminate dst */
415 return (size_t)(s
- src
- 1); /* count does not include NUL */
421 * Appends src to string dst of size siz (unlike strncat, siz is the
422 * full size of dst, not space left). At most siz-1 characters
423 * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
424 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
425 * If retval >= siz, truncation occurred.
428 strlcat(char * restrict dst
, const char * restrict src
, const size_t siz
)
435 /* Find the end of dst and adjust bytes left but don't go past end */
436 while (n
-- != 0 && *d
!= '\0')
438 dlen
= (size_t)(d
- dst
);
442 return(dlen
+ strlen(s
));
452 return (dlen
+ (size_t)(s
- src
)); /* count does not include NUL */
456 /* vim:set ts=3 sw=3 tw=78 expandtab: */