1 /*
2  * Copyright (c)2004 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of the DragonFly Project nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * functions.c
36  * Generic functions for installer.
37  * $Id: functions.c,v 1.22 2005/02/06 21:05:18 cpressey Exp $
38  */
39 
40 #include <ctype.h>
41 #include <stdio.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <libutil.h>
47 
48 #include "libaura/mem.h"
49 #include "libaura/dict.h"
50 
51 #include "libdfui/dfui.h"
52 
53 #include "functions.h"
54 #include "diskutil.h"
55 #include "uiutil.h"
56 
57 /*** INSTALLER CONTEXT CONSTRUCTOR ***/
58 
59 struct i_fn_args *
60 i_fn_args_new(const char *os_root, const char *def_tmp_dir,
61 	      const char *def_cmds_file, int transport, const char *rendezvous)
62 {
63 	struct i_fn_args *a;
64 	char *filename;
65 
66 	AURA_MALLOC(a, i_fn_args);
67 
68 	a->c = NULL;
69 	a->os_root = aura_strdup(os_root);
70 	a->cfg_root = "";
71 	a->name = "";
72 	a->short_desc = "";
73 	a->long_desc = "";
74 	a->result = 0;
75 	a->log = NULL;
76 	a->s = NULL;
77 	a->tmp = NULL;
78 	a->temp_files = NULL;
79 	a->cmd_names = NULL;
80 
81 	asprintf(&filename, "%sinstall.log", def_tmp_dir);
82 	a->log = fopen(filename, "w");
83 	free(filename);
84 	if (a->log == NULL) {
85 		i_fn_args_free(a);
86 		return(NULL);
87 	}
88 
89 	i_log(a, "Installer started");
90 	i_log(a, "-----------------");
91 
92 	i_log(a, "+ Creating DFUI connection on ``%s''\n", rendezvous);
93 
94 	if ((a->c = dfui_connection_new(transport, rendezvous)) == NULL) {
95 		i_log(a, "! ERROR: Couldn't create connection on ``%s''\n", rendezvous);
96 		i_fn_args_free(a);
97 		return(NULL);
98 	}
99 
100 	i_log(a, "+ Connecting on ``%s''\n", rendezvous);
101 
102 	if (!dfui_be_start(a->c)) {
103 		i_log(a, "! ERROR: Couldn't connect to frontend on ``%s''\n", rendezvous);
104 		i_fn_args_free(a);
105 		return(NULL);
106 	}
107 
108 	if ((a->s = storage_new()) == NULL) {
109 		i_log(a, "! ERROR: Couldn't create storage descriptor");
110 		i_fn_args_free(a);
111 		return(NULL);
112 	}
113 
114 	a->tmp = def_tmp_dir;	/* XXX temporarily set to this */
115 	a->temp_files = aura_dict_new(23, AURA_DICT_HASH);
116 	a->cmd_names = config_vars_new();
117 	if (!config_vars_read(a, a->cmd_names, CONFIG_TYPE_SH, "%s",
118 		def_cmds_file)) {
119 		i_log(a, "! ERROR: Couldn't read cmdnames config file");
120 		i_fn_args_free(a);
121 		return(NULL);
122 	}
123 
124 	a->tmp = cmd_name(a, "INSTALLER_TEMP");
125 
126 	i_log(a, "+ Starting installer state machine");
127 
128 	return(a);
129 }
130 
131 void
132 i_fn_args_free(struct i_fn_args *a)
133 {
134 	if (a != NULL) {
135 		if (a->temp_files != NULL) {
136 			temp_files_clean(a);
137 			aura_dict_free(a->temp_files);
138 		}
139 		if (a->cmd_names != NULL) {
140 			config_vars_free(a->cmd_names);
141 		}
142 		if (a->s != NULL) {
143 			storage_free(a->s);
144 		}
145 		if (a->c != NULL) {
146 			dfui_be_stop(a->c);
147 		}
148 		if (a->log != NULL) {
149 			fclose(a->log);
150 		}
151 		AURA_FREE(a, i_fn_args);
152 	}
153 }
154 
155 /*** INSTALLER CONTEXT FUNCTIONS ***/
156 
157 void
158 i_log(struct i_fn_args *a, const char *fmt, ...)
159 {
160 	va_list args;
161 
162 	va_start(args, fmt);
163 	vfprintf(stderr, fmt, args);
164 	fprintf(stderr, "\n");
165 	va_end(args);
166 	if (a->log != NULL) {
167 		va_start(args, fmt);
168 		vfprintf(a->log, fmt, args);
169 		fprintf(a->log, "\n");
170 		fflush(a->log);
171 		va_end(args);
172 	}
173 	va_end(args);
174 }
175 
176 /*** UTILITY ***/
177 
178 void
179 abort_backend(void)
180 {
181 	exit(1);
182 }
183 
184 int
185 assert_clean(struct dfui_connection *c, const char *name, const char *field,
186 	     const char *not_allowed)
187 {
188 	if (strpbrk(field, not_allowed) != NULL) {
189 		inform(c, "The %s field may not contain any of the "
190 		    "following characters:\n\n%s",
191 		    name, not_allowed);
192 		return(0);
193 	} else {
194 		return(1);
195 	}
196 }
197 
198 /*
199  * Expects a leading 0x.
200  */
201 int
202 hex_to_int(const char *hex, int *result)
203 {
204 	int i, a = 0;
205 	char d;
206 
207 	if (strncmp(hex, "0x", 2) != 0)
208 		return(0);
209 
210 	for (i = 2; hex[i] != '\0'; i++) {
211 		d = toupper(hex[i]);
212 		if (isspace(d))
213 			continue;
214 		if (isdigit(d))
215 			a = a * 16 + (d - '0');
216 		else if (d >= 'A' && d <= 'F')
217 			a = a * 16 + (d + 10 - 'A');
218 		else
219 			return(0);
220 	}
221 
222 	*result = a;
223 	return(1);
224 }
225 
226 int
227 first_non_space_char_is(const char *line, char x)
228 {
229 	int i;
230 
231 	for (i = 0; line[i] != '\0'; i++) {
232 		if (isspace(line[i]))
233 			continue;
234 		if (line[i] == x)
235 			return(1);
236 		return(0);
237 	}
238 
239 	return(0);
240 }
241 
242 const char *
243 capacity_to_string(long capacity)
244 {
245 	static char string[6];
246 
247 	if (capacity < 0)
248 		strlcpy(string, "*", 2);
249 	else
250 		humanize_number(string, sizeof(string),
251 		    (int64_t)capacity * 1024 * 1024, "",
252 		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
253 
254 	return(string);
255 }
256 
257 int
258 string_to_capacity(const char *string, long *capacity)
259 {
260 	int error;
261 	int64_t result;
262 
263 	if (!strcmp(string, "*")) {
264 		*capacity = -1;
265 		return(1);
266 	}
267 	error = dehumanize_number(string, &result);
268 	if (error != 0)
269 		return(0);
270 	result /= 1024 * 1024;
271 	if (result == 0)
272 		return(0);
273 	*capacity = result;
274 	return(1);
275 }
276 
277 /*
278  * Round a number up to the nearest power of two.
279  */
280 unsigned long
281 next_power_of_two(unsigned long n)
282 {
283 	unsigned long p, op;
284 
285 	p = 1;
286 	op = 0;
287 	while (p < n && p > op) {
288 		op = p;
289 		p <<= 1;
290 	}
291 
292 	return(p > op ? p : n);
293 }
294 
295 /*
296  * Returns file name without extension.
297  * e.g.
298  *	ru.koi8-r.kbd -> ru.koi8-r
299  *	README -> README
300  *
301  * Caller is responsible for freeing the string returned.
302  */
303 char *
304 filename_noext(const char *filename)
305 {
306 	int i;
307 	char *buffer, *p;
308 
309 	buffer = aura_strdup(filename);
310 
311 	if (strlen(filename) == 0) {
312 		buffer[0] = '\0';
313 		return(buffer);
314 	}
315 
316 	p = strrchr(filename, '.');
317 
318 	if (p != NULL) {
319 		i = strlen(filename) - strlen(p);
320 		buffer[i] = 0;
321 	}
322 
323 	return(buffer);
324 }
325 
326 /*
327  * Temp files
328  */
329 
330 int
331 temp_file_add(struct i_fn_args *a, const char *filename)
332 {
333 	aura_dict_store(a->temp_files, filename, strlen(filename) + 1, "", 1);
334 	return(1);
335 }
336 
337 int
338 temp_files_clean(struct i_fn_args *a)
339 {
340 	void *rk;
341 	size_t rk_len;
342 	char *filename;
343 
344 	aura_dict_rewind(a->temp_files);
345 	while (!aura_dict_eof(a->temp_files)) {
346 		aura_dict_get_current_key(a->temp_files, &rk, &rk_len);
347 		asprintf(&filename, "%s%s", a->tmp, (char *)rk);
348 		(void)unlink(filename);	/* not much we can do if it fails */
349 		free(filename);
350 		aura_dict_next(a->temp_files);
351 	}
352 	return(1);
353 }
354 
355 /*
356  * Command names
357  */
358 const char *
359 cmd_name(const struct i_fn_args *a, const char *cmd_key)
360 {
361 	const char *name;
362 
363 	name = config_var_get(a->cmd_names, cmd_key);
364 	if (strcmp(name, "") == 0)
365 		return("bin/echo");	/* XXX usr/local/sbin/error? */
366 	else
367 		return(name);
368 }
369