1 /* 2 * dhcpcd - DHCP client daemon 3 * SPDX-License-Identifier: BSD-2-Clause 4 * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/statvfs.h> 31 32 #include <ctype.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <stdbool.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include "common.h" 42 #include "eloop.h" 43 44 const char * 45 hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen) 46 { 47 const unsigned char *hp, *ep; 48 char *p; 49 50 /* Allow a hwlen of 0 to be an empty string. */ 51 if (buf == NULL || buflen == 0) { 52 errno = ENOBUFS; 53 return NULL; 54 } 55 56 if (hwlen * 3 > buflen) { 57 /* We should still terminate the string just in case. */ 58 buf[0] = '\0'; 59 errno = ENOBUFS; 60 return NULL; 61 } 62 63 hp = hwaddr; 64 ep = hp + hwlen; 65 p = buf; 66 while (hp < ep) { 67 if (hp != hwaddr) 68 *p++ = ':'; 69 p += snprintf(p, 3, "%.2x", *hp++); 70 } 71 *p++ = '\0'; 72 return buf; 73 } 74 75 size_t 76 hwaddr_aton(uint8_t *buffer, const char *addr) 77 { 78 char c[3]; 79 const char *p = addr; 80 uint8_t *bp = buffer; 81 size_t len = 0; 82 83 c[2] = '\0'; 84 while (*p != '\0') { 85 /* Skip separators */ 86 c[0] = *p++; 87 switch (c[0]) { 88 case '\n': /* long duid split on lines */ 89 case ':': /* typical mac address */ 90 case '-': /* uuid */ 91 continue; 92 } 93 c[1] = *p++; 94 /* Ensure that digits are hex */ 95 if (isxdigit((unsigned char)c[0]) == 0 || 96 isxdigit((unsigned char)c[1]) == 0) { 97 errno = EINVAL; 98 return 0; 99 } 100 /* We should have at least two entries 00:01 */ 101 if (len == 0 && *p == '\0') { 102 errno = EINVAL; 103 return 0; 104 } 105 if (bp) 106 *bp++ = (uint8_t)strtol(c, NULL, 16); 107 len++; 108 } 109 return len; 110 } 111 112 ssize_t 113 readfile(const char *file, void **data, size_t *len) 114 { 115 int fd; 116 ssize_t bytes = -1; 117 struct stat st; 118 size_t nlen; 119 char *buf; 120 121 fd = open(file, O_RDONLY); 122 if (fd == -1) 123 return -1; 124 if (fstat(fd, &st) == -1) 125 goto out; 126 127 /* Ensure that what we read is NUL terminated 128 * because it's mainly text files and this just 129 * makes it easier. */ 130 nlen = (size_t)st.st_size + 1; 131 if (nlen > *len) { 132 void *ndata = realloc(*data, nlen); 133 if (ndata == NULL) 134 goto out; 135 *data = ndata; 136 *len = nlen; 137 } 138 bytes = read(fd, *data, *len - 1); 139 140 out: 141 close(fd); 142 if (bytes == -1) 143 return -1; 144 buf = *data; 145 buf[bytes] = '\0'; 146 return bytes; 147 } 148 149 ssize_t 150 writefile(const char *file, mode_t mode, const void *data, size_t len) 151 { 152 int fd; 153 ssize_t bytes; 154 155 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 156 if (fd == -1) 157 return -1; 158 bytes = write(fd, data, len); 159 close(fd); 160 return bytes; 161 } 162 163 int 164 filemtime(const char *file, time_t *time) 165 { 166 struct stat st; 167 168 if (stat(file, &st) == -1) 169 return -1; 170 *time = st.st_mtime; 171 return 0; 172 } 173 174 /* Handy routine to read very long lines in text files. 175 * This means we read the whole line and avoid any nasty buffer overflows. 176 * We strip leading space and avoid comment lines, making the code that calls 177 * us smaller. */ 178 char * 179 get_line(char **__restrict buf, size_t *__restrict buflen) 180 { 181 char *p, *c; 182 bool quoted; 183 184 do { 185 p = *buf; 186 if (*buf == NULL) 187 return NULL; 188 c = memchr(*buf, '\n', *buflen); 189 if (c == NULL) { 190 c = memchr(*buf, '\0', *buflen); 191 if (c == NULL) 192 return NULL; 193 *buflen = (size_t)(c - *buf); 194 *buf = NULL; 195 } else { 196 *c++ = '\0'; 197 *buflen -= (size_t)(c - *buf); 198 *buf = c; 199 } 200 for (; *p == ' ' || *p == '\t'; p++) 201 ; 202 } while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';'); 203 204 /* Strip embedded comments unless in a quoted string or escaped */ 205 quoted = false; 206 for (c = p; *c != '\0'; c++) { 207 if (*c == '\\') { 208 c++; /* escaped */ 209 continue; 210 } 211 if (*c == '"') 212 quoted = !quoted; 213 else if (*c == '#' && !quoted) { 214 *c = '\0'; 215 break; 216 } 217 } 218 return p; 219 } 220 221 int 222 is_root_local(void) 223 { 224 #ifdef ST_LOCAL 225 struct statvfs vfs; 226 227 if (statvfs("/", &vfs) == -1) 228 return -1; 229 return vfs.f_flag & ST_LOCAL ? 1 : 0; 230 #else 231 errno = ENOTSUP; 232 return -1; 233 #endif 234 } 235 236 uint32_t 237 lifetime_left(uint32_t lifetime, const struct timespec *acquired, 238 struct timespec *now) 239 { 240 uint32_t elapsed; 241 struct timespec n; 242 243 if (lifetime == INFINITE_LIFETIME) 244 return lifetime; 245 246 if (now == NULL) { 247 timespecclear(&n); 248 now = &n; 249 } 250 if (!timespecisset(now)) 251 clock_gettime(CLOCK_MONOTONIC, now); 252 253 elapsed = (uint32_t)eloop_timespec_diff(now, acquired, NULL); 254 if (elapsed > lifetime) 255 return 0; 256 257 return lifetime - elapsed; 258 } 259