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