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