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, int transport, const char *rendezvous)
61 {
62 	struct i_fn_args *a;
63 	char *filename;
64 
65 	AURA_MALLOC(a, i_fn_args);
66 
67 	a->c = NULL;
68 	a->os_root = aura_strdup(os_root);
69 	a->cfg_root = "";
70 	a->name = "";
71 	a->short_desc = "";
72 	a->long_desc = "";
73 	a->result = 0;
74 	a->log = NULL;
75 	a->s = NULL;
76 	a->tmp = NULL;
77 	a->temp_files = NULL;
78 	a->cmd_names = NULL;
79 
80 	asprintf(&filename, "%sinstall.log", def_tmp_dir);
81 	a->log = fopen(filename, "w");
82 	free(filename);
83 	if (a->log == NULL) {
84 		i_fn_args_free(a);
85 		return(NULL);
86 	}
87 
88 	i_log(a, "Installer started");
89 	i_log(a, "-----------------");
90 
91 	i_log(a, "+ Creating DFUI connection on ``%s''\n", rendezvous);
92 
93 	if ((a->c = dfui_connection_new(transport, rendezvous)) == NULL) {
94 		i_log(a, "! ERROR: Couldn't create connection on ``%s''\n", rendezvous);
95 		i_fn_args_free(a);
96 		return(NULL);
97 	}
98 
99 	i_log(a, "+ Connecting on ``%s''\n", rendezvous);
100 
101 	if (!dfui_be_start(a->c)) {
102 		i_log(a, "! ERROR: Couldn't connect to frontend on ``%s''\n", rendezvous);
103 		i_fn_args_free(a);
104 		return(NULL);
105 	}
106 
107 	if ((a->s = storage_new()) == NULL) {
108 		i_log(a, "! ERROR: Couldn't create storage descriptor");
109 		i_fn_args_free(a);
110 		return(NULL);
111 	}
112 
113 	a->tmp = def_tmp_dir;	/* XXX temporarily set to this */
114 	a->temp_files = aura_dict_new(23, AURA_DICT_HASH);
115 	a->cmd_names = config_vars_new();
116 	if (!config_vars_read(a, a->cmd_names, CONFIG_TYPE_SH,
117 	    "usr/share/installer/cmdnames.conf")) {
118 		i_log(a, "! ERROR: Couldn't read cmdnames config file");
119 		i_fn_args_free(a);
120 		return(NULL);
121 	}
122 
123 	a->tmp = cmd_name(a, "INSTALLER_TEMP");
124 
125 	i_log(a, "+ Starting installer state machine");
126 
127 	return(a);
128 }
129 
130 void
131 i_fn_args_free(struct i_fn_args *a)
132 {
133 	if (a != NULL) {
134 		if (a->temp_files != NULL) {
135 			temp_files_clean(a);
136 			aura_dict_free(a->temp_files);
137 		}
138 		if (a->cmd_names != NULL) {
139 			config_vars_free(a->cmd_names);
140 		}
141 		if (a->s != NULL) {
142 			storage_free(a->s);
143 		}
144 		if (a->c != NULL) {
145 			dfui_be_stop(a->c);
146 		}
147 		if (a->log != NULL) {
148 			fclose(a->log);
149 		}
150 		AURA_FREE(a, i_fn_args);
151 	}
152 }
153 
154 /*** INSTALLER CONTEXT FUNCTIONS ***/
155 
156 void
157 i_log(struct i_fn_args *a, const char *fmt, ...)
158 {
159 	va_list args;
160 
161 	va_start(args, fmt);
162 	vfprintf(stderr, fmt, args);
163 	fprintf(stderr, "\n");
164 	va_end(args);
165 	if (a->log != NULL) {
166 		va_start(args, fmt);
167 		vfprintf(a->log, fmt, args);
168 		fprintf(a->log, "\n");
169 		fflush(a->log);
170 		va_end(args);
171 	}
172 	va_end(args);
173 }
174 
175 /*** UTILITY ***/
176 
177 void
178 abort_backend(void)
179 {
180 	exit(1);
181 }
182 
183 int
184 assert_clean(struct dfui_connection *c, const char *name, const char *field,
185 	     const char *not_allowed)
186 {
187 	if (strpbrk(field, not_allowed) != NULL) {
188 		inform(c, "The %s field may not contain any of the "
189 		    "following characters:\n\n%s",
190 		    name, not_allowed);
191 		return(0);
192 	} else {
193 		return(1);
194 	}
195 }
196 
197 /*
198  * Expects a leading 0x.
199  */
200 int
201 hex_to_int(const char *hex, int *result)
202 {
203 	int i, a = 0;
204 	char d;
205 
206 	if (strncmp(hex, "0x", 2) != 0)
207 		return(0);
208 
209 	for (i = 2; hex[i] != '\0'; i++) {
210 		d = toupper(hex[i]);
211 		if (isspace(d))
212 			continue;
213 		if (isdigit(d))
214 			a = a * 16 + (d - '0');
215 		else if (d >= 'A' && d <= 'F')
216 			a = a * 16 + (d + 10 - 'A');
217 		else
218 			return(0);
219 	}
220 
221 	*result = a;
222 	return(1);
223 }
224 
225 int
226 first_non_space_char_is(const char *line, char x)
227 {
228 	int i;
229 
230 	for (i = 0; line[i] != '\0'; i++) {
231 		if (isspace(line[i]))
232 			continue;
233 		if (line[i] == x)
234 			return(1);
235 		return(0);
236 	}
237 
238 	return(0);
239 }
240 
241 const char *
242 capacity_to_string(long capacity)
243 {
244 	static char string[6];
245 
246 	if (capacity < 0)
247 		strlcpy(string, "*", 2);
248 	else
249 		humanize_number(string, sizeof(string),
250 		    (int64_t)capacity * 1024 * 1024, "",
251 		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
252 
253 	return(string);
254 }
255 
256 int
257 string_to_capacity(const char *string, long *capacity)
258 {
259 	int error;
260 	int64_t result;
261 
262 	if (!strcmp(string, "*")) {
263 		*capacity = -1;
264 		return(1);
265 	}
266 	error = dehumanize_number(string, &result);
267 	if (error != 0)
268 		return(0);
269 	result /= 1024 * 1024;
270 	if (result == 0)
271 		return(0);
272 	*capacity = result;
273 	return(1);
274 }
275 
276 /*
277  * Round a number up to the nearest power of two.
278  */
279 unsigned long
280 next_power_of_two(unsigned long n)
281 {
282 	unsigned long p, op;
283 
284 	p = 1;
285 	op = 0;
286 	while (p < n && p > op) {
287 		op = p;
288 		p <<= 1;
289 	}
290 
291 	return(p > op ? p : n);
292 }
293 
294 /*
295  * Returns file name without extension.
296  * e.g.
297  *	ru.koi8-r.kbd -> ru.koi8-r
298  *	README -> README
299  *
300  * Caller is responsible for freeing the string returned.
301  */
302 char *
303 filename_noext(const char *filename)
304 {
305 	int i;
306 	char *buffer, *p;
307 
308 	buffer = aura_strdup(filename);
309 
310 	if (strlen(filename) == 0) {
311 		buffer[0] = '\0';
312 		return(buffer);
313 	}
314 
315 	p = strrchr(filename, '.');
316 
317 	if (p != NULL) {
318 		i = strlen(filename) - strlen(p);
319 		buffer[i] = 0;
320 	}
321 
322 	return(buffer);
323 }
324 
325 /*
326  * Temp files
327  */
328 
329 int
330 temp_file_add(struct i_fn_args *a, const char *filename)
331 {
332 	aura_dict_store(a->temp_files, filename, strlen(filename) + 1, "", 1);
333 	return(1);
334 }
335 
336 int
337 temp_files_clean(struct i_fn_args *a)
338 {
339 	void *rk;
340 	size_t rk_len;
341 	char *filename;
342 
343 	aura_dict_rewind(a->temp_files);
344 	while (!aura_dict_eof(a->temp_files)) {
345 		aura_dict_get_current_key(a->temp_files, &rk, &rk_len);
346 		asprintf(&filename, "%s%s", a->tmp, (char *)rk);
347 		(void)unlink(filename);	/* not much we can do if it fails */
348 		free(filename);
349 		aura_dict_next(a->temp_files);
350 	}
351 	return(1);
352 }
353 
354 /*
355  * Command names
356  */
357 const char *
358 cmd_name(const struct i_fn_args *a, const char *cmd_key)
359 {
360 	const char *name;
361 
362 	name = config_var_get(a->cmd_names, cmd_key);
363 	if (strcmp(name, "") == 0)
364 		return("bin/echo");	/* XXX usr/local/sbin/error? */
365 	else
366 		return(name);
367 }
368