blob: ba9fb16315887c5ee9aedeb4555f5b983e664fef [file] [log] [blame]
/*
* 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 <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(char *prefix, 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 " 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 void print_help(const char *cmd)
{
puts(usage);
}
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 (0 == 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++)
printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
}
static int do_help(int argc, char *argv[])
{
const struct futil_cmd_t *cmd;
if (argc >= 2) {
cmd = find_command(argv[1]);
if (cmd) {
printf("\n%s - %s\n", argv[1], cmd->shorthelp);
if (cmd->longhelp)
cmd->longhelp(argv[1]);
return 0;
}
}
fputs(usage, stdout);
printf("The following commands are built-in:\n\n");
list_commands();
printf("\nUse \"" MYNAME " help COMMAND\" for more information.\n\n");
return 0;
}
DECLARE_FUTIL_COMMAND(help, do_help,
"Show a bit of help (you're looking at it)",
print_help);
static int do_version(int argc, char *argv[])
{
printf("%s\n", futility_version);
return 0;
}
DECLARE_FUTIL_COMMAND(version, do_version,
"Show the futility source revision and build date",
NULL);
int run_command(const struct futil_cmd_t *cmd, int argc, char *argv[])
{
/* Handle the "CMD --help" case ourselves */
if (2 == argc && 0 == strcmp(argv[1], "--help")) {
char *fake_argv[] = {"help",
(char *)cmd->name,
NULL};
return do_help(2, fake_argv);
}
return cmd->handler(argc, argv);
}
static char *simple_basename(char *str)
{
char *s = strrchr(str, '/');
if (s)
s++;
else
s = str;
return s;
}
/* Here we go */
int main(int argc, char *argv[], char *envp[])
{
char *progname;
const struct futil_cmd_t *cmd;
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)
return run_command(cmd, argc, argv);
/* The program name means nothing, so we require an argument. */
if (argc < 2) {
do_help(0, 0);
return 1;
}
/* The first arg should be a command we recognize */
argc--;
argv++;
/* For reasons I've forgotten, treat /blah/blah/CMD the same as CMD */
progname = simple_basename(argv[0]);
/* Oh, and treat "--foo" the same as "foo" */
while (*progname == '-')
progname++;
/* Do we recognize the command? */
cmd = find_command(progname);
if (cmd)
return run_command(cmd, argc, argv);
/* Nope. We've no clue what we're being asked to do. */
do_help(0, 0);
return 1;
}