• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2015 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 <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #ifdef BSD
42 #  include <paths.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <time.h>
51 #include <unistd.h>
52 
53 #include "common.h"
54 #include "dhcpcd.h"
55 #include "if-options.h"
56 
57 #ifndef _PATH_DEVNULL
58 #  define _PATH_DEVNULL "/dev/null"
59 #endif
60 
61 const char *
get_hostname(char * buf,size_t buflen,int short_hostname)62 get_hostname(char *buf, size_t buflen, int short_hostname)
63 {
64 	char *p;
65 
66 	if (gethostname(buf, buflen) != 0)
67 		return NULL;
68 	buf[buflen - 1] = '\0';
69 	if (strcmp(buf, "(none)") == 0 ||
70 	    strcmp(buf, "localhost") == 0 ||
71 	    strncmp(buf, "localhost.", strlen("localhost.")) == 0 ||
72 	    buf[0] == '.')
73 		return NULL;
74 
75 	if (short_hostname) {
76 		p = strchr(buf, '.');
77 		if (p)
78 			*p = '\0';
79 	}
80 
81 	return buf;
82 }
83 
84 /* Handy function to get the time.
85  * We only care about time advancements, not the actual time itself
86  * Which is why we use CLOCK_MONOTONIC, but it is not available on all
87  * platforms.
88  */
89 #define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
90 int
get_monotonic(struct timespec * ts)91 get_monotonic(struct timespec *ts)
92 {
93 
94 #if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
95 	return clock_gettime(CLOCK_MONOTONIC, ts);
96 #elif defined(__APPLE__)
97 	/* We can use mach kernel functions here.
98 	 * This is crap though - why can't they implement clock_gettime?*/
99 	static struct mach_timebase_info info = { 0, 0 };
100 	static double factor = 0.0;
101 	uint64_t nano;
102 	long rem;
103 
104 	if (!posix_clock_set) {
105 		if (mach_timebase_info(&info) == KERN_SUCCESS) {
106 			factor = (double)info.numer / (double)info.denom;
107 			clock_monotonic = posix_clock_set = 1;
108 		}
109 	}
110 	if (clock_monotonic) {
111 		nano = mach_absolute_time();
112 		if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
113 			nano *= factor;
114 		ts->tv_sec = nano / NSEC_PER_SEC;
115 		ts->tv_nsec = nano % NSEC_PER_SEC;
116 		if (ts->tv_nsec < 0) {
117 			ts->tv_sec--;
118 			ts->tv_nsec += NSEC_PER_SEC;
119 		}
120 		return 0;
121 	}
122 #endif
123 
124 #if 0
125 	/* Something above failed, so fall back to gettimeofday */
126 	if (!posix_clock_set) {
127 		logger(NULL, LOG_WARNING, NO_MONOTONIC);
128 		posix_clock_set = 1;
129 	}
130 #endif
131 	{
132 		struct timeval tv;
133 		if (gettimeofday(&tv, NULL) == 0) {
134 			TIMEVAL_TO_TIMESPEC(&tv, ts);
135 			return 0;
136 		}
137 	}
138 
139 	return -1;
140 }
141 
142 #if USE_LOGFILE
143 void
logger_open(struct dhcpcd_ctx * ctx)144 logger_open(struct dhcpcd_ctx *ctx)
145 {
146 
147 	if (ctx->logfile) {
148 		int f = O_CREAT | O_APPEND | O_TRUNC;
149 
150 #ifdef O_CLOEXEC
151 		f |= O_CLOEXEC;
152 #endif
153 		ctx->log_fd = open(ctx->logfile, O_WRONLY | f, 0644);
154 		if (ctx->log_fd == -1)
155 			warn("open: %s", ctx->logfile);
156 #ifndef O_CLOEXEC
157 		else {
158 			if (fcntl(ctx->log_fd, F_GETFD, &f) == -1 ||
159 			    fcntl(ctx->log_fd, F_SETFD, f | FD_CLOEXEC) == -1)
160 				warn("fcntl: %s", ctx->logfile);
161 		}
162 #endif
163 	} else
164 		openlog(PACKAGE, LOG_PID | LOG_PERROR, LOG_DAEMON);
165 }
166 
167 void
logger_close(struct dhcpcd_ctx * ctx)168 logger_close(struct dhcpcd_ctx *ctx)
169 {
170 
171 	if (ctx->log_fd != -1) {
172 		close(ctx->log_fd);
173 		ctx->log_fd = -1;
174 	}
175 	closelog();
176 }
177 
178 void
logger(struct dhcpcd_ctx * ctx,int pri,const char * fmt,...)179 logger(struct dhcpcd_ctx *ctx, int pri, const char *fmt, ...)
180 {
181 	va_list va;
182 	int serrno;
183 #ifndef HAVE_PRINTF_M
184 	char fmt_cpy[1024];
185 #endif
186 
187 	if (pri >= LOG_DEBUG && ctx && !(ctx->options & DHCPCD_DEBUG))
188 		return;
189 
190 	serrno = errno;
191 	va_start(va, fmt);
192 
193 #ifndef HAVE_PRINTF_M
194 	/* Print strerrno(errno) in place of %m */
195 	if (ctx == NULL || !(ctx->options & DHCPCD_QUIET) || ctx->log_fd != -1)
196 	{
197 		const char *p;
198 		char *fp = fmt_cpy, *serr = NULL;
199 		size_t fmt_left = sizeof(fmt_cpy) - 1, fmt_wrote;
200 
201 		for (p = fmt; *p != '\0'; p++) {
202 			if (p[0] == '%' && p[1] == '%') {
203 				if (fmt_left < 2)
204 					break;
205 				*fp++ = '%';
206 				*fp++ = '%';
207 				fmt_left -= 2;
208 				p++;
209 			} else if (p[0] == '%' && p[1] == 'm') {
210 				if (serr == NULL)
211 					serr = strerror(serrno);
212 				fmt_wrote = strlcpy(fp, serr, fmt_left);
213 				if (fmt_wrote > fmt_left)
214 					break;
215 				fp += fmt_wrote;
216 				fmt_left -= fmt_wrote;
217 				p++;
218 			} else {
219 				*fp++ = *p;
220 				--fmt_left;
221 			}
222 			if (fmt_left == 0)
223 				break;
224 		}
225 		*fp++ = '\0';
226 		fmt = fmt_cpy;
227 	}
228 
229 #endif
230 
231 	if (ctx == NULL || !(ctx->options & DHCPCD_QUIET)) {
232 		va_list vac;
233 
234 		va_copy(vac, va);
235 		vfprintf(pri <= LOG_ERR ? stderr : stdout, fmt, vac);
236 		fputc('\n', pri <= LOG_ERR ? stderr : stdout);
237 		va_end(vac);
238 	}
239 
240 #ifdef HAVE_PRINTF_M
241 	errno = serrno;
242 #endif
243 	if (ctx && ctx->log_fd != -1) {
244 		struct timeval tv;
245 		char buf[32];
246 
247 		/* Write the time, syslog style. month day time - */
248 		if (gettimeofday(&tv, NULL) != -1) {
249 			time_t now;
250 			struct tm tmnow;
251 
252 			tzset();
253 			now = tv.tv_sec;
254 			localtime_r(&now, &tmnow);
255 			strftime(buf, sizeof(buf), "%b %d %T ", &tmnow);
256 			dprintf(ctx->log_fd, "%s", buf);
257 		}
258 
259 		vdprintf(ctx->log_fd, fmt, va);
260 		dprintf(ctx->log_fd, "\n");
261 	} else
262 		vsyslog(pri, fmt, va);
263 	va_end(va);
264 }
265 #endif
266 
267 ssize_t
setvar(struct dhcpcd_ctx * ctx,char *** e,const char * prefix,const char * var,const char * value)268 setvar(struct dhcpcd_ctx *ctx,
269     char ***e, const char *prefix, const char *var, const char *value)
270 {
271 	size_t len = strlen(var) + strlen(value) + 3;
272 
273 	if (prefix)
274 		len += strlen(prefix) + 1;
275 	**e = malloc(len);
276 	if (**e == NULL) {
277 		logger(ctx, LOG_ERR, "%s: %m", __func__);
278 		return -1;
279 	}
280 	if (prefix)
281 		snprintf(**e, len, "%s_%s=%s", prefix, var, value);
282 	else
283 		snprintf(**e, len, "%s=%s", var, value);
284 	(*e)++;
285 	return (ssize_t)len;
286 }
287 
288 ssize_t
setvard(struct dhcpcd_ctx * ctx,char *** e,const char * prefix,const char * var,size_t value)289 setvard(struct dhcpcd_ctx *ctx,
290     char ***e, const char *prefix, const char *var, size_t value)
291 {
292 	char buffer[32];
293 
294 	snprintf(buffer, sizeof(buffer), "%zu", value);
295 	return setvar(ctx, e, prefix, var, buffer);
296 }
297 
298 
299 time_t
uptime(void)300 uptime(void)
301 {
302 	struct timespec tv;
303 
304 	if (get_monotonic(&tv) == -1)
305 		return -1;
306 	return tv.tv_sec;
307 }
308 
309 char *
hwaddr_ntoa(const unsigned char * hwaddr,size_t hwlen,char * buf,size_t buflen)310 hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen, char *buf, size_t buflen)
311 {
312 	char *p;
313 	size_t i;
314 
315 	if (buf == NULL) {
316 		return NULL;
317 	}
318 
319 	if (hwlen * 3 > buflen) {
320 		errno = ENOBUFS;
321 		return 0;
322 	}
323 
324 	p = buf;
325 	for (i = 0; i < hwlen; i++) {
326 		if (i > 0)
327 			*p ++= ':';
328 		p += snprintf(p, 3, "%.2x", hwaddr[i]);
329 	}
330 	*p ++= '\0';
331 	return buf;
332 }
333 
334 size_t
hwaddr_aton(unsigned char * buffer,const char * addr)335 hwaddr_aton(unsigned char *buffer, const char *addr)
336 {
337 	char c[3];
338 	const char *p = addr;
339 	unsigned char *bp = buffer;
340 	size_t len = 0;
341 
342 	c[2] = '\0';
343 	while (*p) {
344 		c[0] = *p++;
345 		c[1] = *p++;
346 		/* Ensure that digits are hex */
347 		if (isxdigit((unsigned char)c[0]) == 0 ||
348 		    isxdigit((unsigned char)c[1]) == 0)
349 		{
350 			errno = EINVAL;
351 			return 0;
352 		}
353 		/* We should have at least two entries 00:01 */
354 		if (len == 0 && *p == '\0') {
355 			errno = EINVAL;
356 			return 0;
357 		}
358 		/* Ensure that next data is EOL or a seperator with data */
359 		if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
360 			errno = EINVAL;
361 			return 0;
362 		}
363 		if (*p)
364 			p++;
365 		if (bp)
366 			*bp++ = (unsigned char)strtol(c, NULL, 16);
367 		len++;
368 	}
369 	return len;
370 }
371