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