1*57e232a5Smiod /* $OpenBSD: cmd.c,v 1.5 2024/08/08 13:59:11 miod Exp $ */ 23a62b615Svisa 33a62b615Svisa /* 43a62b615Svisa * Copyright (c) 1997-1999 Michael Shalayeff 53a62b615Svisa * All rights reserved. 63a62b615Svisa * 73a62b615Svisa * Redistribution and use in source and binary forms, with or without 83a62b615Svisa * modification, are permitted provided that the following conditions 93a62b615Svisa * are met: 103a62b615Svisa * 1. Redistributions of source code must retain the above copyright 113a62b615Svisa * notice, this list of conditions and the following disclaimer. 123a62b615Svisa * 2. Redistributions in binary form must reproduce the above copyright 133a62b615Svisa * notice, this list of conditions and the following disclaimer in the 143a62b615Svisa * documentation and/or other materials provided with the distribution. 153a62b615Svisa * 163a62b615Svisa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 173a62b615Svisa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 183a62b615Svisa * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 193a62b615Svisa * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 203a62b615Svisa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 213a62b615Svisa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 223a62b615Svisa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 233a62b615Svisa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 243a62b615Svisa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 253a62b615Svisa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 263a62b615Svisa * SUCH DAMAGE. 273a62b615Svisa */ 283a62b615Svisa 293a62b615Svisa #include <sys/param.h> 303a62b615Svisa #include <sys/reboot.h> 313a62b615Svisa #include <sys/select.h> 323a62b615Svisa #include <sys/stat.h> 333a62b615Svisa 343a62b615Svisa #include <dirent.h> 353a62b615Svisa #include <errno.h> 363a62b615Svisa #include <fcntl.h> 373a62b615Svisa #include <stdio.h> 383a62b615Svisa #include <string.h> 393a62b615Svisa #include <termios.h> 403a62b615Svisa #include <unistd.h> 413a62b615Svisa 423a62b615Svisa #include "cmd.h" 433a62b615Svisa #include "disk.h" 443a62b615Svisa 453a62b615Svisa static int Xboot(void); 463a62b615Svisa static int Xecho(void); 473a62b615Svisa static int Xhelp(void); 483a62b615Svisa static int Xls(void); 493a62b615Svisa static int Xnop(void); 503a62b615Svisa static int Xreboot(void); 513a62b615Svisa #ifdef MACHINE_CMD 523a62b615Svisa static int Xmachine(void); 533a62b615Svisa extern const struct cmd_table MACHINE_CMD[]; 543a62b615Svisa #endif 553a62b615Svisa extern int Xset(void); 563a62b615Svisa 573a62b615Svisa #ifdef CHECK_SKIP_CONF 583a62b615Svisa extern int CHECK_SKIP_CONF(void); 593a62b615Svisa #endif 603a62b615Svisa 613a62b615Svisa extern const struct cmd_table cmd_set[]; 623a62b615Svisa const struct cmd_table cmd_table[] = { 633a62b615Svisa {"#", CMDT_CMD, Xnop}, /* XXX must be first */ 643a62b615Svisa {"boot", CMDT_CMD, Xboot}, 653a62b615Svisa {"echo", CMDT_CMD, Xecho}, 663a62b615Svisa {"help", CMDT_CMD, Xhelp}, 673a62b615Svisa {"ls", CMDT_CMD, Xls}, 683a62b615Svisa #ifdef MACHINE_CMD 693a62b615Svisa {"machine",CMDT_MDC, Xmachine}, 703a62b615Svisa #endif 713a62b615Svisa {"reboot", CMDT_CMD, Xreboot}, 723a62b615Svisa {"set", CMDT_SET, Xset}, 733a62b615Svisa {NULL, 0}, 743a62b615Svisa }; 753a62b615Svisa 763a62b615Svisa static void ls(const char *, struct stat *); 773a62b615Svisa static int readline(char *, size_t, int); 783a62b615Svisa char *nextword(char *); 793a62b615Svisa static char *whatcmd(const struct cmd_table **ct, char *); 803a62b615Svisa static char *qualify(char *); 813a62b615Svisa 823a62b615Svisa char cmd_buf[CMD_BUFF_SIZE]; 833a62b615Svisa 843a62b615Svisa int 853a62b615Svisa getcmd(void) 863a62b615Svisa { 873a62b615Svisa cmd.cmd = NULL; 883a62b615Svisa 893a62b615Svisa if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout)) 903a62b615Svisa cmd.cmd = cmd_table; 913a62b615Svisa 923a62b615Svisa return docmd(); 933a62b615Svisa } 943a62b615Svisa 953a62b615Svisa int 963a62b615Svisa read_conf(void) 973a62b615Svisa { 983a62b615Svisa struct stat sb; 993a62b615Svisa const char *path; 1003a62b615Svisa int fd, rc = 0; 1013a62b615Svisa 1023a62b615Svisa #ifdef CHECK_SKIP_CONF 1033a62b615Svisa if (CHECK_SKIP_CONF()) { 1043a62b615Svisa printf("boot.conf processing skipped at operator request\n"); 1053a62b615Svisa cmd.timeout = 0; 1063a62b615Svisa return -1; /* Pretend file wasn't found */ 1073a62b615Svisa } 1083a62b615Svisa #endif 1093a62b615Svisa 1103a62b615Svisa path = disk_open(qualify(cmd.conf)); 1113a62b615Svisa if (path == NULL) { 1123a62b615Svisa fprintf(stderr, "cannot open device for reading %s: %s\n", 1133a62b615Svisa cmd.conf, strerror(errno)); 1143a62b615Svisa return -1; 1153a62b615Svisa } 1163a62b615Svisa if ((fd = open(path, O_RDONLY)) == -1) { 1173a62b615Svisa if (errno != ENOENT && errno != ENXIO) { 1183a62b615Svisa fprintf(stderr, "%s: open(%s): %s\n", __func__, 1193a62b615Svisa cmd.path, strerror(errno)); 1203a62b615Svisa rc = 0; 1213a62b615Svisa } else 1223a62b615Svisa rc = -1; 1233a62b615Svisa goto out; 1243a62b615Svisa } 1253a62b615Svisa 1263a62b615Svisa (void) fstat(fd, &sb); 1273a62b615Svisa if (sb.st_uid || (sb.st_mode & 2)) { 1283a62b615Svisa fprintf(stderr, "non-secure %s, will not proceed\n", cmd.path); 1293a62b615Svisa rc = -1; 1303a62b615Svisa goto out; 1313a62b615Svisa } 1323a62b615Svisa 1333a62b615Svisa do { 1343a62b615Svisa char *p = cmd_buf; 1353a62b615Svisa 1363a62b615Svisa cmd.cmd = NULL; 1373a62b615Svisa do { 1383a62b615Svisa rc = read(fd, p, 1); 1393a62b615Svisa } while (rc > 0 && *p++ != '\n' && 1403a62b615Svisa (p-cmd_buf) < sizeof(cmd_buf)); 1413a62b615Svisa 1423a62b615Svisa if (rc < 0) { /* Error from read() */ 1433a62b615Svisa fprintf(stderr, "%s: %s\n", cmd.path, strerror(errno)); 1443a62b615Svisa break; 1453a62b615Svisa } 1463a62b615Svisa 1473a62b615Svisa if (rc == 0) { /* eof from read() */ 1483a62b615Svisa if (p != cmd_buf) { /* Line w/o trailing \n */ 1493a62b615Svisa *p = '\0'; 1503a62b615Svisa rc = docmd(); 1513a62b615Svisa break; 1523a62b615Svisa } 1533a62b615Svisa } else { /* rc > 0, read a char */ 1543a62b615Svisa p--; /* Get back to last character */ 1553a62b615Svisa 1563a62b615Svisa if (*p != '\n') { /* Line was too long */ 1573a62b615Svisa fprintf(stderr, "%s: line too long\n", 1583a62b615Svisa cmd.path); 1593a62b615Svisa 1603a62b615Svisa /* Don't want to run the truncated command */ 1613a62b615Svisa rc = -1; 1623a62b615Svisa } 1633a62b615Svisa *p = '\0'; 1643a62b615Svisa } 1653a62b615Svisa } while (rc > 0 && !(rc = docmd())); 1663a62b615Svisa 1673a62b615Svisa out: 1683a62b615Svisa if (fd != -1) 1693a62b615Svisa close(fd); 1703a62b615Svisa disk_close(); 1713a62b615Svisa return rc; 1723a62b615Svisa } 1733a62b615Svisa 1743a62b615Svisa int 1753a62b615Svisa docmd(void) 1763a62b615Svisa { 1773a62b615Svisa char *p = NULL; 1783a62b615Svisa const struct cmd_table *ct = cmd_table, *cs; 1793a62b615Svisa 1803a62b615Svisa cmd.argc = 1; 1813a62b615Svisa if (cmd.cmd == NULL) { 1823a62b615Svisa 1833a62b615Svisa /* command */ 1843a62b615Svisa for (p = cmd_buf; *p == ' ' || *p == '\t'; p++) 1853a62b615Svisa ; 1863a62b615Svisa if (*p == '#' || *p == '\0') { /* comment or empty string */ 1873a62b615Svisa #ifdef DEBUG 1883a62b615Svisa printf("rem\n"); 1893a62b615Svisa #endif 1903a62b615Svisa return 0; 1913a62b615Svisa } 1923a62b615Svisa ct = cmd_table; 1933a62b615Svisa cs = NULL; 1943a62b615Svisa cmd.argv[cmd.argc] = p; /* in case it's shortcut boot */ 1953a62b615Svisa p = whatcmd(&ct, p); 1963a62b615Svisa if (ct == NULL) { 1973a62b615Svisa cmd.argc++; 1983a62b615Svisa ct = cmd_table; 1993a62b615Svisa } else if (ct->cmd_type == CMDT_SET && p != NULL) { 2003a62b615Svisa cs = cmd_set; 2013a62b615Svisa #ifdef MACHINE_CMD 2023a62b615Svisa } else if (ct->cmd_type == CMDT_MDC && p != NULL) { 2033a62b615Svisa cs = MACHINE_CMD; 2043a62b615Svisa #endif 2053a62b615Svisa } 2063a62b615Svisa 2073a62b615Svisa if (cs != NULL) { 2083a62b615Svisa p = whatcmd(&cs, p); 2093a62b615Svisa if (cs == NULL) { 2103a62b615Svisa printf("%s: syntax error\n", ct->cmd_name); 2113a62b615Svisa return 0; 2123a62b615Svisa } 2133a62b615Svisa ct = cs; 2143a62b615Svisa } 2153a62b615Svisa cmd.cmd = ct; 2163a62b615Svisa } 2173a62b615Svisa 2183a62b615Svisa cmd.argv[0] = ct->cmd_name; 2193a62b615Svisa while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) { 2203a62b615Svisa cmd.argv[cmd.argc++] = p; 2213a62b615Svisa p = nextword(p); 2223a62b615Svisa } 2233a62b615Svisa cmd.argv[cmd.argc] = NULL; 2243a62b615Svisa 2253a62b615Svisa return (*cmd.cmd->cmd_exec)(); 2263a62b615Svisa } 2273a62b615Svisa 2283a62b615Svisa static char * 2293a62b615Svisa whatcmd(const struct cmd_table **ct, char *p) 2303a62b615Svisa { 2313a62b615Svisa char *q; 2323a62b615Svisa int l; 2333a62b615Svisa 2343a62b615Svisa q = nextword(p); 2353a62b615Svisa 2363a62b615Svisa for (l = 0; p[l]; l++) 2373a62b615Svisa ; 2383a62b615Svisa 2393a62b615Svisa while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l)) 2403a62b615Svisa (*ct)++; 2413a62b615Svisa 2423a62b615Svisa if ((*ct)->cmd_name == NULL) 2433a62b615Svisa *ct = NULL; 2443a62b615Svisa 2453a62b615Svisa return q; 2463a62b615Svisa } 2473a62b615Svisa 2483a62b615Svisa static int 2493a62b615Svisa readline(char *buf, size_t n, int to) 2503a62b615Svisa { 2513a62b615Svisa struct termios saved_tio, tio; 2523a62b615Svisa struct timeval tv; 2533a62b615Svisa fd_set fdset; 254dd20b077Svisa char *p; 2553a62b615Svisa int timed_out = 0; 2563a62b615Svisa #ifdef DEBUG 2573a62b615Svisa extern int debug; 2583a62b615Svisa #endif 2593a62b615Svisa 2603a62b615Svisa /* Only do timeout if greater than 0 */ 2613a62b615Svisa if (to > 0) { 2623a62b615Svisa /* Switch to non-canonical mode for timeout detection. */ 2633a62b615Svisa tcgetattr(STDIN_FILENO, &saved_tio); 2643a62b615Svisa tio = saved_tio; 2653a62b615Svisa tio.c_lflag &= ~(ECHO | ICANON); 2663a62b615Svisa tcsetattr(STDIN_FILENO, TCSANOW, &tio); 2673a62b615Svisa 2683a62b615Svisa FD_ZERO(&fdset); 2693a62b615Svisa FD_SET(STDIN_FILENO, &fdset); 2703a62b615Svisa tv.tv_sec = to; 2713a62b615Svisa tv.tv_usec = 0; 2723a62b615Svisa if (select(STDIN_FILENO + 1, &fdset, NULL, NULL, &tv) == 0) 2733a62b615Svisa timed_out = 1; 2743a62b615Svisa 2753a62b615Svisa /* Restore canonical mode. */ 2763a62b615Svisa tcsetattr(STDIN_FILENO, TCSANOW, &saved_tio); 2773a62b615Svisa 2783a62b615Svisa if (timed_out) { 2793a62b615Svisa strlcpy(buf, "boot", 5); 2803a62b615Svisa putchar('\n'); 2813a62b615Svisa return strlen(buf); 2823a62b615Svisa } 2833a62b615Svisa } 2843a62b615Svisa 2853a62b615Svisa /* User has typed something. Turn off timeouts. */ 2863a62b615Svisa cmd.timeout = 0; 2873a62b615Svisa 2883a62b615Svisa if (fgets(buf, n, stdin) == NULL) 2893a62b615Svisa return 0; 2903a62b615Svisa 2913a62b615Svisa /* Strip trailing newline. */ 292dd20b077Svisa p = strchr(buf, '\n'); 293dd20b077Svisa if (p != NULL) 294dd20b077Svisa *p = '\0'; 2953a62b615Svisa 2963a62b615Svisa return strlen(buf); 2973a62b615Svisa } 2983a62b615Svisa 2993a62b615Svisa /* 3003a62b615Svisa * Search for spaces/tabs after the current word. If found, \0 the 3013a62b615Svisa * first one. Then pass a pointer to the first character of the 3023a62b615Svisa * next word, or NULL if there is no next word. 3033a62b615Svisa */ 3043a62b615Svisa char * 3053a62b615Svisa nextword(char *p) 3063a62b615Svisa { 3073a62b615Svisa /* skip blanks */ 3083a62b615Svisa while (*p && *p != '\t' && *p != ' ') 3093a62b615Svisa p++; 3103a62b615Svisa if (*p) { 3113a62b615Svisa *p++ = '\0'; 3123a62b615Svisa while (*p == '\t' || *p == ' ') 3133a62b615Svisa p++; 3143a62b615Svisa } 3153a62b615Svisa if (*p == '\0') 3163a62b615Svisa p = NULL; 3173a62b615Svisa return p; 3183a62b615Svisa } 3193a62b615Svisa 3203a62b615Svisa static void 3213a62b615Svisa print_help(const struct cmd_table *ct) 3223a62b615Svisa { 3233a62b615Svisa for (; ct->cmd_name != NULL; ct++) 3243a62b615Svisa printf(" %s", ct->cmd_name); 3253a62b615Svisa putchar('\n'); 3263a62b615Svisa } 3273a62b615Svisa 3283a62b615Svisa static int 3293a62b615Svisa Xhelp(void) 3303a62b615Svisa { 3313a62b615Svisa printf("commands:"); 3323a62b615Svisa print_help(cmd_table); 3333a62b615Svisa #ifdef MACHINE_CMD 3343a62b615Svisa return Xmachine(); 3353a62b615Svisa #else 3363a62b615Svisa return 0; 3373a62b615Svisa #endif 3383a62b615Svisa } 3393a62b615Svisa 3403a62b615Svisa #ifdef MACHINE_CMD 3413a62b615Svisa static int 3423a62b615Svisa Xmachine(void) 3433a62b615Svisa { 3443a62b615Svisa printf("machine:"); 3453a62b615Svisa print_help(MACHINE_CMD); 3463a62b615Svisa return 0; 3473a62b615Svisa } 3483a62b615Svisa #endif 3493a62b615Svisa 3503a62b615Svisa static int 3513a62b615Svisa Xecho(void) 3523a62b615Svisa { 3533a62b615Svisa int i; 3543a62b615Svisa 3553a62b615Svisa for (i = 1; i < cmd.argc; i++) 3563a62b615Svisa printf("%s ", cmd.argv[i]); 3573a62b615Svisa putchar('\n'); 3583a62b615Svisa return 0; 3593a62b615Svisa } 3603a62b615Svisa 3613a62b615Svisa static int 3623a62b615Svisa Xls(void) 3633a62b615Svisa { 3643a62b615Svisa struct stat sb; 3653a62b615Svisa const char *path; 3663a62b615Svisa DIR *dir; 3673a62b615Svisa struct dirent *dent; 3683a62b615Svisa int dirfd, oldcwd; 3693a62b615Svisa 3703a62b615Svisa path = disk_open(qualify(cmd.argv[1] ? cmd.argv[1] : "/.")); 3713a62b615Svisa if (path == NULL) 3723a62b615Svisa return 0; 3733a62b615Svisa 3743a62b615Svisa if (stat(path, &sb) < 0) { 3753a62b615Svisa printf("stat(%s): %s\n", cmd.path, strerror(errno)); 3763a62b615Svisa goto out; 3773a62b615Svisa } 3783a62b615Svisa 3793a62b615Svisa if ((sb.st_mode & S_IFMT) != S_IFDIR) 3803a62b615Svisa ls(path, &sb); 3813a62b615Svisa else { 3823a62b615Svisa oldcwd = open(".", O_RDONLY); 3833a62b615Svisa 3843a62b615Svisa dirfd = open(path, O_RDONLY); 3853a62b615Svisa if (dirfd < 0) { 3863a62b615Svisa printf("opendir(%s): %s\n", cmd.path, strerror(errno)); 3873a62b615Svisa close(oldcwd); 3883a62b615Svisa goto out; 3893a62b615Svisa } 3903a62b615Svisa if ((dir = fdopendir(dirfd)) < 0) { 3913a62b615Svisa printf("opendir(%s): %s\n", cmd.path, strerror(errno)); 3923a62b615Svisa close(dirfd); 3933a62b615Svisa close(oldcwd); 3943a62b615Svisa goto out; 3953a62b615Svisa } 3963a62b615Svisa fchdir(dirfd); 3973a62b615Svisa while ((dent = readdir(dir)) != NULL) { 3983a62b615Svisa if (fstatat(dirfd, dent->d_name, &sb, 3993a62b615Svisa AT_SYMLINK_NOFOLLOW) < 0) 4003a62b615Svisa printf("stat(%s): %s\n", dent->d_name, 4013a62b615Svisa strerror(errno)); 4023a62b615Svisa else 4033a62b615Svisa ls(dent->d_name, &sb); 4043a62b615Svisa } 4053a62b615Svisa closedir(dir); 4063a62b615Svisa 4073a62b615Svisa fchdir(oldcwd); 40800803372Svisa close(oldcwd); 4093a62b615Svisa } 4103a62b615Svisa 4113a62b615Svisa out: 4123a62b615Svisa disk_close(); 4133a62b615Svisa return 0; 4143a62b615Svisa } 4153a62b615Svisa 4163a62b615Svisa #define lsrwx(mode,s) \ 4173a62b615Svisa putchar ((mode) & S_IROTH? 'r' : '-'); \ 4183a62b615Svisa putchar ((mode) & S_IWOTH? 'w' : '-'); \ 4193a62b615Svisa putchar ((mode) & S_IXOTH? *(s): (s)[1]); 4203a62b615Svisa 4213a62b615Svisa static void 4223a62b615Svisa ls(const char *name, struct stat *sb) 4233a62b615Svisa { 4243a62b615Svisa putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]); 4253a62b615Svisa lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-")); 4263a62b615Svisa lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-")); 4273a62b615Svisa lsrwx(sb->st_mode , (sb->st_mode & S_ISTXT? "tT" : "x-")); 4283a62b615Svisa 4293a62b615Svisa printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid, 4303a62b615Svisa (u_long)sb->st_size, name); 4313a62b615Svisa } 4323a62b615Svisa #undef lsrwx 4333a62b615Svisa 4343a62b615Svisa int doboot = 1; 4353a62b615Svisa 4363a62b615Svisa static int 4373a62b615Svisa Xnop(void) 4383a62b615Svisa { 4393a62b615Svisa if (doboot) { 4403a62b615Svisa doboot = 0; 4413a62b615Svisa return (Xboot()); 4423a62b615Svisa } 4433a62b615Svisa 4443a62b615Svisa return 0; 4453a62b615Svisa } 4463a62b615Svisa 4473a62b615Svisa static int 4483a62b615Svisa Xboot(void) 4493a62b615Svisa { 4503a62b615Svisa if (cmd.argc > 1 && cmd.argv[1][0] != '-') { 4513a62b615Svisa qualify((cmd.argv[1]? cmd.argv[1]: cmd.image)); 4523a62b615Svisa if (bootparse(2)) 4533a62b615Svisa return 0; 4543a62b615Svisa } else { 4553a62b615Svisa if (bootparse(1)) 4563a62b615Svisa return 0; 4573a62b615Svisa snprintf(cmd.path, sizeof cmd.path, "%s:%s", 4583a62b615Svisa cmd.bootdev, cmd.image); 4593a62b615Svisa } 4603a62b615Svisa 4613a62b615Svisa return 1; 4623a62b615Svisa } 4633a62b615Svisa 4643a62b615Svisa /* 4653a62b615Svisa * Qualifies the path adding necessary dev 4663a62b615Svisa */ 4673a62b615Svisa 4683a62b615Svisa static char * 4693a62b615Svisa qualify(char *name) 4703a62b615Svisa { 4713a62b615Svisa char *p; 4723a62b615Svisa 4733a62b615Svisa for (p = name; *p; p++) 4743a62b615Svisa if (*p == ':') 4753a62b615Svisa break; 4763a62b615Svisa if (*p == ':') 4773a62b615Svisa strlcpy(cmd.path, name, sizeof(cmd.path)); 4783a62b615Svisa else 4793a62b615Svisa snprintf(cmd.path, sizeof cmd.path, "%s:%s", 4803a62b615Svisa cmd.bootdev, name); 4813a62b615Svisa return cmd.path; 4823a62b615Svisa } 4833a62b615Svisa 4843a62b615Svisa static int 4853a62b615Svisa Xreboot(void) 4863a62b615Svisa { 4873a62b615Svisa printf("Rebooting...\n"); 4883a62b615Svisa reboot(0); 4893a62b615Svisa return 0; /* just in case */ 4903a62b615Svisa } 4913a62b615Svisa 4923a62b615Svisa int 4933a62b615Svisa upgrade(void) 4943a62b615Svisa { 4953a62b615Svisa struct stat sb; 4963a62b615Svisa const char *path; 4973a62b615Svisa int ret = 0; 4983a62b615Svisa 4993a62b615Svisa path = disk_open(qualify("/bsd.upgrade")); 5003a62b615Svisa if (path == NULL) 5013a62b615Svisa return 0; 502*57e232a5Smiod if (stat(path, &sb) == 0 && S_ISREG(sb.st_mode)) { 5033a62b615Svisa ret = 1; 504d807f41cSkn if ((sb.st_mode & S_IXUSR) == 0) { 505d807f41cSkn printf("/bsd.upgrade is not u+x\n"); 506d807f41cSkn ret = 0; 507d807f41cSkn } 508*57e232a5Smiod } 5093a62b615Svisa disk_close(); 5103a62b615Svisa 5113a62b615Svisa return ret; 5123a62b615Svisa } 513