1 /*
2 * Miscellaneous support routines..
3 *
4 * $FreeBSD: stable/9/usr.sbin/sysinstall/misc.c 209359 2010-06-20 04:14:49Z randi $
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 */
52 Boolean
file_readable(char * fname)53 file_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 */
61 Boolean
file_executable(char * fname)62 file_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 */
70 char *
string_concat(char * one,char * two)71 string_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 */
82 char *
sstrncpy(char * dst,const char * src,int size)83 sstrncpy(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 */
90 char *
string_concat3(char * one,char * two,char * three)91 string_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 */
103 char *
string_prune(char * str)104 string_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 */
114 char *
string_skipwhite(char * str)115 string_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 */
123 char *
string_copy(char * s1,char * s2)124 string_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 */
136 char *
itoa(int value)137 itoa(int value)
138 {
139 static char buf[13];
140
141 snprintf(buf, 12, "%d", value);
142 return buf;
143 }
144
145 Boolean
directory_exists(const char * dirname)146 directory_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
163 char *
pathBaseName(const char * path)164 pathBaseName(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 */
180 void
safe_free(void * ptr)181 safe_free(void *ptr)
182 {
183 if (ptr)
184 free(ptr);
185 }
186
187 /* A malloc that checks errors */
188 void *
safe_malloc(size_t size)189 safe_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 */
203 void *
safe_realloc(void * orig,size_t size)204 safe_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 /) */
217 char *
root_bias(char * path)218 root_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 */
236 dialogMenuItem *
item_add(dialogMenuItem * list,char * prompt,char * title,int (* checked)(dialogMenuItem * self),int (* fire)(dialogMenuItem * self),void (* selected)(dialogMenuItem * self,int is_selected),void * data,void * aux,int * curr,int * max)237 item_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 */
262 void
items_free(dialogMenuItem * list,int * curr,int * max)263 items_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
275 int
Mkdir(char * ipath)276 Mkdir(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
314 int
Mkdir_command(char * key,void * dir)315 Mkdir_command(char *key, void *dir)
316 {
317 return (Mkdir((char*)dir));
318 }
319
320 int
Mount(char * mountp,void * dev)321 Mount(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
356 int
Mount_msdosfs(char * mountp,void * dev)357 Mount_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
394 WINDOW *
openLayoutDialog(char * helpfile,char * title,int x,int y,int width,int height)395 openLayoutDialog(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
416 ComposeObj *
initLayoutDialog(WINDOW * win,Layout * layout,int x,int y,int * max)417 initLayoutDialog(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
452 int
layoutDialogLoop(WINDOW * win,Layout * layout,ComposeObj ** obj,int * n,int max,int * cbutton,int * cancel)453 layoutDialogLoop(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
514 WINDOW *
savescr(void)515 savescr(void)
516 {
517 WINDOW *w;
518
519 w = dupwin(newscr);
520 return w;
521 }
522
523 void
restorescr(WINDOW * w)524 restorescr(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 */
535 char *
getsysctlbyname(const char * sysctlname)536 getsysctlbyname(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