2 * copyright (c) 2001-2014 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.
35 #define PATH_DEVNULL "/dev/null"
37 /* malloc() that exits on failure. */
39 xmalloc(const size_t size
)
41 void *ptr
= malloc(size
);
44 errx(1, "malloc(): out of memory");
48 /* calloc() that exits on failure. */
50 xcalloc(const size_t num
, const size_t size
)
52 void *ptr
= calloc(num
, size
);
55 errx(1, "calloc(): out of memory");
59 /* realloc() that exits on failure. */
61 xrealloc(void *original
, const size_t size
)
63 void *ptr
= realloc(original
, size
);
66 errx(1, "realloc(): out of memory");
70 /* strdup() that exits on failure. */
72 xstrdup(const char *s
)
74 char *tmp
= strdup(s
);
77 errx(1, "strdup(): out of memory");
81 /* ---------------------------------------------------------------------------
82 * Split string out of src with range [left:right-1]
85 split_string(const char *src
, const size_t left
, const size_t right
)
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 */
92 dest
= xmalloc(right
- left
+ 1);
93 memcpy(dest
, src
+left
, right
-left
);
94 dest
[right
-left
] = '\0';
98 /* ---------------------------------------------------------------------------
99 * Uppercasify all characters in a string of given length.
102 strntoupper(char *str
, const size_t length
)
106 for (i
=0; i
<length
; i
++)
107 str
[i
] = toupper(str
[i
]);
110 /* ---------------------------------------------------------------------------
111 * Returns non-zero if haystack starts with needle.
114 str_starts_with(const char *haystack
, const char *needle
)
118 while (needle
[i
] != '\0') {
119 if ((haystack
[i
] == '\0') || (haystack
[i
] != needle
[i
]))
126 /* split - splits a string by a delimiter character into an array of
129 * The chunks and the array are dynamically allocated using xmalloc() so
130 * it will errx() if it runs out of memory.
133 * char **chunks = split('.', "..one...two....", &num_chunks);
135 * num_chunks = 2, chunks = { "one", "two", NULL }
138 split(const char delimiter
, const char *str
, unsigned int *num_chunks
)
140 unsigned int num
= 0;
141 char **chunks
= NULL
;
142 size_t left
, right
= 0;
144 #define PUSH(c) do { num++; chunks = (char**) xrealloc(chunks, \
145 sizeof(*chunks) * num); chunks[num-1] = c; } while(0)
148 /* find first non-delimiter */
149 for (left
= right
; str
[left
] == delimiter
; left
++)
152 if (str
[left
] == '\0')
153 break; /* ran out of string */
155 /* find first delimiter or end of string */
157 str
[right
] != delimiter
&& str
[right
] != '\0';
161 /* split chunk out */
162 PUSH( split_string(str
, left
, right
) );
164 if (str
[right
] == '\0')
165 break; /* ran out of string */
172 if (num_chunks
!= NULL
)
173 *num_chunks
= num
-1; /* NULL doesn't count */
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.
183 * qs = "sort=in&start=20";
184 * qs_get(sq, "sort") returns "in"
185 * qs_get(sq, "end") returns NULL
188 qs_get(const char *qs
, const char *key
)
190 size_t pos
, qslen
, keylen
;
192 if (qs
== NULL
) return NULL
;
195 keylen
= strlen(key
);
197 while (pos
< qslen
) {
198 if (!(pos
+ keylen
+ 1 < qslen
))
199 /* not enough room for "key" + "=" */
202 if (str_starts_with(qs
+pos
, key
) && qs
[pos
+keylen
] == '=') {
206 start
= pos
+ keylen
+ 1;
207 for (end
=start
; end
<qslen
&& qs
[end
] != '&'; end
++)
209 return split_string(qs
, start
, end
);
211 /* didn't find key, skip to next & */
212 do { pos
++; } while ((pos
< qslen
) && (qs
[pos
] != '&'));
213 pos
++; /* skip the ampersand */
217 return NULL
; /* not found */
220 static int lifeline
[2] = { -1, -1 };
221 static int fd_null
= -1;
224 daemonize_start(void)
228 if (pipe(lifeline
) == -1)
229 err(1, "pipe(lifeline)");
231 fd_null
= open(PATH_DEVNULL
, O_RDWR
, 0);
233 err(1, "open(" PATH_DEVNULL
")");
239 /* parent: wait for child */
243 verbosef("parent waiting");
244 if (close(lifeline
[1]) == -1)
245 warn("close lifeline in parent");
246 if (read(lifeline
[0], tmp
, sizeof(tmp
)) != 0) /* expecting EOF */
247 err(1, "lifeline read() failed");
248 verbosef("parent done reading, calling waitpid");
249 w
= waitpid(f
, &status
, WNOHANG
);
250 verbosef("waitpid ret %d, status is %d", w
, status
);
254 /* child is running happily */
257 /* child init failed, pass on its exit status */
258 exit(WEXITSTATUS(status
));
260 /* else we are the child: continue initializing */
264 daemonize_finish(void)
267 return; /* didn't daemonize_start(), i.e. we're not daemonizing */
271 if (close(lifeline
[0]) == -1)
272 warn("close read end of lifeline in child");
273 if (close(lifeline
[1]) == -1)
274 warn("couldn't cut the lifeline");
276 /* close all our std fds */
277 if (dup2(fd_null
, STDIN_FILENO
) == -1)
279 if (dup2(fd_null
, STDOUT_FILENO
) == -1)
280 warn("dup2(stdout)");
281 if (dup2(fd_null
, STDERR_FILENO
) == -1)
282 warn("dup2(stderr)");
288 * For security, chroot (optionally) and drop privileges.
289 * Pass a NULL chroot_dir to disable chroot() behaviour.
291 void privdrop(const char *chroot_dir
, const char *privdrop_user
) {
295 pw
= getpwnam(privdrop_user
);
299 errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user
);
301 err(1, "getpwnam(\"%s\") failed", privdrop_user
);
303 if (chroot_dir
== NULL
) {
304 verbosef("no --chroot dir specified, darkstat will not chroot()");
306 tzset(); /* read /etc/localtime before we chroot */
307 if (chdir(chroot_dir
) == -1)
308 err(1, "chdir(\"%s\") failed", chroot_dir
);
309 if (chroot(chroot_dir
) == -1)
310 err(1, "chroot(\"%s\") failed", chroot_dir
);
311 verbosef("chrooted into: %s", chroot_dir
);
313 if (setgid(pw
->pw_gid
) == -1)
315 if (setuid(pw
->pw_uid
) == -1)
317 verbosef("set uid/gid to %d/%d", (int)pw
->pw_uid
, (int)pw
->pw_gid
);
320 /* Make the specified file descriptor non-blocking. */
322 fd_set_nonblock(const int fd
)
326 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
327 err(1, "fcntl(fd %d) to get flags", fd
);
329 if (fcntl(fd
, F_SETFL
, flags
) == -1)
330 err(1, "fcntl(fd %d) to set O_NONBLOCK", fd
);
331 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == O_NONBLOCK
);
334 /* Make the specified file descriptor blocking. */
336 fd_set_block(const int fd
)
340 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
341 err(1, "fcntl(fd %d) to get flags", fd
);
342 flags
&= ~O_NONBLOCK
;
343 if (fcntl(fd
, F_SETFL
, flags
) == -1)
344 err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd
);
345 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == 0 );
348 /* vim:set ts=3 sw=3 tw=78 expandtab: */