blob: ccad014e7fc84f8f37b0ba9468c970701a2c38ef [file] [log] [blame]
Samuel Tand7ed8512015-08-13 16:11:35 -07001/*
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
61const char *
62get_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"
90int
91get_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
143void
144logger_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
Samuel Tane942e092015-08-13 17:14:37 -0700164 openlog(PACKAGE, LOG_PID | LOG_PERROR, LOG_DAEMON);
Samuel Tand7ed8512015-08-13 16:11:35 -0700165}
166
167void
168logger_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
178void
179logger(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
267ssize_t
268setvar(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
288ssize_t
289setvard(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
299time_t
300uptime(void)
301{
302 struct timespec tv;
303
304 if (get_monotonic(&tv) == -1)
305 return -1;
306 return tv.tv_sec;
307}
308
309char *
310hwaddr_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
334size_t
335hwaddr_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}