389 lines
8.4 KiB
C
389 lines
8.4 KiB
C
/* Copyright 2013 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "futility.h"
|
|
|
|
/******************************************************************************/
|
|
/* Logging stuff */
|
|
|
|
/* File to use for logging, if present */
|
|
#define LOGFILE "/tmp/futility.log"
|
|
|
|
/* Normally logging will only happen if the logfile already exists. Uncomment
|
|
* this to force log file creation (and thus logging) always. */
|
|
|
|
/* #define FORCE_LOGGING_ON */
|
|
|
|
static int log_fd = -1;
|
|
|
|
/* Write the string and a newline. Silently give up on errors */
|
|
static void log_str(const char *prefix, const char *str)
|
|
{
|
|
int len, done, n;
|
|
|
|
if (log_fd < 0)
|
|
return;
|
|
|
|
if (!str)
|
|
str = "(NULL)";
|
|
|
|
if (prefix && *prefix) {
|
|
len = strlen(prefix);
|
|
for (done = 0; done < len; done += n) {
|
|
n = write(log_fd, prefix + done, len - done);
|
|
if (n < 0)
|
|
return;
|
|
}
|
|
}
|
|
|
|
len = strlen(str);
|
|
if (len == 0) {
|
|
str = "(EMPTY)";
|
|
len = strlen(str);
|
|
}
|
|
|
|
for (done = 0; done < len; done += n) {
|
|
n = write(log_fd, str + done, len - done);
|
|
if (n < 0)
|
|
return;
|
|
}
|
|
|
|
if (write(log_fd, "\n", 1) < 0)
|
|
return;
|
|
}
|
|
|
|
static void log_close(void)
|
|
{
|
|
struct flock lock;
|
|
|
|
if (log_fd >= 0) {
|
|
memset(&lock, 0, sizeof(lock));
|
|
lock.l_type = F_UNLCK;
|
|
lock.l_whence = SEEK_SET;
|
|
if (fcntl(log_fd, F_SETLKW, &lock))
|
|
perror("Unable to unlock log file");
|
|
|
|
close(log_fd);
|
|
log_fd = -1;
|
|
}
|
|
}
|
|
|
|
static void log_open(void)
|
|
{
|
|
struct flock lock;
|
|
int ret;
|
|
|
|
#ifdef FORCE_LOGGING_ON
|
|
log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
|
#else
|
|
log_fd = open(LOGFILE, O_WRONLY | O_APPEND);
|
|
#endif
|
|
if (log_fd < 0) {
|
|
|
|
if (errno != EACCES)
|
|
return;
|
|
|
|
/* Permission problems should improve shortly ... */
|
|
sleep(1);
|
|
log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
|
if (log_fd < 0) /* Nope, they didn't */
|
|
return;
|
|
}
|
|
|
|
/* Let anyone have a turn */
|
|
fchmod(log_fd, 0666);
|
|
|
|
/* But only one at a time */
|
|
memset(&lock, 0, sizeof(lock));
|
|
lock.l_type = F_WRLCK;
|
|
lock.l_whence = SEEK_END;
|
|
|
|
ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
|
|
if (ret < 0)
|
|
log_close();
|
|
}
|
|
|
|
static void log_args(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
ssize_t r;
|
|
pid_t parent;
|
|
char buf[80];
|
|
FILE *fp;
|
|
char caller_buf[PATH_MAX];
|
|
|
|
log_open();
|
|
|
|
/* delimiter */
|
|
log_str(NULL, "##### LOG #####");
|
|
|
|
/* Can we tell who called us? */
|
|
parent = getppid();
|
|
snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
|
|
r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
|
|
if (r >= 0) {
|
|
caller_buf[r] = '\0';
|
|
log_str("CALLER:", caller_buf);
|
|
}
|
|
|
|
/* From where? */
|
|
snprintf(buf, sizeof(buf), "/proc/%d/cwd", parent);
|
|
r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
|
|
if (r >= 0) {
|
|
caller_buf[r] = '\0';
|
|
log_str("DIR:", caller_buf);
|
|
}
|
|
|
|
/* And maybe the args? */
|
|
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", parent);
|
|
fp = fopen(buf, "r");
|
|
if (fp) {
|
|
memset(caller_buf, 0, sizeof(caller_buf));
|
|
r = fread(caller_buf, 1, sizeof(caller_buf) - 1, fp);
|
|
if (r > 0) {
|
|
char *s = caller_buf;
|
|
for (i = 0; i < r && *s; ) {
|
|
log_str("CMDLINE:", s);
|
|
while (i < r && *s)
|
|
i++, s++;
|
|
i++, s++;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
/* Now log the stuff about ourselves */
|
|
for (i = 0; i < argc; i++)
|
|
log_str(NULL, argv[i]);
|
|
|
|
log_close();
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static const char *const usage = "\n"
|
|
"Usage: " MYNAME " [options] COMMAND [args...]\n"
|
|
"\n"
|
|
"This is the unified firmware utility, which will eventually replace\n"
|
|
"most of the distinct verified boot tools formerly produced by the\n"
|
|
"vboot_reference package.\n"
|
|
"\n"
|
|
"When symlinked under the name of one of those previous tools, it should\n"
|
|
"fully implement the original behavior. It can also be invoked directly\n"
|
|
"as " MYNAME ", followed by the original name as the first argument.\n"
|
|
"\n";
|
|
|
|
static const char *const options =
|
|
"Global options:\n"
|
|
"\n"
|
|
" --vb1 Use only vboot v1.0 binary formats\n"
|
|
" --vb21 Use only vboot v2.1 binary formats\n"
|
|
" --debug Be noisy about what's going on\n"
|
|
"\n";
|
|
|
|
static const struct futil_cmd_t *find_command(const char *name)
|
|
{
|
|
const struct futil_cmd_t *const *cmd;
|
|
|
|
for (cmd = futil_cmds; *cmd; cmd++)
|
|
if (((*cmd)->version & vboot_version) &&
|
|
!strcmp((*cmd)->name, name))
|
|
return *cmd;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void list_commands(void)
|
|
{
|
|
const struct futil_cmd_t *const *cmd;
|
|
|
|
for (cmd = futil_cmds; *cmd; cmd++)
|
|
if (vboot_version & (*cmd)->version)
|
|
printf(" %-20s %s\n",
|
|
(*cmd)->name, (*cmd)->shorthelp);
|
|
}
|
|
|
|
static int run_command(const struct futil_cmd_t *cmd, int argc, char *argv[])
|
|
{
|
|
int i;
|
|
VB2_DEBUG("\"%s\" ...\n", cmd->name);
|
|
for (i = 0; i < argc; i++)
|
|
VB2_DEBUG(" argv[%d] = \"%s\"\n", i, argv[i]);
|
|
|
|
return cmd->handler(argc, argv);
|
|
}
|
|
|
|
static int do_help(int argc, char *argv[])
|
|
{
|
|
const struct futil_cmd_t *cmd;
|
|
const char *vstr = "";
|
|
|
|
/* Help about a known command? */
|
|
if (argc > 1) {
|
|
cmd = find_command(argv[1]);
|
|
if (cmd) {
|
|
/* Let the command provide its own help */
|
|
argv[0] = argv[1];
|
|
argv[1] = (char *)"--help";
|
|
return run_command(cmd, argc, argv);
|
|
}
|
|
}
|
|
|
|
fputs(usage, stdout);
|
|
|
|
if (vboot_version == VBOOT_VERSION_ALL)
|
|
fputs(options, stdout);
|
|
|
|
switch (vboot_version) {
|
|
case VBOOT_VERSION_1_0:
|
|
vstr = "version 1.0 ";
|
|
break;
|
|
case VBOOT_VERSION_2_1:
|
|
vstr = "version 2.1 ";
|
|
break;
|
|
case VBOOT_VERSION_ALL:
|
|
vstr = "";
|
|
break;
|
|
}
|
|
printf("The following %scommands are built-in:\n\n", vstr);
|
|
list_commands();
|
|
printf("\nUse \"" MYNAME " help COMMAND\" for more information.\n\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
DECLARE_FUTIL_COMMAND(help, do_help, VBOOT_VERSION_ALL,
|
|
"Show a bit of help (you're looking at it)");
|
|
|
|
static const char ver_help[] =
|
|
"Show the futility source revision and build date";
|
|
static int do_version(int argc, char *argv[])
|
|
{
|
|
if (argc > 1)
|
|
printf("%s - %s\n", argv[0], ver_help);
|
|
else
|
|
printf("%s\n", futility_version);
|
|
return 0;
|
|
}
|
|
|
|
DECLARE_FUTIL_COMMAND(version, do_version, VBOOT_VERSION_ALL,
|
|
ver_help);
|
|
|
|
static char *simple_basename(char *str)
|
|
{
|
|
char *s = strrchr(str, '/');
|
|
if (s)
|
|
s++;
|
|
else
|
|
s = str;
|
|
return s;
|
|
}
|
|
|
|
/* Here we go */
|
|
#define OPT_HELP 1000
|
|
int main(int argc, char *argv[], char *envp[])
|
|
{
|
|
char *progname;
|
|
const struct futil_cmd_t *cmd;
|
|
int i, errorcnt = 0;
|
|
int vb_ver = VBOOT_VERSION_ALL;
|
|
int helpind = 0;
|
|
struct option long_opts[] = {
|
|
{"debug", 0, &debugging_enabled, 1},
|
|
{"vb1" , 0, &vb_ver, VBOOT_VERSION_1_0},
|
|
{"vb21", 0, &vb_ver, VBOOT_VERSION_2_1},
|
|
{"help", 0, 0, OPT_HELP},
|
|
{ 0, 0, 0, 0},
|
|
};
|
|
|
|
log_args(argc, argv);
|
|
|
|
/* How were we invoked? */
|
|
progname = simple_basename(argv[0]);
|
|
|
|
/* See if the program name is a command we recognize */
|
|
cmd = find_command(progname);
|
|
if (cmd) {
|
|
/* Yep, just do that */
|
|
return !!run_command(cmd, argc, argv);
|
|
}
|
|
|
|
/* Parse the global options, stopping at the first non-option. */
|
|
opterr = 0; /* quiet, you. */
|
|
while ((i = getopt_long(argc, argv, "+:", long_opts, NULL)) != -1) {
|
|
switch (i) {
|
|
case OPT_HELP:
|
|
/* Remember where we found this option */
|
|
/* Note: this might be GNU-specific */
|
|
helpind = optind - 1;
|
|
break;
|
|
case '?':
|
|
if (optopt)
|
|
fprintf(stderr, "Unrecognized option: -%c\n",
|
|
optopt);
|
|
else
|
|
fprintf(stderr, "Unrecognized option: %s\n",
|
|
argv[optind - 1]);
|
|
errorcnt++;
|
|
break;
|
|
case ':':
|
|
fprintf(stderr, "Missing argument to -%c\n", optopt);
|
|
errorcnt++;
|
|
break;
|
|
case 0: /* handled option */
|
|
break;
|
|
default:
|
|
FATAL("Unrecognized getopt output: %d\n", i);
|
|
}
|
|
}
|
|
vboot_version = vb_ver;
|
|
|
|
/*
|
|
* Translate "--help" in the args to "help" as the first parameter,
|
|
* by rearranging argv[].
|
|
*/
|
|
if (helpind) {
|
|
int j;
|
|
optind--;
|
|
for (j = helpind; j < optind; j++)
|
|
argv[j] = argv[j + 1];
|
|
argv[j] = (char *)"help";
|
|
}
|
|
|
|
/* We require a command name. */
|
|
if (errorcnt || argc == optind) {
|
|
do_help(1, argv);
|
|
return 1;
|
|
}
|
|
|
|
/* For reasons I've forgotten, treat /blah/blah/CMD the same as CMD */
|
|
argv[optind] = simple_basename(argv[optind]);
|
|
|
|
/* Do we recognize the command? */
|
|
cmd = find_command(argv[optind]);
|
|
if (cmd) {
|
|
/* Reset so commands can parse their own options */
|
|
argc -= optind;
|
|
argv += optind;
|
|
optind = 0;
|
|
return !!run_command(cmd, argc, argv);
|
|
}
|
|
|
|
/* Nope. We've no clue what we're being asked to do. */
|
|
do_help(1, argv);
|
|
return 1;
|
|
}
|