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