1/*
2 * Miscellaneous support routines..
3 *
4 * $FreeBSD$
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#include <fs/msdosfs/msdosfsmount.h>
49#include <sys/sysctl.h>
50
51/* Quick check to see if a file is readable */
52Boolean
53file_readable(char *fname)
54{
55    if (!access(fname, F_OK))
56	return TRUE;
57    return FALSE;
58}
59
60/* Quick check to see if a file is executable */
61Boolean
62file_executable(char *fname)
63{
64    if (!access(fname, X_OK))
65	return TRUE;
66    return FALSE;
67}
68
69/* Concatenate two strings into static storage */
70char *
71string_concat(char *one, char *two)
72{
73    static char tmp[FILENAME_MAX];
74
75    /* Yes, we're deliberately cavalier about not checking for overflow */
76    strcpy(tmp, one);
77    strcat(tmp, two);
78    return tmp;
79}
80
81/* sane strncpy() function */
82char *
83sstrncpy(char *dst, const char *src, int size)
84{
85    dst[size] = '\0';
86    return strncpy(dst, src, size);
87}
88
89/* Concatenate three strings into static storage */
90char *
91string_concat3(char *one, char *two, char *three)
92{
93    static char tmp[FILENAME_MAX];
94
95    /* Yes, we're deliberately cavalier about not checking for overflow */
96    strcpy(tmp, one);
97    strcat(tmp, two);
98    strcat(tmp, three);
99    return tmp;
100}
101
102/* Clip the whitespace off the end of a string */
103char *
104string_prune(char *str)
105{
106    int len = str ? strlen(str) : 0;
107
108    while (len && isspace(str[len - 1]))
109	str[--len] = '\0';
110    return str;
111}
112
113/* run the whitespace off the front of a string */
114char *
115string_skipwhite(char *str)
116{
117    while (*str && isspace(*str))
118	++str;
119    return str;
120}
121
122/* copy optionally and allow second arg to be null */
123char *
124string_copy(char *s1, char *s2)
125{
126    if (!s1)
127	return NULL;
128    if (!s2)
129	s1[0] = '\0';
130    else
131	strcpy(s1, s2);
132    return s1;
133}
134
135/* convert an integer to a string, using a static buffer */
136char *
137itoa(int value)
138{
139    static char buf[13];
140
141    snprintf(buf, 12, "%d", value);
142    return buf;
143}
144
145Boolean
146directory_exists(const char *dirname)
147{
148    DIR *tptr;
149
150    if (!dirname)
151	return FALSE;
152    if (!strlen(dirname))
153	return FALSE;
154
155    tptr = opendir(dirname);
156    if (!tptr)
157	return (FALSE);
158
159    closedir(tptr);
160    return (TRUE);
161}
162
163char *
164pathBaseName(const char *path)
165{
166    char *pt;
167    char *ret = (char *)path;
168
169    pt = strrchr(path,(int)'/');
170
171    if (pt != 0)			/* if there is a slash */
172    {
173	ret = ++pt;			/* start the file after it */
174    }
175
176    return(ret);
177}
178
179/* A free guaranteed to take NULL ptrs */
180void
181safe_free(void *ptr)
182{
183    if (ptr)
184	free(ptr);
185}
186
187/* A malloc that checks errors */
188void *
189safe_malloc(size_t size)
190{
191    void *ptr;
192
193    if (size <= 0)
194	msgFatal("Invalid malloc size of %ld!", (long)size);
195    ptr = malloc(size);
196    if (!ptr)
197	msgFatal("Out of memory!");
198    bzero(ptr, size);
199    return ptr;
200}
201
202/* A realloc that checks errors */
203void *
204safe_realloc(void *orig, size_t size)
205{
206    void *ptr;
207
208    if (size <= 0)
209	msgFatal("Invalid realloc size of %ld!", (long)size);
210    ptr = reallocf(orig, size);
211    if (!ptr)
212	msgFatal("Out of memory!");
213    return ptr;
214}
215
216/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
217char *
218root_bias(char *path)
219{
220    static char tmp[FILENAME_MAX];
221    char *cp = variable_get(VAR_INSTALL_ROOT);
222
223    if (!strcmp(cp, "/"))
224	return path;
225    strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
226    strcat(tmp, path);
227    return tmp;
228}
229
230/*
231 * These next routines are kind of specialized just for building item lists
232 * for dialog_menu().
233 */
234
235/* Add an item to an item list */
236dialogMenuItem *
237item_add(dialogMenuItem *list, char *prompt, char *title,
238	 int (*checked)(dialogMenuItem *self),
239	 int (*fire)(dialogMenuItem *self),
240	 void (*selected)(dialogMenuItem *self, int is_selected),
241	 void *data, void *aux, int *curr, int *max)
242{
243    dialogMenuItem *d;
244
245    if (*curr == *max) {
246	*max += 20;
247	list = (dialogMenuItem *)safe_realloc(list, sizeof(dialogMenuItem) * *max);
248    }
249    d = &list[(*curr)++];
250    bzero(d, sizeof(*d));
251    d->prompt = prompt ? strdup(prompt) : NULL;
252    d->title = title ? strdup(title) : NULL;
253    d->checked = checked;
254    d->fire = fire;
255    d->selected = selected;
256    d->data = data;
257    d->aux = (long)aux;
258    return list;
259}
260
261/* Toss the items out */
262void
263items_free(dialogMenuItem *list, int *curr, int *max)
264{
265    int i;
266
267    for (i = 0; list[i].prompt; i++) {
268	safe_free(list[i].prompt);
269	safe_free(list[i].title);
270    }
271    safe_free(list);
272    *curr = *max = 0;
273}
274
275int
276Mkdir(char *ipath)
277{
278    struct stat sb;
279    int final;
280    char *p, *path;
281
282    if (file_readable(ipath) || Fake)
283	return DITEM_SUCCESS;
284
285    path = strcpy(alloca(strlen(ipath) + 1), ipath);
286    if (isDebug())
287	msgDebug("mkdir(%s)\n", path);
288    p = path;
289    if (p[0] == '/')		/* Skip leading '/'. */
290	++p;
291    for (final = FALSE; !final; ++p) {
292	if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
293	    final = TRUE;
294	else if (p[0] != '/')
295	    continue;
296	*p = '\0';
297	if (stat(path, &sb)) {
298	    if (errno != ENOENT) {
299		msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
300		return DITEM_FAILURE;
301	    }
302	    if (isDebug())
303		msgDebug("mkdir(%s..)\n", path);
304	    if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
305		msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
306		return DITEM_FAILURE;
307	    }
308	}
309	*p = '/';
310    }
311    return DITEM_SUCCESS;
312}
313
314int
315Mkdir_command(char *key, void *dir)
316{
317    return (Mkdir((char*)dir));
318}
319
320int
321Mount(char *mountp, void *dev)
322{
323    struct ufs_args ufsargs;
324    char device[80];
325    char mountpoint[FILENAME_MAX];
326
327    if (Fake)
328	return DITEM_SUCCESS;
329
330    if (*((char *)dev) != '/') {
331    	sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
332	sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
333    }
334    else {
335	strcpy(device, dev);
336	strcpy(mountpoint, mountp);
337    }
338    memset(&ufsargs,0,sizeof ufsargs);
339
340    if (Mkdir(mountpoint)) {
341	msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
342	return DITEM_FAILURE;
343    }
344    if (isDebug())
345	msgDebug("mount %s %s\n", device, mountpoint);
346
347    ufsargs.fspec = device;
348    if (mount("ufs", mountpoint, RunningAsInit ? MNT_ASYNC | MNT_NOATIME : 0,
349	(caddr_t)&ufsargs) == -1) {
350	msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
351	return DITEM_FAILURE;
352    }
353    return DITEM_SUCCESS;
354}
355
356int
357Mount_msdosfs(char *mountp, void *dev)
358{
359    struct msdosfs_args mount_args;
360    char device[80];
361    char mountpoint[FILENAME_MAX];
362
363    if (Fake)
364	return DITEM_SUCCESS;
365
366    if (*((char *)dev) != '/') {
367    	sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
368	sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
369    }
370    else {
371	strcpy(device, dev);
372	strcpy(mountpoint, mountp);
373    }
374
375    if (Mkdir(mountpoint)) {
376	msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
377	return DITEM_FAILURE;
378    }
379    if (isDebug())
380	msgDebug("mount %s %s\n", device, mountpoint);
381
382    memset(&mount_args, 0, sizeof(mount_args));
383    mount_args.fspec = device;
384    mount_args.magic = MSDOSFS_ARGSMAGIC;
385    mount_args.mask = S_IRWXU | S_IRWXG | S_IRWXO;
386    if (mount("msdosfs", mountpoint, RunningAsInit ? MNT_ASYNC|MNT_NOATIME : 0,
387	    (caddr_t)&mount_args) == -1) {
388	msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
389	return DITEM_FAILURE;
390    }
391    return DITEM_SUCCESS;
392}
393
394WINDOW *
395openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
396{
397    WINDOW		*win;
398    static char		help[FILENAME_MAX];
399
400    /* We need a curses window */
401    win = newwin(LINES, COLS, 0, 0);
402    if (win) {
403	/* Say where our help comes from */
404	if (helpfile) {
405	    use_helpline("Press F1 for more information on this screen.");
406	    use_helpfile(systemHelpFile(helpfile, help));
407	}
408	/* Setup a nice screen for us to splat stuff onto */
409	draw_box(win, y, x, height, width, dialog_attr, border_attr);
410	wattrset(win, dialog_attr);
411	mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
412    }
413    return win;
414}
415
416ComposeObj *
417initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
418{
419    ComposeObj *obj = NULL, *first;
420    int n;
421
422    /* Loop over the layout list, create the objects, and add them
423       onto the chain of objects that dialog uses for traversal*/
424
425    n = 0;
426    while (layout[n].help != NULL) {
427	int t = TYPE_OF_OBJ(layout[n].type);
428
429	switch (t) {
430	case STRINGOBJ:
431	    layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
432					 layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
433	    ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
434	    break;
435
436	case BUTTONOBJ:
437	    layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
438	    break;
439
440	default:
441	    msgFatal("Don't support this object yet!");
442	}
443	AddObj(&obj, t, (void *) layout[n].obj);
444	n++;
445    }
446    *max = n - 1;
447    /* Find the first object in the list */
448    for (first = obj; first->prev; first = first->prev);
449    return first;
450}
451
452int
453layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
454{
455    char help_line[80];
456    int ret, i, len = strlen(layout[*n].help);
457
458    /* Display the help line at the bottom of the screen */
459    for (i = 0; i < 79; i++)
460	help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
461    help_line[i] = '\0';
462    use_helpline(help_line);
463    display_helpline(win, LINES - 1, COLS - 1);
464    wrefresh(win);
465
466    /* Ask for libdialog to do its stuff */
467    ret = PollObj(obj);
468    /* Handle special case stuff that libdialog misses. Sigh */
469    switch (ret) {
470    case SEL_ESC:	/* Bail out */
471	*cancel = TRUE;
472	return FALSE;
473
474	/* This doesn't work for list dialogs. Oh well. Perhaps
475	   should special case the move from the OK button ``up''
476	   to make it go to the interface list, but then it gets
477	   awkward for the user to go back and correct screw up's
478	   in the per-interface section */
479    case KEY_DOWN:
480    case SEL_CR:
481    case SEL_TAB:
482	if (*n < max)
483	    ++*n;
484	else
485	    *n = 0;
486	break;
487
488	/* The user has pressed enter over a button object */
489    case SEL_BUTTON:
490	if (cbutton && *cbutton)
491	    *cancel = TRUE;
492	else
493	    *cancel = FALSE;
494	return FALSE;
495
496    case KEY_UP:
497    case SEL_BACKTAB:
498	if (*n)
499	    --*n;
500	else
501	    *n = max;
502	break;
503
504    case KEY_F(1):
505	display_helpfile();
506
507	/* They tried some key combination we don't support - tootle them forcefully! */
508    default:
509	beep();
510    }
511    return TRUE;
512}
513
514WINDOW *
515savescr(void)
516{
517    WINDOW *w;
518
519    w = dupwin(newscr);
520    return w;
521}
522
523void
524restorescr(WINDOW *w)
525{
526    touchwin(w);
527    wrefresh(w);
528    delwin(w);
529}
530
531/*
532 * Get a sysctl variable as a string or "<unknown>" if sysctl fails.
533 * Caller must free returned string.
534 */
535char *
536getsysctlbyname(const char *sysctlname)
537{
538    char *buf;
539    size_t sz, buf_sz = 0;
540    const char unk_str[] = "<unknown>";
541
542    sysctlbyname(sysctlname, NULL, &buf_sz, NULL, 0);
543    buf_sz = MAX(sizeof(unk_str), buf_sz) + 1;
544    sz = buf_sz - 1;
545    buf = (char *)safe_malloc(buf_sz);
546
547    if (sysctlbyname(sysctlname, buf, &sz, NULL, 0) != -1)
548	buf[sz] = '\0';
549    else
550	strlcpy(buf, unk_str, buf_sz);
551
552    return buf;
553}
554