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