Home | History | Annotate | Download | only in dhcpcd-6.8.2
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2015 Roy Marples <roy (at) 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 *
     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
     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
    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
    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
    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
    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
    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
    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 *
    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
    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