Initial import from FreeBSD RELENG_4:
[dragonfly.git] / release / sysinstall / misc.c
1 /*
2  * Miscellaneous support routines..
3  *
4  * $FreeBSD: src/release/sysinstall/misc.c,v 1.40.2.1 2001/09/27 06:51:09 murray Exp $
5  *
6  * Copyright (c) 1995
7  *      Jordan Hubbard.  All rights reserved.
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  *    verbatim and that no modifications are made prior to this
15  *    point in the file.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33
34 #include "sysinstall.h"
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <sys/stat.h>
38 #include <sys/errno.h>
39 #include <sys/file.h>
40 #include <sys/types.h>
41 #include <dirent.h>
42 #include <sys/wait.h>
43 #include <sys/param.h>
44 #include <sys/mount.h>
45 #include <ufs/ufs/ufsmount.h>
46 #include <sys/reboot.h>
47 #include <sys/disklabel.h>
48
49 /* Quick check to see if a file is readable */
50 Boolean
51 file_readable(char *fname)
52 {
53     if (!access(fname, F_OK))
54         return TRUE;
55     return FALSE;
56 }
57
58 /* Quick check to see if a file is executable */
59 Boolean
60 file_executable(char *fname)
61 {
62     if (!access(fname, X_OK))
63         return TRUE;
64     return FALSE;
65 }
66
67 /* Concatenate two strings into static storage */
68 char *
69 string_concat(char *one, char *two)
70 {
71     static char tmp[FILENAME_MAX];
72
73     /* Yes, we're deliberately cavalier about not checking for overflow */
74     strcpy(tmp, one);
75     strcat(tmp, two);
76     return tmp;
77 }
78
79 /* sane strncpy() function */
80 char *
81 sstrncpy(char *dst, const char *src, int size)
82 {
83     dst[size] = '\0';
84     return strncpy(dst, src, size);
85 }
86
87 /* Concatenate three strings into static storage */
88 char *
89 string_concat3(char *one, char *two, char *three)
90 {
91     static char tmp[FILENAME_MAX];
92
93     /* Yes, we're deliberately cavalier about not checking for overflow */
94     strcpy(tmp, one);
95     strcat(tmp, two);
96     strcat(tmp, three);
97     return tmp;
98 }
99
100 /* Clip the whitespace off the end of a string */
101 char *
102 string_prune(char *str)
103 {
104     int len = str ? strlen(str) : 0;
105
106     while (len && isspace(str[len - 1]))
107         str[--len] = '\0';
108     return str;
109 }
110
111 /* run the whitespace off the front of a string */
112 char *
113 string_skipwhite(char *str)
114 {
115     while (*str && isspace(*str))
116         ++str;
117     return str;
118 }
119
120 /* copy optionally and allow second arg to be null */
121 char *
122 string_copy(char *s1, char *s2)
123 {
124     if (!s1)
125         return NULL;
126     if (!s2)
127         s1[0] = '\0';
128     else
129         strcpy(s1, s2);
130     return s1;
131 }
132
133 /* convert an integer to a string, using a static buffer */
134 char *
135 itoa(int value)
136 {
137     static char buf[13];
138
139     snprintf(buf, 12, "%d", value);
140     return buf;
141 }
142
143 Boolean
144 directory_exists(const char *dirname)
145 {
146     DIR *tptr;
147
148     if (!dirname)
149         return FALSE;
150     if (!strlen(dirname))
151         return FALSE;
152
153     tptr = opendir(dirname);
154     if (!tptr)
155         return (FALSE);
156
157     closedir(tptr);
158     return (TRUE);
159 }
160
161 char *
162 pathBaseName(const char *path)
163 {
164     char *pt;
165     char *ret = (char *)path;
166
167     pt = strrchr(path,(int)'/');
168
169     if (pt != 0)                        /* if there is a slash */
170     {
171         ret = ++pt;                     /* start the file after it */
172     }
173     
174     return(ret);
175 }
176
177 /* A free guaranteed to take NULL ptrs */
178 void
179 safe_free(void *ptr)
180 {
181     if (ptr)
182         free(ptr);
183 }
184
185 /* A malloc that checks errors */
186 void *
187 safe_malloc(size_t size)
188 {
189     void *ptr;
190
191     if (size <= 0)
192         msgFatal("Invalid malloc size of %ld!", (long)size);
193     ptr = malloc(size);
194     if (!ptr)
195         msgFatal("Out of memory!");
196     bzero(ptr, size);
197     return ptr;
198 }
199
200 /* A realloc that checks errors */
201 void *
202 safe_realloc(void *orig, size_t size)
203 {
204     void *ptr;
205
206     if (size <= 0)
207         msgFatal("Invalid realloc size of %ld!", (long)size);
208     ptr = realloc(orig, size);
209     if (!ptr)
210         msgFatal("Out of memory!");
211     return ptr;
212 }
213
214 /* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
215 char *
216 root_bias(char *path)
217 {
218     static char tmp[FILENAME_MAX];
219     char *cp = variable_get(VAR_INSTALL_ROOT);
220
221     if (!strcmp(cp, "/"))
222         return path;
223     strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
224     strcat(tmp, path);
225     return tmp;
226 }
227
228 /*
229  * These next routines are kind of specialized just for building item lists
230  * for dialog_menu().
231  */
232
233 /* Add an item to an item list */
234 dialogMenuItem *
235 item_add(dialogMenuItem *list, char *prompt, char *title,
236          int (*checked)(dialogMenuItem *self),
237          int (*fire)(dialogMenuItem *self),
238          void (*selected)(dialogMenuItem *self, int is_selected),
239          void *data, int aux, int *curr, int *max)
240 {
241     dialogMenuItem *d;
242
243     if (*curr == *max) {
244         *max += 20;
245         list = (dialogMenuItem *)realloc(list, sizeof(dialogMenuItem) * *max);
246     }
247     d = &list[(*curr)++];
248     bzero(d, sizeof(*d));
249     d->prompt = prompt ? strdup(prompt) : NULL;
250     d->title = title ? strdup(title) : NULL;
251     d->checked = checked;
252     d->fire = fire;
253     d->selected = selected;
254     d->data = data;
255     d->aux = aux;
256     return list;
257 }
258
259 /* Toss the items out */
260 void
261 items_free(dialogMenuItem *list, int *curr, int *max)
262 {
263     int i;
264
265     for (i = 0; list[i].prompt; i++) {
266         safe_free(list[i].prompt);
267         safe_free(list[i].title);
268     }
269     safe_free(list);
270     *curr = *max = 0;
271 }
272
273 int
274 Mkdir(char *ipath)
275 {
276     struct stat sb;
277     int final;
278     char *p, *path;
279
280     if (file_readable(ipath) || Fake)
281         return DITEM_SUCCESS;
282
283     path = strcpy(alloca(strlen(ipath) + 1), ipath);
284     if (isDebug())
285         msgDebug("mkdir(%s)\n", path);
286     p = path;
287     if (p[0] == '/')            /* Skip leading '/'. */
288         ++p;
289     for (final = FALSE; !final; ++p) {
290         if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
291             final = TRUE;
292         else if (p[0] != '/')
293             continue;
294         *p = '\0';
295         if (stat(path, &sb)) {
296             if (errno != ENOENT) {
297                 msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
298                 return DITEM_FAILURE;
299             }
300             if (isDebug())
301                 msgDebug("mkdir(%s..)\n", path);
302             if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
303                 msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
304                 return DITEM_FAILURE;
305             }
306         }
307         *p = '/';
308     }
309     return DITEM_SUCCESS;
310 }
311
312 int
313 Mount(char *mountp, void *dev)
314 {
315     struct ufs_args ufsargs;
316     char device[80];
317     char mountpoint[FILENAME_MAX];
318
319     if (Fake)
320         return DITEM_SUCCESS;
321
322     if (*((char *)dev) != '/') {
323         sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
324         sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
325     }
326     else {
327         strcpy(device, dev);
328         strcpy(mountpoint, mountp);
329     }
330     memset(&ufsargs,0,sizeof ufsargs);
331
332     if (Mkdir(mountpoint)) {
333         msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
334         return DITEM_FAILURE;
335     }
336     if (isDebug())
337         msgDebug("mount %s %s\n", device, mountpoint);
338
339     ufsargs.fspec = device;
340     if (mount("ufs", mountpoint, RunningAsInit ? MNT_ASYNC | MNT_NOATIME : 0,
341         (caddr_t)&ufsargs) == -1) {
342         msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
343         return DITEM_FAILURE;
344     }
345     return DITEM_SUCCESS;
346 }
347
348 WINDOW *
349 openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
350 {
351     WINDOW              *win;
352     static char         help[FILENAME_MAX];
353
354     /* We need a curses window */
355     win = newwin(LINES, COLS, 0, 0);
356     if (win) {
357         /* Say where our help comes from */
358         if (helpfile) {
359             use_helpline("Press F1 for more information on this screen.");
360             use_helpfile(systemHelpFile(helpfile, help));
361         }
362         /* Setup a nice screen for us to splat stuff onto */
363         draw_box(win, y, x, height, width, dialog_attr, border_attr);
364         wattrset(win, dialog_attr);
365         mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
366     }
367     return win;
368 }
369
370 ComposeObj *
371 initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
372 {
373     ComposeObj *obj = NULL, *first;
374     int n;
375
376     /* Loop over the layout list, create the objects, and add them
377        onto the chain of objects that dialog uses for traversal*/
378     
379     n = 0;
380     while (layout[n].help != NULL) {
381         int t = TYPE_OF_OBJ(layout[n].type);
382
383         switch (t) {
384         case STRINGOBJ:
385             layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
386                                          layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
387             ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
388             break;
389             
390         case BUTTONOBJ:
391             layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
392             break;
393             
394         default:
395             msgFatal("Don't support this object yet!");
396         }
397         AddObj(&obj, t, (void *) layout[n].obj);
398         n++;
399     }
400     *max = n - 1;
401     /* Find the first object in the list */
402     for (first = obj; first->prev; first = first->prev);
403     return first;
404 }
405
406 int
407 layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
408 {
409     char help_line[80];
410     int ret, i, len = strlen(layout[*n].help);
411
412     /* Display the help line at the bottom of the screen */
413     for (i = 0; i < 79; i++)
414         help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
415     help_line[i] = '\0';
416     use_helpline(help_line);
417     display_helpline(win, LINES - 1, COLS - 1);
418     wrefresh(win);
419             
420     /* Ask for libdialog to do its stuff */
421     ret = PollObj(obj);
422     /* Handle special case stuff that libdialog misses. Sigh */
423     switch (ret) {
424     case SEL_ESC:       /* Bail out */
425         *cancel = TRUE;
426         return FALSE;
427               
428         /* This doesn't work for list dialogs. Oh well. Perhaps
429            should special case the move from the OK button ``up''
430            to make it go to the interface list, but then it gets
431            awkward for the user to go back and correct screw up's
432            in the per-interface section */
433     case KEY_DOWN:
434     case SEL_CR:
435     case SEL_TAB:
436         if (*n < max)
437             ++*n;
438         else
439             *n = 0;
440         break;
441               
442         /* The user has pressed enter over a button object */
443     case SEL_BUTTON:
444         if (cbutton && *cbutton)
445             *cancel = TRUE;
446         else
447             *cancel = FALSE;
448         return FALSE;
449         
450     case KEY_UP:
451     case SEL_BACKTAB:
452         if (*n)
453             --*n;
454         else
455             *n = max;
456         break;
457         
458     case KEY_F(1):
459         display_helpfile();
460         
461         /* They tried some key combination we don't support - tootle them forcefully! */
462     default:
463         beep();
464     }
465     return TRUE;
466 }
467
468 WINDOW *
469 savescr(void)
470 {
471     WINDOW *w;
472
473     w = dupwin(newscr);
474     return w;
475 }
476
477 void
478 restorescr(WINDOW *w)
479 {
480     touchwin(w);
481     wrefresh(w);
482     delwin(w);
483 }
484