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