1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2014 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33 /* 34 * All the "defined" stuff is for handling variables, 35 * such as ${OSNAME}, in maps. 36 */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/types.h> 42 #include <sys/time.h> 43 #include <sys/ioctl.h> 44 #include <sys/param.h> 45 #include <sys/linker.h> 46 #include <sys/mount.h> 47 #include <sys/socket.h> 48 #include <sys/stat.h> 49 #include <sys/wait.h> 50 #include <sys/utsname.h> 51 #include <assert.h> 52 #include <ctype.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <libgen.h> 56 #include <libutil.h> 57 #include <netdb.h> 58 #include <signal.h> 59 #include <stdbool.h> 60 #include <stdint.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 66 #include "common.h" 67 68 static TAILQ_HEAD(, defined_value) defined_values; 69 70 static const char * 71 defined_find(const char *name) 72 { 73 struct defined_value *d; 74 75 TAILQ_FOREACH(d, &defined_values, d_next) { 76 if (strcmp(d->d_name, name) == 0) 77 return (d->d_value); 78 } 79 80 return (NULL); 81 } 82 83 char * 84 defined_expand(const char *string) 85 { 86 const char *value; 87 char c, *expanded, *name; 88 int i, ret, before_len = 0, name_off = 0, name_len = 0, after_off = 0; 89 bool backslashed = false, bracketed = false; 90 91 expanded = checked_strdup(string); 92 93 for (i = 0; string[i] != '\0'; i++) { 94 c = string[i]; 95 if (c == '\\' && backslashed == false) { 96 backslashed = true; 97 continue; 98 } 99 if (backslashed) { 100 backslashed = false; 101 continue; 102 } 103 backslashed = false; 104 if (c != '$') 105 continue; 106 107 /* 108 * The 'before_len' variable contains the number 109 * of characters before the '$'. 110 */ 111 before_len = i; 112 assert(i + 1 < (int)strlen(string)); 113 if (string[i + 1] == '{') 114 bracketed = true; 115 116 if (string[i + 1] == '\0') { 117 log_warnx("truncated variable"); 118 return (NULL); 119 } 120 121 /* 122 * Skip '$'. 123 */ 124 i++; 125 126 if (bracketed) { 127 if (string[i + 1] == '\0') { 128 log_warnx("truncated variable"); 129 return (NULL); 130 } 131 132 /* 133 * Skip '{'. 134 */ 135 i++; 136 } 137 138 /* 139 * The 'name_off' variable contains the number 140 * of characters before the variable name, 141 * including the "$" or "${". 142 */ 143 name_off = i; 144 145 for (; string[i] != '\0'; i++) { 146 c = string[i]; 147 /* 148 * XXX: Decide on the set of characters that can be 149 * used in a variable name. 150 */ 151 if (isalnum(c) || c == '_') 152 continue; 153 154 /* 155 * End of variable name. 156 */ 157 if (bracketed) { 158 if (c != '}') 159 continue; 160 161 /* 162 * The 'after_off' variable contains the number 163 * of characters before the rest of the string, 164 * i.e. after the variable name. 165 */ 166 after_off = i + 1; 167 assert(i > 1); 168 assert(i - 1 > name_off); 169 name_len = i - name_off; 170 break; 171 } 172 173 after_off = i; 174 assert(i > 1); 175 assert(i > name_off); 176 name_len = i - name_off; 177 break; 178 } 179 180 name = strndup(string + name_off, name_len); 181 if (name == NULL) 182 log_err(1, "strndup"); 183 value = defined_find(name); 184 if (value == NULL) { 185 log_warnx("undefined variable ${%s}", name); 186 return (NULL); 187 } 188 189 /* 190 * Concatenate it back. 191 */ 192 ret = asprintf(&expanded, "%.*s%s%s", 193 before_len, string, value, string + after_off); 194 if (ret < 0) 195 log_err(1, "asprintf"); 196 197 //log_debugx("\"%s\" expanded to \"%s\"", string, expanded); 198 free(name); 199 200 /* 201 * Figure out where to start searching for next variable. 202 */ 203 string = expanded; 204 i = before_len + strlen(value); 205 backslashed = bracketed = false; 206 before_len = name_off = name_len = after_off = 0; 207 assert(i <= (int)strlen(string)); 208 } 209 210 if (before_len != 0 || name_off != 0 || name_len != 0 || after_off != 0) { 211 log_warnx("truncated variable"); 212 return (NULL); 213 } 214 215 return (expanded); 216 } 217 218 static void 219 defined_add(const char *name, const char *value) 220 { 221 struct defined_value *d; 222 const char *found; 223 224 found = defined_find(name); 225 if (found != NULL) 226 log_errx(1, "variable %s already defined", name); 227 228 log_debugx("defining variable %s=%s", name, value); 229 230 d = calloc(1, sizeof(*d)); 231 if (d == NULL) 232 log_err(1, "calloc"); 233 d->d_name = checked_strdup(name); 234 d->d_value = checked_strdup(value); 235 236 TAILQ_INSERT_TAIL(&defined_values, d, d_next); 237 } 238 239 void 240 defined_parse_and_add(char *def) 241 { 242 char *name, *value; 243 244 value = def; 245 name = strsep(&value, "="); 246 247 if (value == NULL || value[0] == '\0') 248 log_errx(1, "missing variable value"); 249 if (name == NULL || name[0] == '\0') 250 log_errx(1, "missing variable name"); 251 252 defined_add(name, value); 253 } 254 255 void 256 defined_init(void) 257 { 258 struct utsname name; 259 int error; 260 261 TAILQ_INIT(&defined_values); 262 263 error = uname(&name); 264 if (error != 0) 265 log_err(1, "uname"); 266 267 defined_add("ARCH", name.machine); 268 defined_add("CPU", name.machine); 269 defined_add("HOST", name.nodename); 270 defined_add("OSNAME", name.sysname); 271 defined_add("OSREL", name.release); 272 defined_add("OSVERS", name.version); 273 } 274