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.
36 #define PATH_DEVNULL "/dev/null"
38 /* malloc() that exits on failure. */
40 xmalloc(const size_t size
)
42 void *ptr
= malloc(size
);
45 errx(1, "malloc(): out of memory");
49 /* calloc() that exits on failure. */
51 xcalloc(const size_t num
, const size_t size
)
53 void *ptr
= calloc(num
, size
);
56 errx(1, "calloc(): out of memory");
60 /* realloc() that exits on failure. */
62 xrealloc(void *original
, const size_t size
)
64 void *ptr
= realloc(original
, size
);
67 errx(1, "realloc(): out of memory");
71 /* strdup() that exits on failure. */
73 xstrdup(const char *s
)
75 char *tmp
= strdup(s
);
78 errx(1, "strdup(): out of memory");
82 /* ---------------------------------------------------------------------------
83 * Split string out of src with range [left:right-1]
86 split_string(const char *src
, const size_t left
, const size_t right
)
89 assert(left
<= right
);
90 assert(left
< strlen(src
)); /* [left means must be smaller */
91 assert(right
<= strlen(src
)); /* right) means can be equal or smaller */
93 dest
= xmalloc(right
- left
+ 1);
94 memcpy(dest
, src
+left
, right
-left
);
95 dest
[right
-left
] = '\0';
99 /* ---------------------------------------------------------------------------
100 * Uppercasify all characters in a string of given length.
103 strntoupper(char *str
, const size_t length
)
107 for (i
=0; i
<length
; i
++)
108 str
[i
] = toupper(str
[i
]);
111 /* ---------------------------------------------------------------------------
112 * Returns non-zero if haystack starts with needle.
115 str_starts_with(const char *haystack
, const char *needle
)
119 while (needle
[i
] != '\0') {
120 if ((haystack
[i
] == '\0') || (haystack
[i
] != needle
[i
]))
127 /* split - splits a string by a delimiter character into an array of
130 * The chunks and the array are dynamically allocated using xmalloc() so
131 * it will errx() if it runs out of memory.
134 * char **chunks = split('.', "..one...two....", &num_chunks);
136 * num_chunks = 2, chunks = { "one", "two", NULL }
139 split(const char delimiter
, const char *str
, unsigned int *num_chunks
)
141 unsigned int num
= 0;
142 char **chunks
= NULL
;
143 size_t left
, right
= 0;
145 #define PUSH(c) do { num++; chunks = (char**) xrealloc(chunks, \
146 sizeof(*chunks) * num); chunks[num-1] = c; } while(0)
149 /* find first non-delimiter */
150 for (left
= right
; str
[left
] == delimiter
; left
++)
153 if (str
[left
] == '\0')
154 break; /* ran out of string */
156 /* find first delimiter or end of string */
158 str
[right
] != delimiter
&& str
[right
] != '\0';
162 /* split chunk out */
163 PUSH( split_string(str
, left
, right
) );
165 if (str
[right
] == '\0')
166 break; /* ran out of string */
173 if (num_chunks
!= NULL
)
174 *num_chunks
= num
-1; /* NULL doesn't count */
179 /* Given an HTTP query string and a key to search for, return the value
180 * associated with it, or NULL if there is no such key or qs is NULL.
181 * The returned string needs to be freed.
184 * qs = "sort=in&start=20";
185 * qs_get(sq, "sort") returns "in"
186 * qs_get(sq, "end") returns NULL
189 qs_get(const char *qs
, const char *key
)
191 size_t pos
, qslen
, keylen
;
193 if (qs
== NULL
) return NULL
;
196 keylen
= strlen(key
);
198 while (pos
< qslen
) {
199 if (!(pos
+ keylen
+ 1 < qslen
))
200 /* not enough room for "key" + "=" */
203 if (str_starts_with(qs
+pos
, key
) && qs
[pos
+keylen
] == '=') {
207 start
= pos
+ keylen
+ 1;
208 for (end
=start
; end
<qslen
&& qs
[end
] != '&'; end
++)
210 return split_string(qs
, start
, end
);
212 /* didn't find key, skip to next & */
213 do { pos
++; } while ((pos
< qslen
) && (qs
[pos
] != '&'));
214 pos
++; /* skip the ampersand */
218 return NULL
; /* not found */
221 static int lifeline
[2] = { -1, -1 };
222 static int fd_null
= -1;
225 daemonize_start(void)
229 if (pipe(lifeline
) == -1)
230 err(1, "pipe(lifeline)");
232 fd_null
= open(PATH_DEVNULL
, O_RDWR
, 0);
234 err(1, "open(" PATH_DEVNULL
")");
240 /* parent: wait for child */
244 verbosef("parent waiting");
245 if (close(lifeline
[1]) == -1)
246 warn("close lifeline in parent");
247 if (read(lifeline
[0], tmp
, sizeof(tmp
)) != 0) /* expecting EOF */
248 err(1, "lifeline read() failed");
249 verbosef("parent done reading, calling waitpid");
250 w
= waitpid(f
, &status
, WNOHANG
);
251 verbosef("waitpid ret %d, status is %d", w
, status
);
255 /* child is running happily */
258 /* child init failed, pass on its exit status */
259 exit(WEXITSTATUS(status
));
261 /* else we are the child: continue initializing */
265 daemonize_finish(void)
268 return; /* didn't daemonize_start(), i.e. we're not daemonizing */
272 if (close(lifeline
[0]) == -1)
273 warn("close read end of lifeline in child");
274 if (close(lifeline
[1]) == -1)
275 warn("couldn't cut the lifeline");
277 /* close all our std fds */
278 if (dup2(fd_null
, STDIN_FILENO
) == -1)
280 if (dup2(fd_null
, STDOUT_FILENO
) == -1)
281 warn("dup2(stdout)");
282 if (dup2(fd_null
, STDERR_FILENO
) == -1)
283 warn("dup2(stderr)");
289 * For security, chroot (optionally) and drop privileges.
290 * Pass a NULL chroot_dir to disable chroot() behaviour.
292 void privdrop(const char *chroot_dir
, const char *privdrop_user
) {
296 pw
= getpwnam(privdrop_user
);
300 errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user
);
302 err(1, "getpwnam(\"%s\") failed", privdrop_user
);
304 if (chroot_dir
== NULL
) {
305 verbosef("no --chroot dir specified, darkstat will not chroot()");
307 tzset(); /* read /etc/localtime before we chroot */
308 if (chdir(chroot_dir
) == -1)
309 err(1, "chdir(\"%s\") failed", chroot_dir
);
310 if (chroot(chroot_dir
) == -1)
311 err(1, "chroot(\"%s\") failed", chroot_dir
);
312 verbosef("chrooted into: %s", chroot_dir
);
316 list
[0] = pw
->pw_gid
;
317 if (setgroups(1, list
) == -1)
320 if (setgid(pw
->pw_gid
) == -1)
322 if (setuid(pw
->pw_uid
) == -1)
324 verbosef("set uid/gid to %d/%d", (int)pw
->pw_uid
, (int)pw
->pw_gid
);
327 /* Make the specified file descriptor non-blocking. */
329 fd_set_nonblock(const int fd
)
333 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
334 err(1, "fcntl(fd %d) to get flags", fd
);
336 if (fcntl(fd
, F_SETFL
, flags
) == -1)
337 err(1, "fcntl(fd %d) to set O_NONBLOCK", fd
);
338 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == O_NONBLOCK
);
341 /* Make the specified file descriptor blocking. */
343 fd_set_block(const int fd
)
347 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1)
348 err(1, "fcntl(fd %d) to get flags", fd
);
349 flags
&= ~O_NONBLOCK
;
350 if (fcntl(fd
, F_SETFL
, flags
) == -1)
351 err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd
);
352 assert( (fcntl(fd
, F_GETFL
, 0) & O_NONBLOCK
) == 0 );
355 /* vim:set ts=3 sw=3 tw=78 expandtab: */