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