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