1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright 2006-2008 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#ifdef __APPLE__
29#  include <mach/mach_time.h>
30#  include <mach/kern_return.h>
31#endif
32
33#include <sys/param.h>
34#include <sys/time.h>
35
36#include <errno.h>
37#include <fcntl.h>
38#ifdef BSD
39#  include <paths.h>
40#endif
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <time.h>
46#include <unistd.h>
47
48#include "common.h"
49#include "logger.h"
50
51#ifndef _PATH_DEVNULL
52#  define _PATH_DEVNULL "/dev/null"
53#endif
54
55int clock_monotonic = 0;
56
57/* Handy routine to read very long lines in text files.
58 * This means we read the whole line and avoid any nasty buffer overflows. */
59ssize_t
60get_line(char **line, size_t *len, FILE *fp)
61{
62	char *p;
63	size_t last = 0;
64
65	while(!feof(fp)) {
66		if (*line == NULL || last != 0) {
67			*len += BUFSIZ;
68			*line = xrealloc(*line, *len);
69		}
70		p = *line + last;
71		memset(p, 0, BUFSIZ);
72		if (fgets(p, BUFSIZ, fp) == NULL)
73			break;
74		last += strlen(p);
75		if (last && (*line)[last - 1] == '\n') {
76			(*line)[last - 1] = '\0';
77			break;
78		}
79	}
80	return last;
81}
82
83/* Simple hack to return a random number without arc4random */
84#ifndef HAVE_ARC4RANDOM
85uint32_t arc4random(void)
86{
87	int fd;
88	static unsigned long seed = 0;
89
90	if (!seed) {
91		fd = open("/dev/urandom", 0);
92		if (fd == -1 || read(fd,  &seed, sizeof(seed)) == -1)
93			seed = time(0);
94		if (fd >= 0)
95			close(fd);
96		srandom(seed);
97	}
98
99	return (uint32_t)random();
100}
101#endif
102
103/* strlcpy is nice, shame glibc does not define it */
104#if HAVE_STRLCPY
105#else
106size_t
107strlcpy(char *dst, const char *src, size_t size)
108{
109	const char *s = src;
110	size_t n = size;
111
112	if (n && --n)
113		do {
114			if (!(*dst++ = *src++))
115				break;
116		} while (--n);
117
118	if (!n) {
119		if (size)
120			*dst = '\0';
121		while (*src++);
122	}
123
124	return src - s - 1;
125}
126#endif
127
128#if HAVE_CLOSEFROM
129#else
130int
131closefrom(int fd)
132{
133	int max = getdtablesize();
134	int i;
135	int r = 0;
136
137	for (i = fd; i < max; i++)
138		r += close(i);
139	return r;
140}
141#endif
142
143/* Close our fd's */
144int
145close_fds(void)
146{
147	int fd;
148
149	if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1)
150		return -1;
151
152	dup2(fd, fileno(stdin));
153	dup2(fd, fileno(stdout));
154	dup2(fd, fileno(stderr));
155	if (fd > 2)
156		close(fd);
157	return 0;
158}
159
160int
161set_cloexec(int fd)
162{
163	int flags;
164
165	if ((flags = fcntl(fd, F_GETFD, 0)) == -1
166	    || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
167	{
168		logger(LOG_ERR, "fcntl: %s", strerror(errno));
169		return -1;
170	}
171	return 0;
172}
173
174int
175set_nonblock(int fd)
176{
177	int flags;
178
179	if ((flags = fcntl(fd, F_GETFL, 0)) == -1
180	    || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
181	{
182		logger(LOG_ERR, "fcntl: %s", strerror(errno));
183		return -1;
184	}
185	return 0;
186}
187
188/* Handy function to get the time.
189 * We only care about time advancements, not the actual time itself
190 * Which is why we use CLOCK_MONOTONIC, but it is not available on all
191 * platforms.
192 */
193#define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
194int
195get_monotonic(struct timeval *tp)
196{
197	static int posix_clock_set = 0;
198#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
199	struct timespec ts;
200	static clockid_t posix_clock;
201
202	if (posix_clock_set == 0) {
203		if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
204			posix_clock = CLOCK_MONOTONIC;
205			clock_monotonic = posix_clock_set = 1;
206		}
207	}
208
209	if (clock_monotonic) {
210		if (clock_gettime(posix_clock, &ts) == 0) {
211			tp->tv_sec = ts.tv_sec;
212			tp->tv_usec = ts.tv_nsec / 1000;
213			return 0;
214		}
215	}
216#elif defined(__APPLE__)
217#define NSEC_PER_SEC 1000000000
218	/* We can use mach kernel functions here.
219	 * This is crap though - why can't they implement clock_gettime?*/
220	static struct mach_timebase_info info = { 0, 0 };
221	static double factor = 0.0;
222	uint64_t nano;
223	long rem;
224
225	if (posix_clock_set == 0) {
226		if (mach_timebase_info(&info) == KERN_SUCCESS) {
227			factor = (double)info.numer / (double)info.denom;
228			clock_monotonic = posix_clock_set = 1;
229		}
230	}
231	if (clock_monotonic) {
232		nano = mach_absolute_time();
233		if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
234			nano *= factor;
235		tp->tv_sec = nano / NSEC_PER_SEC;
236		rem = nano % NSEC_PER_SEC;
237		if (rem < 0) {
238			tp->tv_sec--;
239			rem += NSEC_PER_SEC;
240		}
241		tp->tv_usec = rem / 1000;
242		return 0;
243	}
244#endif
245
246	/* Something above failed, so fall back to gettimeofday */
247	if (!posix_clock_set) {
248		logger(LOG_WARNING, NO_MONOTONIC);
249		posix_clock_set = 1;
250	}
251	return gettimeofday(tp, NULL);
252}
253
254time_t
255uptime(void)
256{
257	struct timeval tv;
258
259	if (get_monotonic(&tv) == -1)
260		return -1;
261	return tv.tv_sec;
262}
263
264int
265writepid(int fd, pid_t pid)
266{
267	char spid[16];
268	ssize_t len;
269
270	if (ftruncate(fd, (off_t)0) == -1)
271		return -1;
272	snprintf(spid, sizeof(spid), "%u\n", pid);
273	len = pwrite(fd, spid, strlen(spid), (off_t)0);
274	if (len != (ssize_t)strlen(spid))
275		return -1;
276	return 0;
277}
278
279void *
280xmalloc(size_t s)
281{
282	void *value = malloc(s);
283
284	if (value)
285		return value;
286	logger(LOG_ERR, "memory exhausted");
287	exit (EXIT_FAILURE);
288	/* NOTREACHED */
289}
290
291void *
292xzalloc(size_t s)
293{
294	void *value = xmalloc(s);
295
296	memset(value, 0, s);
297	return value;
298}
299
300void *
301xrealloc(void *ptr, size_t s)
302{
303	void *value = realloc(ptr, s);
304
305	if (value)
306		return (value);
307	logger(LOG_ERR, "memory exhausted");
308	exit(EXIT_FAILURE);
309	/* NOTREACHED */
310}
311
312char *
313xstrdup(const char *str)
314{
315	char *value;
316
317	if (!str)
318		return NULL;
319
320	if ((value = strdup(str)))
321		return value;
322
323	logger(LOG_ERR, "memory exhausted");
324	exit(EXIT_FAILURE);
325	/* NOTREACHED */
326}
327