blob: f18edb50308383fe079beba7364474f82374b949 [file] [log] [blame]
Bill Richardsonfeb25182013-03-07 12:54:29 -08001/*
Bill Richardson6f396152014-07-15 12:52:19 -07002 * Copyright 2013 The Chromium OS Authors. All rights reserved.
Bill Richardsonfeb25182013-03-07 12:54:29 -08003 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
Bill Richardsonfeb25182013-03-07 12:54:29 -08007#include <errno.h>
8#include <fcntl.h>
9#include <limits.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/stat.h>
15#include <unistd.h>
16
17#include "futility.h"
18
Bill Richardson6db8c752013-04-05 13:30:43 -070019#define MYNAME_S MYNAME "_s"
Bill Richardsonfeb25182013-03-07 12:54:29 -080020
21/* File to use for logging, if present */
22#define LOGFILE "/tmp/futility.log"
23
24/* Normally logging will only happen if the logfile already exists. Uncomment
25 * this to force log file creation (and thus logging) always. */
26/* #define FORCE_LOGGING_ON */
27
28/******************************************************************************/
29
Bill Richardson31d95c22014-08-24 22:07:17 -070030static const char *const usage = "\n\
Bill Richardsonfeb25182013-03-07 12:54:29 -080031Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\
32\n\
33This is the unified firmware utility, which will eventually replace\n\
Bill Richardson6f396152014-07-15 12:52:19 -070034most of the distinct verified boot tools formerly produced by the\n\
Bill Richardsonfeb25182013-03-07 12:54:29 -080035vboot_reference package.\n\
36\n\
Bill Richardson6f396152014-07-15 12:52:19 -070037When symlinked under the name of one of those previous tools, should\n\
38fully implement the original behavior. It can also be invoked directly\n\
39as " MYNAME ", followed by the original name as the first argument.\n\
Bill Richardsonfeb25182013-03-07 12:54:29 -080040\n\
Bill Richardson6f396152014-07-15 12:52:19 -070041In either case it will append some usage information to " LOGFILE "\n\
42(iff that file exists), to help improve coverage and correctness.\n\
Bill Richardsonfeb25182013-03-07 12:54:29 -080043\n";
44
Bill Richardson7d028c42014-07-12 10:46:56 -070045static int do_help(int argc, char *argv[])
Bill Richardsonfeb25182013-03-07 12:54:29 -080046{
Bill Richardson31d95c22014-08-24 22:07:17 -070047 const struct futil_cmd_t *const *cmd;
48 int i;
Bill Richardsonfeb25182013-03-07 12:54:29 -080049
Bill Richardson31d95c22014-08-24 22:07:17 -070050 fputs(usage, stdout);
Bill Richardsonfeb25182013-03-07 12:54:29 -080051
Bill Richardson31d95c22014-08-24 22:07:17 -070052 printf("The following commands are built-in:\n\n");
Bill Richardsonfeb25182013-03-07 12:54:29 -080053
Bill Richardson31d95c22014-08-24 22:07:17 -070054 for (cmd = futil_cmds; *cmd; cmd++)
55 printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
56 printf("\n");
Bill Richardsonfeb25182013-03-07 12:54:29 -080057
Bill Richardson31d95c22014-08-24 22:07:17 -070058 if (argc) {
59 printf("FYI, you added these args that I'm ignoring:\n");
60 for (i = 0; i < argc; i++)
61 printf("argv[%d] = %s\n", i, argv[i]);
62 }
Bill Richardsonfeb25182013-03-07 12:54:29 -080063
Bill Richardson31d95c22014-08-24 22:07:17 -070064 return 0;
Bill Richardsonfeb25182013-03-07 12:54:29 -080065}
Bill Richardson31d95c22014-08-24 22:07:17 -070066
Bill Richardson6f396152014-07-15 12:52:19 -070067DECLARE_FUTIL_COMMAND(help, do_help,
68 "Show a bit of help (you're looking at it)");
Bill Richardsonfeb25182013-03-07 12:54:29 -080069
Bill Richardson6f396152014-07-15 12:52:19 -070070/*
71 * These are built-in functions that we'd like to abandon completely someday.
72 * TODO: If no one complains, get rid of them.
73 */
Bill Richardson31d95c22014-08-24 22:07:17 -070074static const char *const dep_cmds[] = {
75 "dev_sign_file",
Bill Richardsone1550442014-07-17 11:32:17 -070076};
77
Bill Richardson31d95c22014-08-24 22:07:17 -070078static const char *const dep_usage = "\n\
Bill Richardson6f396152014-07-15 12:52:19 -070079The program \"%s\" is deprecated and may go away soon.\n\
Bill Richardsone1550442014-07-17 11:32:17 -070080\n\
81If you feel this is in error, please open a bug at\n\
82\n\
83 http://dev.chromium.org/for-testers/bug-reporting-guidelines\n\
84\n\
85In the meantime, you may continue to use the program by invoking it as\n\
86\n\
87 " MYNAME " %s [...]\n\
88\n";
89
90static void deprecated(const char *depname)
91{
Bill Richardson31d95c22014-08-24 22:07:17 -070092 fprintf(stderr, dep_usage, depname, depname);
93 exit(1);
Bill Richardsone1550442014-07-17 11:32:17 -070094}
Bill Richardsonfeb25182013-03-07 12:54:29 -080095
96/******************************************************************************/
97/* Logging stuff */
98
99static int log_fd = -1;
100
101/* Write the string and a newline. Silently give up on errors */
102static void log_str(char *str)
103{
Bill Richardson31d95c22014-08-24 22:07:17 -0700104 int len, done, n;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800105
Bill Richardson31d95c22014-08-24 22:07:17 -0700106 if (log_fd < 0)
107 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800108
Bill Richardson31d95c22014-08-24 22:07:17 -0700109 if (!str)
110 str = "(NULL)";
Bill Richardsonfeb25182013-03-07 12:54:29 -0800111
Bill Richardson31d95c22014-08-24 22:07:17 -0700112 len = strlen(str);
113 if (len == 0) {
114 str = "(EMPTY)";
115 len = strlen(str);
116 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800117
Bill Richardson31d95c22014-08-24 22:07:17 -0700118 for (done = 0; done < len; done += n) {
119 n = write(log_fd, str + done, len - done);
120 if (n < 0)
121 return;
122 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800123
Bill Richardson31d95c22014-08-24 22:07:17 -0700124 if (write(log_fd, "\n", 1) < 0)
125 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800126}
127
128static void log_close(void)
129{
Bill Richardson31d95c22014-08-24 22:07:17 -0700130 struct flock lock;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800131
Bill Richardson31d95c22014-08-24 22:07:17 -0700132 if (log_fd >= 0) {
133 memset(&lock, 0, sizeof(lock));
134 lock.l_type = F_UNLCK;
135 lock.l_whence = SEEK_SET;
136 if (fcntl(log_fd, F_SETLKW, &lock))
137 perror("Unable to unlock log file");
Bill Richardsonfeb25182013-03-07 12:54:29 -0800138
Bill Richardson31d95c22014-08-24 22:07:17 -0700139 close(log_fd);
140 log_fd = -1;
141 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800142}
143
144static void log_open(void)
145{
Bill Richardson31d95c22014-08-24 22:07:17 -0700146 struct flock lock;
147 int ret;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800148
149#ifdef FORCE_LOGGING_ON
Bill Richardson31d95c22014-08-24 22:07:17 -0700150 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800151#else
Bill Richardson31d95c22014-08-24 22:07:17 -0700152 log_fd = open(LOGFILE, O_WRONLY | O_APPEND);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800153#endif
Bill Richardson31d95c22014-08-24 22:07:17 -0700154 if (log_fd < 0) {
Bill Richardsonfeb25182013-03-07 12:54:29 -0800155
Bill Richardson31d95c22014-08-24 22:07:17 -0700156 if (errno != EACCES)
157 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800158
Bill Richardson31d95c22014-08-24 22:07:17 -0700159 /* Permission problems should improve shortly ... */
160 sleep(1);
161 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
162 if (log_fd < 0) /* Nope, they didn't */
163 return;
164 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800165
Bill Richardson31d95c22014-08-24 22:07:17 -0700166 /* Let anyone have a turn */
167 fchmod(log_fd, 0666);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800168
Bill Richardson31d95c22014-08-24 22:07:17 -0700169 /* But only one at a time */
170 memset(&lock, 0, sizeof(lock));
171 lock.l_type = F_WRLCK;
172 lock.l_whence = SEEK_END;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800173
Bill Richardson31d95c22014-08-24 22:07:17 -0700174 ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
175 if (ret < 0)
176 log_close();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800177}
178
179#define CALLER_PREFIX "CALLER:"
180static void log_args(int argc, char *argv[])
181{
Bill Richardson31d95c22014-08-24 22:07:17 -0700182 int i;
183 ssize_t r;
184 pid_t parent;
185 char buf[80];
186 char str_caller[PATH_MAX + sizeof(CALLER_PREFIX)] = CALLER_PREFIX;
187 char *truename = str_caller + sizeof(CALLER_PREFIX) - 1;
188 /* Note: truename starts on the \0 from CALLER_PREFIX, so we can write
189 * PATH_MAX chars into truename and still append a \0 at the end. */
Bill Richardsonfeb25182013-03-07 12:54:29 -0800190
Bill Richardson31d95c22014-08-24 22:07:17 -0700191 log_open();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800192
Bill Richardson31d95c22014-08-24 22:07:17 -0700193 /* delimiter */
194 log_str("##### HEY #####");
Bill Richardsonfeb25182013-03-07 12:54:29 -0800195
Bill Richardson31d95c22014-08-24 22:07:17 -0700196 /* Can we tell who called us? */
197 parent = getppid();
198 snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
199 r = readlink(buf, truename, PATH_MAX);
200 if (r >= 0) {
201 truename[r] = '\0';
202 log_str(str_caller);
203 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800204
Bill Richardson31d95c22014-08-24 22:07:17 -0700205 /* Now log the stuff about ourselves */
206 for (i = 0; i < argc; i++)
207 log_str(argv[i]);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800208
Bill Richardson31d95c22014-08-24 22:07:17 -0700209 log_close();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800210}
211
Bill Richardsonfeb25182013-03-07 12:54:29 -0800212/******************************************************************************/
213/* Here we go */
214
215int main(int argc, char *argv[], char *envp[])
216{
Bill Richardson31d95c22014-08-24 22:07:17 -0700217 char *fullname, *progname;
218 char truename[PATH_MAX];
219 char buf[80];
220 pid_t myproc;
221 ssize_t r;
222 const struct futil_cmd_t *const *cmd;
223 int i;
224 int via_symlink = 0;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800225
Bill Richardson31d95c22014-08-24 22:07:17 -0700226 log_args(argc, argv);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800227
Bill Richardson31d95c22014-08-24 22:07:17 -0700228 /* How were we invoked? */
229 fullname = strdup(argv[0]);
230 progname = strrchr(argv[0], '/');
231 if (progname)
232 progname++;
233 else
234 progname = argv[0];
Bill Richardsonfeb25182013-03-07 12:54:29 -0800235
Bill Richardson31d95c22014-08-24 22:07:17 -0700236 /* Invoked directly by name */
237 if (0 == strcmp(progname, MYNAME) || 0 == strcmp(progname, MYNAME_S)) {
238 if (argc < 2) { /* must have an argument */
239 do_help(0, 0);
240 exit(1);
241 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800242
Bill Richardson31d95c22014-08-24 22:07:17 -0700243 /* We can just pass the rest along, then */
244 argc--;
245 argv++;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800246
Bill Richardson31d95c22014-08-24 22:07:17 -0700247 /* So now what function do we want to invoke? */
248 progname = strrchr(argv[0], '/');
249 if (progname)
250 progname++;
251 else
252 progname = argv[0];
253 } else { /* Invoked by symlink */
254 via_symlink = 1;
255 /* Block any deprecated functions. */
256 for (i = 0; i < ARRAY_SIZE(dep_cmds); i++)
257 if (0 == strcmp(dep_cmds[i], progname))
258 deprecated(progname);
259 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800260
Bill Richardson31d95c22014-08-24 22:07:17 -0700261 /* See if it's asking for something we know how to do ourselves */
262 for (cmd = futil_cmds; *cmd; cmd++)
263 if (0 == strcmp((*cmd)->name, progname))
264 return (*cmd)->handler(argc, argv);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800265
Bill Richardson31d95c22014-08-24 22:07:17 -0700266 /* Nope */
267 if (!via_symlink) {
268 do_help(0, 0);
269 exit(1);
270 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800271
Bill Richardson31d95c22014-08-24 22:07:17 -0700272 /* Complain about bogus symlink */
Bill Richardson6f396152014-07-15 12:52:19 -0700273
Bill Richardson31d95c22014-08-24 22:07:17 -0700274 myproc = getpid();
275 snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc);
276 r = readlink(buf, truename, PATH_MAX - 1);
277 if (r < 0) {
278 fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0],
279 buf, strerror(errno));
280 exit(1);
281 }
282 truename[r] = '\0';
Bill Richardsonfeb25182013-03-07 12:54:29 -0800283
Bill Richardson31d95c22014-08-24 22:07:17 -0700284 fprintf(stderr, "\n\
Bill Richardson6f396152014-07-15 12:52:19 -0700285The program\n\n %s\n\nis a symlink to\n\n %s\n\
286\n\
287However, " MYNAME " doesn't know how to implement that function.\n\
288\n\
289This is probably an error in your installation. If the problem persists\n\
290after a fresh checkout/build/install, please open a bug at\n\
291\n\
292 http://dev.chromium.org/for-testers/bug-reporting-guidelines\n\
293\n", fullname, truename);
Bill Richardson31d95c22014-08-24 22:07:17 -0700294 return 1;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800295}