installer: Move the installer from contrib/ to usr.sbin/.
[dragonfly.git] / usr.sbin / installer / libinstaller / functions.c
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         if (a->log != NULL) {
164                 vfprintf(a->log, fmt, args);
165                 fprintf(a->log, "\n");
166                 fflush(a->log);
167         }
168         va_end(args);
169 }
170
171 /*** UTILITY ***/
172
173 void
174 abort_backend(void)
175 {
176         exit(1);
177 }
178
179 int
180 assert_clean(struct dfui_connection *c, const char *name, const char *field,
181              const char *not_allowed)
182 {
183         if (strpbrk(field, not_allowed) != NULL) {
184                 inform(c, "The %s field may not contain any of the "
185                     "following characters:\n\n%s",
186                     name, not_allowed);
187                 return(0);
188         } else {
189                 return(1);
190         }
191 }
192
193 /*
194  * Expects a leading 0x.
195  */
196 int
197 hex_to_int(const char *hex, int *result)
198 {
199         int i, a = 0;
200         char d;
201
202         if (strncmp(hex, "0x", 2) != 0)
203                 return(0);
204
205         for (i = 2; hex[i] != '\0'; i++) {
206                 d = toupper(hex[i]);
207                 if (isspace(d))
208                         continue;
209                 if (isdigit(d))
210                         a = a * 16 + (d - '0');
211                 else if (d >= 'A' && d <= 'F')
212                         a = a * 16 + (d + 10 - 'A');
213                 else
214                         return(0);
215         }
216
217         *result = a;
218         return(1);
219 }
220
221 int
222 first_non_space_char_is(const char *line, char x)
223 {
224         int i;
225
226         for (i = 0; line[i] != '\0'; i++) {
227                 if (isspace(line[i]))
228                         continue;
229                 if (line[i] == x)
230                         return(1);
231                 return(0);
232         }
233
234         return(0);
235 }
236
237 const char *
238 capacity_to_string(long capacity)
239 {
240         static char string[256];
241
242         if (capacity < 0)
243                 strlcpy(string, "*", 2);
244         else
245                 snprintf(string, 255, "%ldM", capacity);
246
247         return(string);
248 }
249
250 int
251 string_to_capacity(const char *string, long *capacity)
252 {
253         char unit;
254
255         unit = string[strlen(string) - 1];
256         if (!strcmp(string, "*")) {
257                 *capacity = -1;
258                 return(1);
259         } else if (unit == 'm' || unit == 'M') {
260                 *capacity = strtol(string, NULL, 10);
261                 return(1);
262         } else if (unit == 'g' || unit == 'G') {
263                 *capacity = strtol(string, NULL, 10) * 1024;
264                 return(1);
265         } else {
266                 return(0);
267         }
268 }
269
270 /*
271  * Round a number up to the nearest power of two.
272  */
273 unsigned long
274 next_power_of_two(unsigned long n)
275 {
276         unsigned long p, op;
277
278         p = 1;
279         op = 0;
280         while (p < n && p > op) {
281                 op = p;
282                 p <<= 1;
283         }
284
285         return(p > op ? p : n);
286 }
287
288 /*
289  * Returns file name without extension.
290  * e.g.
291  *      ru.koi8-r.kbd -> ru.koi8-r
292  *      README -> README
293  *
294  * Caller is responsible for freeing the string returned.
295  */
296 char *
297 filename_noext(const char *filename)
298 {
299         int i;
300         char *buffer, *p;
301
302         buffer = aura_strdup(filename);
303
304         if (strlen(filename) == 0) {
305                 buffer[0] = '\0';
306                 return(buffer);
307         }
308
309         p = strrchr(filename, '.');
310
311         if (p != NULL) {
312                 i = strlen(filename) - strlen(p);
313                 buffer[i] = 0;
314         }
315
316         return(buffer);
317 }
318
319 /*
320  * Temp files
321  */
322
323 int
324 temp_file_add(struct i_fn_args *a, const char *filename)
325 {
326         aura_dict_store(a->temp_files, filename, strlen(filename) + 1, "", 1);
327         return(1);
328 }
329
330 int
331 temp_files_clean(struct i_fn_args *a)
332 {
333         void *rk;
334         size_t rk_len;
335         char *filename;
336
337         aura_dict_rewind(a->temp_files);
338         while (!aura_dict_eof(a->temp_files)) {
339                 aura_dict_get_current_key(a->temp_files, &rk, &rk_len);
340                 asprintf(&filename, "%s%s", a->tmp, (char *)rk);
341                 (void)unlink(filename); /* not much we can do if it fails */
342                 free(filename);
343                 aura_dict_next(a->temp_files);
344         }
345         return(1);
346 }
347
348 /*
349  * Command names
350  */
351 const char *
352 cmd_name(const struct i_fn_args *a, const char *cmd_key)
353 {
354         const char *name;
355
356         name = config_var_get(a->cmd_names, cmd_key);
357         if (strcmp(name, "") == 0)
358                 return("bin/echo");     /* XXX usr/local/sbin/error? */
359         else
360                 return(name);
361 }