/* * 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 #include #include #include #include #include #include #include #include #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; }