xref: /dragonfly/usr.sbin/autofs/defined.c (revision 3d33658b)
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("DOLLAR", "$");
250 	defined_add("HOST", name.nodename);
251 	defined_add("OSNAME", name.sysname);
252 	defined_add("OSREL", name.release);
253 	defined_add("OSVERS", name.version);
254 }
255