#include <sys/param.h>
#include <sys/reboot.h>
#include <libsa.h>
#include <lib/libkern/funcs.h>
#include "cmd.h"
#define CTRL(c) ((c)&0x1f)
static int Xboot(void);
static int Xecho(void);
static int Xhelp(void);
static int Xhexdump(void);
static int Xls(void);
static int Xnop(void);
static int Xreboot(void);
#ifdef BOOT_STTY
static int Xstty(void);
#endif
static int Xtime(void);
#ifdef MACHINE_CMD
static int Xmachine(void);
extern const struct cmd_table MACHINE_CMD[];
#endif
extern int Xset(void);
extern int Xenv(void);
#ifdef CHECK_SKIP_CONF
extern int CHECK_SKIP_CONF(void);
#endif
extern const struct cmd_table cmd_set[];
const struct cmd_table cmd_table[] = {
{"#", CMDT_CMD, Xnop},
{"boot", CMDT_CMD, Xboot},
{"echo", CMDT_CMD, Xecho},
{"env", CMDT_CMD, Xenv},
{"help", CMDT_CMD, Xhelp},
{"hexdump",CMDT_CMD, Xhexdump},
{"ls", CMDT_CMD, Xls},
#ifdef MACHINE_CMD
{"machine",CMDT_MDC, Xmachine},
#endif
{"reboot", CMDT_CMD, Xreboot},
{"set", CMDT_SET, Xset},
#ifdef BOOT_STTY
{"stty", CMDT_CMD, Xstty},
#endif
{"time", CMDT_CMD, Xtime},
{NULL, 0},
};
static void ls(char *, struct stat *);
static int readline(char *, size_t, int);
char *nextword(char *);
static char *whatcmd(const struct cmd_table **ct, char *);
static char *qualify(char *);
char cmd_buf[CMD_BUFF_SIZE];
int
getcmd(void)
{
cmd.cmd = NULL;
if (!readline(cmd_buf, sizeof(cmd_buf), cmd.timeout))
cmd.cmd = cmd_table;
return docmd();
}
int
read_conf(void)
{
#ifndef INSECURE
struct stat sb;
#endif
int fd, rc = 0;
#ifdef CHECK_SKIP_CONF
if (CHECK_SKIP_CONF()) {
printf("boot.conf processing skipped at operator request\n");
cmd.timeout = 0;
return -1;
}
#endif
if ((fd = open(qualify(cmd.conf), O_RDONLY)) < 0) {
if (errno != ENOENT && errno != ENXIO) {
printf("open(%s): %s\n", cmd.path, strerror(errno));
return 0;
}
return -1;
}
#ifndef INSECURE
(void) fstat(fd, &sb);
if (sb.st_uid || (sb.st_mode & 2)) {
printf("non-secure %s, will not proceed\n", cmd.path);
close(fd);
return -1;
}
#endif
do {
char *p = cmd_buf;
cmd.cmd = NULL;
do {
rc = read(fd, p, 1);
} while (rc > 0 && *p++ != '\n' &&
(p-cmd_buf) < sizeof(cmd_buf));
if (rc < 0) {
printf("%s: %s\n", cmd.path, strerror(errno));
break;
}
if (rc == 0) {
if (p != cmd_buf) {
*p = '\0';
rc = docmd();
break;
}
} else {
p--;
if (*p != '\n') {
printf("%s: line too long\n", cmd.path);
rc = -1;
}
*p = '\0';
}
} while (rc > 0 && !(rc = docmd()));
close(fd);
return rc;
}
int
docmd(void)
{
char *p = NULL;
const struct cmd_table *ct = cmd_table, *cs;
cmd.argc = 1;
if (cmd.cmd == NULL) {
for (p = cmd_buf; *p == ' ' || *p == '\t'; p++)
;
if (*p == '#' || *p == '\0') {
#ifdef DEBUG
printf("rem\n");
#endif
return 0;
}
ct = cmd_table;
cs = NULL;
cmd.argv[cmd.argc] = p;
p = whatcmd(&ct, p);
if (ct == NULL) {
cmd.argc++;
ct = cmd_table;
} else if (ct->cmd_type == CMDT_SET && p != NULL) {
cs = cmd_set;
#ifdef MACHINE_CMD
} else if (ct->cmd_type == CMDT_MDC && p != NULL) {
cs = MACHINE_CMD;
#endif
}
if (cs != NULL) {
p = whatcmd(&cs, p);
if (cs == NULL) {
printf("%s: syntax error\n", ct->cmd_name);
return 0;
}
ct = cs;
}
cmd.cmd = ct;
}
cmd.argv[0] = ct->cmd_name;
while (p && cmd.argc+1 < sizeof(cmd.argv) / sizeof(cmd.argv[0])) {
cmd.argv[cmd.argc++] = p;
p = nextword(p);
}
cmd.argv[cmd.argc] = NULL;
return (*cmd.cmd->cmd_exec)();
}
static char *
whatcmd(const struct cmd_table **ct, char *p)
{
char *q;
int l;
q = nextword(p);
for (l = 0; p[l]; l++)
;
while ((*ct)->cmd_name != NULL && strncmp(p, (*ct)->cmd_name, l))
(*ct)++;
if ((*ct)->cmd_name == NULL)
*ct = NULL;
return q;
}
static int
readline(char *buf, size_t n, int to)
{
#ifdef DEBUG
extern int debug;
#endif
char *p = buf, ch;
if (to > 0) {
time_t tt = getsecs() + to;
#ifdef DEBUG
if (debug > 2)
printf ("readline: timeout(%d) at %u\n", to, tt);
#endif
while (!cnischar() && getsecs() < tt)
continue;
if (!cnischar()) {
strlcpy(buf, "boot", 5);
putchar('\n');
return strlen(buf);
}
} else
while (!cnischar())
;
cmd.timeout = 0;
while (1) {
switch ((ch = getchar())) {
case CTRL('u'):
while (p > buf) {
putchar('\177');
p--;
}
continue;
case '\n':
case '\r':
*p = '\0';
break;
case '\b':
case '\177':
if (p > buf) {
putchar('\177');
p--;
}
continue;
default:
if (ch >= ' ' && ch < '\177') {
if (p - buf < n-1)
*p++ = ch;
else {
putchar('\007');
putchar('\177');
}
}
continue;
}
break;
}
return p - buf;
}
char *
nextword(char *p)
{
while (*p && *p != '\t' && *p != ' ')
p++;
if (*p) {
*p++ = '\0';
while (*p == '\t' || *p == ' ')
p++;
}
if (*p == '\0')
p = NULL;
return p;
}
static void
print_help(const struct cmd_table *ct)
{
for (; ct->cmd_name != NULL; ct++)
printf(" %s", ct->cmd_name);
putchar('\n');
}
static int
Xhelp(void)
{
printf("commands:");
print_help(cmd_table);
#ifdef MACHINE_CMD
return Xmachine();
#else
return 0;
#endif
}
static int
Xhexdump(void)
{
long long val[2];
char *ep;
int i;
if (cmd.argc != 3) {
printf("hexdump addr size\n");
return 0;
}
for (i = 1; i < cmd.argc; i++) {
val[i-1] = strtoll(cmd.argv[i], &ep, 0);
if (cmd.argv[i][0] == '\0' || *ep != '\0') {
printf("bad '%c' in \"%s\"\n", *ep, cmd.argv[i]);
return 0;
}
}
hexdump((void *)(unsigned long)val[0], val[1]);
return 0;
}
#ifdef MACHINE_CMD
static int
Xmachine(void)
{
printf("machine:");
print_help(MACHINE_CMD);
return 0;
}
#endif
static int
Xecho(void)
{
int i;
for (i = 1; i < cmd.argc; i++)
printf("%s ", cmd.argv[i]);
putchar('\n');
return 0;
}
#ifdef BOOT_STTY
static int
Xstty(void)
{
int sp;
char *cp;
dev_t dev;
if (cmd.argc == 1) {
printf("%s speed is %d\n", ttyname(0), cnspeed(0, -1));
return 0;
}
dev = ttydev(cmd.argv[1]);
if (dev == NODEV) {
printf("%s not a console device\n", cmd.argv[1]);
return 0;
}
if (cmd.argc == 2)
printf("%s speed is %d\n", cmd.argv[1],
cnspeed(dev, -1));
else {
sp = 0;
for (cp = cmd.argv[2]; isdigit(*cp); cp++)
sp = sp * 10 + (*cp - '0');
cnspeed(dev, sp);
}
return 0;
}
#endif
static int
Xtime(void)
{
time_t tt = getsecs();
if (cmd.argc == 1)
printf(ctime(&tt));
return 0;
}
static int
Xls(void)
{
struct stat sb;
char *p;
int fd;
if (stat(qualify((cmd.argv[1]? cmd.argv[1]: "/.")), &sb) < 0) {
printf("stat(%s): %s\n", cmd.path, strerror(errno));
return 0;
}
if ((sb.st_mode & S_IFMT) != S_IFDIR)
ls(cmd.path, &sb);
else {
if ((fd = opendir(cmd.path)) < 0) {
printf("opendir(%s): %s\n", cmd.path,
strerror(errno));
return 0;
}
for (p = cmd.path; *p; p++)
;
*p++ = '/';
*p = '\0';
while (readdir(fd, p) >= 0) {
if (stat(cmd.path, &sb) < 0)
printf("stat(%s): %s\n", cmd.path,
strerror(errno));
else
ls(p, &sb);
}
closedir (fd);
}
return 0;
}
#define lsrwx(mode,s) \
putchar ((mode) & S_IROTH? 'r' : '-'); \
putchar ((mode) & S_IWOTH? 'w' : '-'); \
putchar ((mode) & S_IXOTH? *(s): (s)[1]);
static void
ls(char *name, struct stat *sb)
{
putchar("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
lsrwx(sb->st_mode , (sb->st_mode & S_ISTXT? "tT" : "x-"));
printf (" %u,%u\t%lu\t%s\n", sb->st_uid, sb->st_gid,
(u_long)sb->st_size, name);
}
#undef lsrwx
int doboot = 1;
static int
Xnop(void)
{
if (doboot) {
doboot = 0;
return (Xboot());
}
return 0;
}
static int
Xboot(void)
{
if (cmd.argc > 1 && cmd.argv[1][0] != '-') {
qualify((cmd.argv[1]? cmd.argv[1]: cmd.image));
if (bootparse(2))
return 0;
} else {
if (bootparse(1))
return 0;
snprintf(cmd.path, sizeof cmd.path, "%s:%s",
cmd.bootdev, cmd.image);
}
return 1;
}
static char *
qualify(char *name)
{
char *p;
for (p = name; *p; p++)
if (*p == ':')
break;
if (*p == ':')
strlcpy(cmd.path, name, sizeof(cmd.path));
else
snprintf(cmd.path, sizeof cmd.path, "%s:%s",
cmd.bootdev, name);
return cmd.path;
}
static int
Xreboot(void)
{
printf("Rebooting...\n");
exit();
return 0;
}
int
upgrade(void)
{
struct stat sb;
if (stat(qualify(("/bsd.upgrade")), &sb) < 0)
return 0;
if ((sb.st_mode & S_IXUSR) == 0) {
printf("/bsd.upgrade is not u+x\n");
return 0;
}
return 1;
}