Wrap all vboot utilities with futility.

This moves all the old userspace utilities generated by vboot_reference into
a subdirectory not in $PATH, and replaces them with symlinks to a single
executable named 'futility'. At the moment that utility just execs the
original utilities (optionally logging that fact first).

Ultimately, the old utilities will be subsumed into a single binary instead
of multiple separate executables.

There is a matching CL needed to make the recovery image creation work.

BUG=chromium-os:37062
BRANCH=none
CQ-DEPEND=CL:44864
TEST=auto

To test, build everything, test everything. It should work as before in all
cases. I have built normal images, test images, factory installers, recovery
images; they all seem to work.

I've run trybots on daisy-paladin link-paladin lumpy-paladin and alex-paladin.

Change-Id: Ie93db676f2ed2a64e4b13b3b5dc6b65a77db0f8c
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/44871
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
This commit is contained in:
Bill Richardson
2013-03-07 12:54:29 -08:00
committed by ChromeBot
parent 5fed2a6670
commit feb2518166
11 changed files with 564 additions and 36 deletions

View File

@@ -37,22 +37,29 @@
# We should only run pwd once, not every time we refer to ${BUILD}.
SRCDIR := $(shell pwd)
BUILD ?= $(SRCDIR)/build
BUILD = $(SRCDIR)/build
export BUILD
# Stuff for 'make install'
INSTALL ?= install
DESTDIR ?= /usr/local/bin
INSTALL = install
DESTDIR = /usr/local/bin
OLDDIR = old_bins
# Where exactly do the pieces go?
# FT_DIR = futility target directory - where it will be on the target
# F_DIR = futility install directory - where it gets put right now
# UB_DIR = userspace binary directory for futility's exec() targets
# VB_DIR = target vboot directory - for dev-mode-only helpers, keys, etc.
ifeq (${MINIMAL},)
# Host install just puts everything in one place
UB_DIR=${DESTDIR}
SB_DIR=${DESTDIR}
VB_DIR=${DESTDIR}
FT_DIR=${DESTDIR}
F_DIR=${DESTDIR}
UB_DIR=${DESTDIR}/${OLDDIR}
else
# Target install puts things into DESTDIR subdirectories
UB_DIR=${DESTDIR}/usr/bin
SB_DIR=${DESTDIR}/sbin
FT_DIR=/usr/bin
F_DIR=${DESTDIR}${FT_DIR}
UB_DIR=${F_DIR}/${OLDDIR}
VB_DIR=${DESTDIR}/usr/share/vboot/bin
endif
@@ -127,6 +134,10 @@ CC ?= gcc
CFLAGS += -DCHROMEOS_ENVIRONMENT -Wall -Werror # HEY: always want last two?
endif
ifneq (${OLDDIR},)
CFLAGS += -DOLDDIR=${OLDDIR}
endif
ifneq (${DEBUG},)
CFLAGS += -DVBOOT_DEBUG
endif
@@ -135,6 +146,10 @@ ifeq (${DISABLE_NDEBUG},)
CFLAGS += -DNDEBUG
endif
ifneq (${FORCE_LOGGING_ON},)
CFLAGS += -DFORCE_LOGGING_ON=${FORCE_LOGGING_ON}
endif
# Create / use dependency files
CFLAGS += -MMD -MF $@.d
@@ -429,8 +444,13 @@ SIGNING_COMMON = scripts/image_signing/common_minimal.sh
# The unified firmware utility will eventually replace all the others
FUTIL_BIN = ${BUILD}/futility/futility
# These are the others it will replace.
FUTIL_OLD = $(notdir ${CGPT} ${UTIL_BINS} ${UTIL_SCRIPTS} \
${SIGNING_SCRIPTS} ${SIGNING_SCRIPTS_DEV})
FUTIL_SRCS = \
futility/IGNOREME.c
futility/futility.c \
futility/cmd_foo.c
FUTIL_LDS = futility/futility.lds
@@ -696,13 +716,15 @@ utils_install: ${UTIL_BINS} ${UTIL_SCRIPTS}
# And some signing stuff for the target
.PHONY: signing_install
signing_install: ${SIGNING_SCRIPTS} ${SIGNING_SCRIPTS_DEV} ${SIGNING_COMMON}
ifneq (${MINIMAL},)
@printf " INSTALL SIGNING\n"
${Q}mkdir -p ${UB_DIR}
${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS}
${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS_DEV}
${Q}${INSTALL} -t ${UB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON}
ifneq (${VB_DIR},)
${Q}mkdir -p ${VB_DIR}
${Q}${INSTALL} -t ${VB_DIR} ${SIGNING_SCRIPTS_DEV}
${Q}${INSTALL} -t ${VB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON}
${Q}for prog in $(notdir ${SIGNING_SCRIPTS_DEV}); do \
ln -sf "${FT_DIR}/futility" "${VB_DIR}/$$prog"; done
endif
# ----------------------------------------------------------------------------
@@ -718,9 +740,10 @@ ${FUTIL_BIN}: ${FUTIL_LDS} ${FUTIL_OBJS}
.PHONY: futil_install
futil_install: ${FUTIL_BIN}
@printf " INSTALL futility\n"
${Q}mkdir -p ${UB_DIR}
${Q}${INSTALL} -t ${UB_DIR} $^
${Q}mkdir -p ${F_DIR}
${Q}${INSTALL} -t ${F_DIR} ${FUTIL_BIN}
${Q}for prog in ${FUTIL_OLD}; do \
ln -sf futility "${F_DIR}/$$prog"; done
# ----------------------------------------------------------------------------
# Utility to generate TLCL structure definition header file.
@@ -791,6 +814,10 @@ ${BUILD}/firmware/linktest/main: LIBS = ${FWLIB}
# GBB utility needs C++ linker. TODO: It shouldn't.
${BUILD}/utility/gbb_utility: LD = ${CXX}
# Because we play some clever linker script games to add new commands without
# changing any header files, futility must be linked with ld.bfd, not gold.
${FUTIL_BIN}: LDFLAGS += -fuse-ld=bfd
# Some utilities need external crypto functions
${BUILD}/utility/dumpRSAPublicKey: LDLIBS += ${CRYPTO_LIBS}
${BUILD}/utility/pad_digest_utility: LDLIBS += ${CRYPTO_LIBS}
@@ -855,7 +882,7 @@ test_targets:: runcgpttests runmisctests
ifeq (${MINIMAL},)
# Bitmap utility isn't compiled for minimal variant
test_targets:: runbmptests
test_targets:: runbmptests runfutiltests
# Scripts don't work under qemu testing
# TODO: convert scripts to makefile so they can be called directly
test_targets:: runtestscripts
@@ -938,9 +965,9 @@ runmisctests: test_setup
${RUNTEST} ${BUILD_RUN}/tests/vboot_nvstorage_test
.PHONY: runfutiltests
runfutiltests: DESTDIR := ${TEST_INSTALL_DIR}
runfutiltests: override DESTDIR = ${TEST_INSTALL_DIR}
runfutiltests: test_setup install
@echo "$@ passed"
futility/tests/run_futility_tests.sh ${DESTDIR}
# Run long tests, including all permutations of encryption keys (instead of
# just the ones we use) and tests of currently-unused code.
@@ -987,4 +1014,3 @@ coverage:
else
coverage: coverage_init runtests coverage_html
endif

View File

@@ -1,12 +0,0 @@
/*
* Copyright (c) 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 <stdio.h>
int main(int argc, char *argv[])
{
printf("Pay no attention to that man behind the curtain.\n");
return 1;
}

22
futility/cmd_foo.c Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 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 <stdio.h>
#include "futility.h"
static int do_something(int argc, char *argv[])
{
int i;
printf("this is %s\n", __func__);
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
DECLARE_FUTIL_COMMAND(foo, do_something, "invoke a foo");
DECLARE_FUTIL_COMMAND(bar, do_something, "go to bar");
DECLARE_FUTIL_COMMAND(hey, do_something, "shout");

285
futility/futility.c Normal file
View File

@@ -0,0 +1,285 @@
/*
* Copyright (c) 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.
*/
#define _GNU_SOURCE
#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"
#define MYNAME "futility"
#ifdef OLDDIR
#define XSTR(A) STR(A)
#define STR(A) #A
#define SUBDIR XSTR(OLDDIR)
#else
#define SUBDIR "old_bins"
#endif
/* 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 const char * const usage= "\n\
Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\
\n\
This is the unified firmware utility, which will eventually replace\n\
all the distinct userspace tools formerly produced by the\n\
vboot_reference package.\n\
\n\
When symlinked under the name of one of those previous tools, it can\n\
do one of two things: either it will fully implement the original\n\
behavior, or (until that functionality is complete) it will just exec\n\
the original binary.\n\
\n\
In either case it can append some usage information to " LOGFILE "\n\
to help improve coverage and correctness.\n\
\n\
If you invoke it directly instead of via a symlink, it requires one\n\
argument, which is the name of the old binary to exec. That binary\n\
must be located in a directory named \"" SUBDIR "\" underneath\n\
the " MYNAME " executable.\n\
\n";
static int help(int argc, char *argv[])
{
futil_cmd_t *cmd;
int i;
fputs(usage, stdout);
printf("The following commands are built-in:\n");
for (cmd = futil_cmds_start(); cmd < futil_cmds_end(); cmd++)
printf(" %-20s %s\n",
cmd->name, cmd->shorthelp);
printf("\n");
printf("FYI, you added these args that I'm ignoring:\n");
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
DECLARE_FUTIL_COMMAND(help, help, "Show a bit of help");
/******************************************************************************/
/* Logging stuff */
static int log_fd = -1;
/* Write the string and a newline. Silently give up on errors */
static void log_str(char *str)
{
int len, done, n;
if (log_fd < 0)
return;
if (!str)
str = "(NULL)";
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;
}
write(log_fd, "\n", 1);
}
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();
}
#define CALLER_PREFIX "CALLER:"
static void log_args(int argc, char *argv[])
{
int i;
ssize_t r;
pid_t parent;
char buf[80];
char str_caller[PATH_MAX + sizeof(CALLER_PREFIX)] = CALLER_PREFIX;
char *truename = str_caller + sizeof(CALLER_PREFIX) - 1;
/* Note: truename starts on the \0 from CALLER_PREFIX, so we can write
* PATH_MAX chars into truename and still append a \0 at the end. */
log_open();
/* delimiter */
log_str("##### HEY #####");
/* Can we tell who called us? */
parent = getppid();
snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
r = readlink(buf, truename, PATH_MAX);
if (r >= 0) {
truename[r] = '\0';
log_str(str_caller);
}
/* Now log the stuff about ourselves */
for (i = 0; i < argc; i++)
log_str(argv[i]);
log_close();
}
/******************************************************************************/
/* Here we go */
int main(int argc, char *argv[], char *envp[])
{
char *progname;
char truename[PATH_MAX];
char oldname[PATH_MAX];
char buf[80];
pid_t myproc;
ssize_t r;
char *s;
futil_cmd_t *cmd;
log_args(argc, argv);
/* How were we invoked? */
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
/* Invoked directly by name */
if (0 == strcmp(progname, MYNAME)) {
if (argc < 2) { /* must have an argument */
fputs(usage, stderr);
exit(1);
}
/* We can just pass the rest along, then */
argc--;
argv++;
/* So now what name do we want to invoke? */
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
}
/* See if it's asking for something we know how to do ourselves */
for (cmd = futil_cmds_start(); cmd < futil_cmds_end(); cmd++)
if (0 == strcmp(cmd->name, progname))
return cmd->handler(argc, argv);
/* Nope, it must be wrapped */
/* The old binaries live under the true executable. Find out where that is. */
myproc = getpid();
snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc);
r = readlink(buf, truename, PATH_MAX - 1);
if (r < 0) {
fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0],
buf, strerror(errno));
exit(1);
} else if (r == PATH_MAX - 1) {
/* Yes, it might _just_ fit, but we'll count that as wrong anyway. We can't
* determine the right size using the example in the readlink manpage,
* because the /proc symlink returns an st_size of 0. */
fprintf(stderr, "%s is too long: %s => %s\n", MYNAME, argv[0], buf);
exit(1);
}
truename[r] = '\0';
s = strrchr(truename, '/'); /* Find the true directory */
if (s) {
*s = '\0';
} else { /* I don't think this can happen */
fprintf(stderr, "%s says %s doesn't make sense\n", MYNAME, truename);
exit(1);
}
/* If the old binary path doesn't fit, just give up. */
r = snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname);
if (r >= PATH_MAX) {
fprintf(stderr, "%s/%s/%s is too long\n", truename, SUBDIR, progname);
exit(1);
}
fflush(0);
execve(oldname, argv, envp);
fprintf(stderr, "%s failed to exec %s: %s\n", MYNAME,
oldname, strerror(errno));
return 1;
}

57
futility/futility.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 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 <stdint.h>
#ifndef VBOOT_REFERENCE_FUTILITY_H_
#define VBOOT_REFERENCE_FUTILITY_H_
/*
Here's a structure to define the commands that futility implements.
*/
typedef struct {
const char const * name;
int (*handler)(int argc, char **argv);
const char const * shorthelp;
} __attribute__ ((aligned (16))) futil_cmd_t ; /* align for x86_64 ABI */
/*
* Create an instance in a separate section. We'll have a linker script to
* gather them all up later, so we can refer to them without explictly
* declaring every function in a header somewhere
*/
#define DECLARE_FUTIL_COMMAND(name, handler, shorthelp) \
static const char __futil_cmd_name_##name[] = #name; \
const futil_cmd_t __futil_cmd_##name \
__attribute__((section(".futil_cmds." #name))) \
= { __futil_cmd_name_##name, handler, shorthelp }
/*
* Functions to find the command table. We have to play some games here,
* because the x86_64 ABI says this:
*
* An array uses the same alignment as its elements, except that a local or
* global array variable that requires at least 16 bytes, or a C99 local or
* global variable-length array variable, always has alignment of at least
* 16 bytes.
*
* The linker script doesn't know what alignment to use for __futil_cmds_start,
* because that's determined at compile-time and unavailable to the script
* unless we define one global futil_cmd_t in advance.
*/
static inline futil_cmd_t *futil_cmds_start(void)
{
extern uintptr_t __futil_cmds_start[]; /* from linker script */
uintptr_t mask = sizeof(futil_cmd_t) - 1;
uintptr_t addr = (uintptr_t)(__futil_cmds_start);
return (futil_cmd_t *)((addr + mask) & ~mask);
}
static inline futil_cmd_t *futil_cmds_end(void)
{
extern uintptr_t __futil_cmds_end[]; /* from linker script */
return (futil_cmd_t *)(&__futil_cmds_end[0]);
}
#endif /* VBOOT_REFERENCE_FUTILITY_H_ */

View File

@@ -4,4 +4,11 @@
* found in the LICENSE file.
*/
/* Nothing to see here. Move along... */
SECTIONS
{
.rodata : {
__futil_cmds_start = .;
*(SORT(.futil_cmds.*));
__futil_cmds_end = .;
}
}

47
futility/tests/common.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
# Copyright (c) 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.
# Color output encodings.
COL_RED='\E[31;1m'
COL_GREEN='\E[32;1m'
COL_YELLOW='\E[33;1m'
COL_BLUE='\E[34;1m'
COL_STOP='\E[0;m'
# args: [message]
green() {
echo -e "${COL_GREEN}$*${COL_STOP}"
}
# args: [message]
yellow() {
echo -e "${COL_YELLOW}WARNING: $*${COL_STOP}"
}
# args: [message]
red() {
echo -e "${COL_RED}$*${COL_STOP}"
}
# args: [nested level] [message]
error() {
local lev=${1:-}
case "${1:-}" in
[0-9]*)
lev=$1
shift
;;
*) lev=0
;;
esac
local x=$(caller $lev)
local cline="${x%% *}"
local cfile="${x#* }"
cfile="${cfile##*/}"
local args="$*"
local spacer="${args:+: }"
red "at ${cfile}, line ${cline}${spacer}${args}" 1>&2
exit 1
}

View File

@@ -0,0 +1,78 @@
#!/bin/bash
# Copyright (c) 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.
# Load common constants and variables.
. "$(dirname "$0")/common.sh"
# Where are the programs I'm testing against?
[ -z "${1:-}" ] && error "Directory argument is required"
BINDIR="$1"
shift
FUTILITY="$BINDIR/futility"
OLDDIR="$BINDIR/old_bins"
BUILD=$(dirname "${BINDIR}")
# Here are the old programs to be wrapped
# FIXME(chromium-os:37062): There are others besides these.
# FIXME: dev_debug_vboot isn't tested right now.
PROGS=${*:-cgpt crossystem dev_sign_file dumpRSAPublicKey
dump_fmap dump_kernel_config enable_dev_usb_boot gbb_utility
tpm_init_temp_fix tpmc vbutil_firmware vbutil_kernel vbutil_key
vbutil_keyblock vbutil_what_keys}
# Get ready
pass=0
progs=0
pwd
OUTDIR="${BUILD}/tests/futility_test_dir"
[ -d "$OUTDIR" ] || mkdir -p "$OUTDIR"
# For now just compare results of invoking each program with no args.
# FIXME(chromium-os:37062): Create true rigorous tests for every program.
for i in $PROGS; do
: $(( progs++ ))
# Try the real thing first
echo -n "$i ... "
rc=$("${OLDDIR}/$i" \
1>"${OUTDIR}/$i.stdout.orig" 2>"${OUTDIR}/$i.stderr.orig" \
|| echo "$?")
echo "${rc:-0}" > "${OUTDIR}/$i.return.orig"
# Then try the symlink
rc=$("$BINDIR/$i" 1>"${OUTDIR}/$i.stdout.link" \
2>"${OUTDIR}/$i.stderr.link" || echo "$?")
echo "${rc:-0}" > "${OUTDIR}/$i.return.link"
# And finally try the explicit wrapper
rc=$("$FUTILITY" "$i" 1>"${OUTDIR}/$i.stdout.futil" \
2>"${OUTDIR}/$i.stderr.futil" || echo "$?")
echo "${rc:-0}" > "${OUTDIR}/$i.return.futil"
# Different?
if cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.link" &&
cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.link" &&
cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.link" &&
cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.futil" &&
cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.futil" &&
cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.futil" ; then
green "passed"
: $(( pass++ ))
rm -f ${OUTDIR}/$i.{stdout,stderr,return}.{orig,link,futil}
else
red "failed"
fi
done
# done
if [ "$pass" -eq "$progs" ]; then
green "Success: $pass / $progs passed"
exit 0
fi
red "FAIL: $pass / $progs passed"
exit 1

View File

@@ -141,9 +141,15 @@ int main(int argc, char* argv[]) {
X509* cert = NULL;
RSA* pubkey = NULL;
EVP_PKEY* key;
char *progname;
if (argc != 3 || (strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", argv[0]);
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
return -1;
}

View File

@@ -486,6 +486,11 @@ using vboot_reference::GoogleBinaryBlockUtil;
// utility function: provide usage of this utility and exit.
static void usagehelp_exit(const char *prog_name) {
const char *basename = strrchr(prog_name, '/');
if (basename)
basename++;
else
basename = prog_name;
fprintf(stderr,
"Utility to manage Google Binary Block (GBB)\n"
"Usage: %s [-g|-s|-c] [OPTIONS] bios_file [output_file]\n"
@@ -516,7 +521,7 @@ static void usagehelp_exit(const char *prog_name) {
" %s -g bios.bin\n"
" %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n"
" %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n",
prog_name, prog_name, prog_name, prog_name);
basename, basename, basename, basename);
exit(1);
}

View File

@@ -412,9 +412,16 @@ command_record command_table[] = {
static int n_commands = sizeof(command_table) / sizeof(command_table[0]);
int main(int argc, char* argv[]) {
char *progname;
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
if (argc < 2) {
fprintf(stderr, "usage: %s <TPM command> [args]\n or: %s help\n",
argv[0], argv[0]);
progname, progname);
return OTHER_ERROR;
} else {
command_record* c;
@@ -439,7 +446,7 @@ int main(int argc, char* argv[]) {
}
/* No command matched. */
fprintf(stderr, "%s: unknown command: %s\n", argv[0], cmd);
fprintf(stderr, "%s: unknown command: %s\n", progname, cmd);
return OTHER_ERROR;
}
}