1 /*- 2 * Copyright (c) 2016 The DragonFly Project 3 * Copyright (c) 2014 The FreeBSD Foundation 4 * All rights reserved. 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/types.h> 38 #include <sys/utsname.h> 39 #include <assert.h> 40 #include <ctype.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "common.h" 47 48 static TAILQ_HEAD(, defined_value) defined_values; 49 50 static const char * 51 defined_find(const char *name) 52 { 53 struct defined_value *d; 54 55 TAILQ_FOREACH(d, &defined_values, d_next) { 56 if (strcmp(d->d_name, name) == 0) 57 return (d->d_value); 58 } 59 60 return (NULL); 61 } 62 63 char * 64 defined_expand(const char *string) 65 { 66 const char *value; 67 char c, *expanded, *name; 68 int i, ret, before_len = 0, name_off = 0, name_len = 0, after_off = 0; 69 bool backslashed = false, bracketed = false; 70 71 expanded = checked_strdup(string); 72 73 for (i = 0; string[i] != '\0'; i++) { 74 c = string[i]; 75 if (c == '\\' && backslashed == false) { 76 backslashed = true; 77 continue; 78 } 79 if (backslashed) { 80 backslashed = false; 81 continue; 82 } 83 backslashed = false; 84 if (c != '$') 85 continue; 86 87 /* 88 * The 'before_len' variable contains the number 89 * of characters before the '$'. 90 */ 91 before_len = i; 92 assert(i + 1 < (int)strlen(string)); 93 if (string[i + 1] == '{') 94 bracketed = true; 95 96 if (string[i + 1] == '\0') { 97 log_warnx("truncated variable"); 98 return (NULL); 99 } 100 101 /* 102 * Skip '$'. 103 */ 104 i++; 105 106 if (bracketed) { 107 if (string[i + 1] == '\0') { 108 log_warnx("truncated variable"); 109 return (NULL); 110 } 111 112 /* 113 * Skip '{'. 114 */ 115 i++; 116 } 117 118 /* 119 * The 'name_off' variable contains the number 120 * of characters before the variable name, 121 * including the "$" or "${". 122 */ 123 name_off = i; 124 125 for (; string[i] != '\0'; i++) { 126 c = string[i]; 127 /* 128 * XXX: Decide on the set of characters that can be 129 * used in a variable name. 130 */ 131 if (isalnum(c) || c == '_') 132 continue; 133 134 /* 135 * End of variable name. 136 */ 137 if (bracketed) { 138 if (c != '}') 139 continue; 140 141 /* 142 * The 'after_off' variable contains the number 143 * of characters before the rest of the string, 144 * i.e. after the variable name. 145 */ 146 after_off = i + 1; 147 assert(i > 1); 148 assert(i - 1 > name_off); 149 name_len = i - name_off; 150 break; 151 } 152 153 after_off = i; 154 assert(i > 1); 155 assert(i > name_off); 156 name_len = i - name_off; 157 break; 158 } 159 160 name = strndup(string + name_off, name_len); 161 if (name == NULL) 162 log_err(1, "strndup"); 163 value = defined_find(name); 164 if (value == NULL) { 165 log_warnx("undefined variable ${%s}", name); 166 return (NULL); 167 } 168 169 /* 170 * Concatenate it back. 171 */ 172 ret = asprintf(&expanded, "%.*s%s%s", 173 before_len, string, value, string + after_off); 174 if (ret < 0) 175 log_err(1, "asprintf"); 176 177 //log_debugx("\"%s\" expanded to \"%s\"", string, expanded); 178 free(name); 179 180 /* 181 * Figure out where to start searching for next variable. 182 */ 183 string = expanded; 184 i = before_len + strlen(value); 185 backslashed = bracketed = false; 186 before_len = name_off = name_len = after_off = 0; 187 assert(i <= (int)strlen(string)); 188 } 189 190 if (before_len != 0 || name_off != 0 || name_len != 0 || after_off != 0) { 191 log_warnx("truncated variable"); 192 return (NULL); 193 } 194 195 return (expanded); 196 } 197 198 static void 199 defined_add(const char *name, const char *value) 200 { 201 struct defined_value *d; 202 const char *found; 203 204 found = defined_find(name); 205 if (found != NULL) 206 log_errx(1, "variable %s already defined", name); 207 208 log_debugx("defining variable %s=%s", name, value); 209 210 d = calloc(1, sizeof(*d)); 211 if (d == NULL) 212 log_err(1, "calloc"); 213 d->d_name = checked_strdup(name); 214 d->d_value = checked_strdup(value); 215 216 TAILQ_INSERT_TAIL(&defined_values, d, d_next); 217 } 218 219 void 220 defined_parse_and_add(char *def) 221 { 222 char *name, *value; 223 224 value = def; 225 name = strsep(&value, "="); 226 227 if (value == NULL || value[0] == '\0') 228 log_errx(1, "missing variable value"); 229 if (name == NULL || name[0] == '\0') 230 log_errx(1, "missing variable name"); 231 232 defined_add(name, value); 233 } 234 235 void 236 defined_init(void) 237 { 238 struct utsname name; 239 int error; 240 241 TAILQ_INIT(&defined_values); 242 243 error = uname(&name); 244 if (error != 0) 245 log_err(1, "uname"); 246 247 defined_add("ARCH", name.machine); 248 defined_add("CPU", name.machine); 249 defined_add("HOST", name.nodename); 250 defined_add("OSNAME", name.sysname); 251 defined_add("OSREL", name.release); 252 defined_add("OSVERS", name.version); 253 } 254