xref: /dragonfly/usr.sbin/autofs/defined.c (revision c37c9ab3)
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