blob: b4070618df0fd2dcc3da7417534849b389c1b77d [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 Richardson779796f2014-09-23 11:47:40 -070019
20/******************************************************************************/
21/* Logging stuff */
Bill Richardsonfeb25182013-03-07 12:54:29 -080022
23/* File to use for logging, if present */
24#define LOGFILE "/tmp/futility.log"
25
26/* Normally logging will only happen if the logfile already exists. Uncomment
27 * this to force log file creation (and thus logging) always. */
Bill Richardson779796f2014-09-23 11:47:40 -070028
Bill Richardsonfeb25182013-03-07 12:54:29 -080029/* #define FORCE_LOGGING_ON */
30
Bill Richardsonfeb25182013-03-07 12:54:29 -080031static int log_fd = -1;
32
33/* Write the string and a newline. Silently give up on errors */
Bill Richardsonee53d652014-09-04 16:18:15 -070034static void log_str(char *prefix, char *str)
Bill Richardsonfeb25182013-03-07 12:54:29 -080035{
Bill Richardson31d95c22014-08-24 22:07:17 -070036 int len, done, n;
Bill Richardsonfeb25182013-03-07 12:54:29 -080037
Bill Richardson31d95c22014-08-24 22:07:17 -070038 if (log_fd < 0)
39 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -080040
Bill Richardson31d95c22014-08-24 22:07:17 -070041 if (!str)
42 str = "(NULL)";
Bill Richardsonfeb25182013-03-07 12:54:29 -080043
Bill Richardsonee53d652014-09-04 16:18:15 -070044 if (prefix && *prefix) {
45 len = strlen(prefix);
46 for (done = 0; done < len; done += n) {
47 n = write(log_fd, prefix + done, len - done);
48 if (n < 0)
49 return;
50 }
51 }
52
Bill Richardson31d95c22014-08-24 22:07:17 -070053 len = strlen(str);
54 if (len == 0) {
55 str = "(EMPTY)";
56 len = strlen(str);
57 }
Bill Richardsonfeb25182013-03-07 12:54:29 -080058
Bill Richardson31d95c22014-08-24 22:07:17 -070059 for (done = 0; done < len; done += n) {
60 n = write(log_fd, str + done, len - done);
61 if (n < 0)
62 return;
63 }
Bill Richardsonfeb25182013-03-07 12:54:29 -080064
Bill Richardson31d95c22014-08-24 22:07:17 -070065 if (write(log_fd, "\n", 1) < 0)
66 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -080067}
68
69static void log_close(void)
70{
Bill Richardson31d95c22014-08-24 22:07:17 -070071 struct flock lock;
Bill Richardsonfeb25182013-03-07 12:54:29 -080072
Bill Richardson31d95c22014-08-24 22:07:17 -070073 if (log_fd >= 0) {
74 memset(&lock, 0, sizeof(lock));
75 lock.l_type = F_UNLCK;
76 lock.l_whence = SEEK_SET;
77 if (fcntl(log_fd, F_SETLKW, &lock))
78 perror("Unable to unlock log file");
Bill Richardsonfeb25182013-03-07 12:54:29 -080079
Bill Richardson31d95c22014-08-24 22:07:17 -070080 close(log_fd);
81 log_fd = -1;
82 }
Bill Richardsonfeb25182013-03-07 12:54:29 -080083}
84
85static void log_open(void)
86{
Bill Richardson31d95c22014-08-24 22:07:17 -070087 struct flock lock;
88 int ret;
Bill Richardsonfeb25182013-03-07 12:54:29 -080089
90#ifdef FORCE_LOGGING_ON
Bill Richardson31d95c22014-08-24 22:07:17 -070091 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
Bill Richardsonfeb25182013-03-07 12:54:29 -080092#else
Bill Richardson31d95c22014-08-24 22:07:17 -070093 log_fd = open(LOGFILE, O_WRONLY | O_APPEND);
Bill Richardsonfeb25182013-03-07 12:54:29 -080094#endif
Bill Richardson31d95c22014-08-24 22:07:17 -070095 if (log_fd < 0) {
Bill Richardsonfeb25182013-03-07 12:54:29 -080096
Bill Richardson31d95c22014-08-24 22:07:17 -070097 if (errno != EACCES)
98 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -080099
Bill Richardson31d95c22014-08-24 22:07:17 -0700100 /* Permission problems should improve shortly ... */
101 sleep(1);
102 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
103 if (log_fd < 0) /* Nope, they didn't */
104 return;
105 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800106
Bill Richardson31d95c22014-08-24 22:07:17 -0700107 /* Let anyone have a turn */
108 fchmod(log_fd, 0666);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800109
Bill Richardson31d95c22014-08-24 22:07:17 -0700110 /* But only one at a time */
111 memset(&lock, 0, sizeof(lock));
112 lock.l_type = F_WRLCK;
113 lock.l_whence = SEEK_END;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800114
Bill Richardson31d95c22014-08-24 22:07:17 -0700115 ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
116 if (ret < 0)
117 log_close();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800118}
119
Bill Richardsonfeb25182013-03-07 12:54:29 -0800120static void log_args(int argc, char *argv[])
121{
Bill Richardson31d95c22014-08-24 22:07:17 -0700122 int i;
123 ssize_t r;
124 pid_t parent;
125 char buf[80];
Bill Richardsonee53d652014-09-04 16:18:15 -0700126 FILE *fp;
127 char caller_buf[PATH_MAX];
Bill Richardsonfeb25182013-03-07 12:54:29 -0800128
Bill Richardson31d95c22014-08-24 22:07:17 -0700129 log_open();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800130
Bill Richardson31d95c22014-08-24 22:07:17 -0700131 /* delimiter */
Bill Richardson6df3e332014-10-02 18:50:33 -0700132 log_str(NULL, "##### LOG #####");
Bill Richardsonfeb25182013-03-07 12:54:29 -0800133
Bill Richardson31d95c22014-08-24 22:07:17 -0700134 /* Can we tell who called us? */
135 parent = getppid();
136 snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
Bill Richardsonee53d652014-09-04 16:18:15 -0700137 r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
Bill Richardson31d95c22014-08-24 22:07:17 -0700138 if (r >= 0) {
Bill Richardsonee53d652014-09-04 16:18:15 -0700139 caller_buf[r] = '\0';
140 log_str("CALLER:", caller_buf);
141 }
142
143 /* From where? */
144 snprintf(buf, sizeof(buf), "/proc/%d/cwd", parent);
145 r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
146 if (r >= 0) {
147 caller_buf[r] = '\0';
148 log_str("DIR:", caller_buf);
149 }
150
151 /* And maybe the args? */
152 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", parent);
153 fp = fopen(buf, "r");
154 if (fp) {
155 memset(caller_buf, 0, sizeof(caller_buf));
156 r = fread(caller_buf, 1, sizeof(caller_buf) - 1, fp);
157 if (r > 0) {
158 char *s = caller_buf;
159 for (i = 0; i < r && *s; ) {
160 log_str("CMDLINE:", s);
161 while (i < r && *s)
162 i++, s++;
163 i++, s++;
164 }
165 }
166 fclose(fp);
Bill Richardson31d95c22014-08-24 22:07:17 -0700167 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800168
Bill Richardson31d95c22014-08-24 22:07:17 -0700169 /* Now log the stuff about ourselves */
170 for (i = 0; i < argc; i++)
Bill Richardsonee53d652014-09-04 16:18:15 -0700171 log_str(NULL, argv[i]);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800172
Bill Richardson31d95c22014-08-24 22:07:17 -0700173 log_close();
Bill Richardsonfeb25182013-03-07 12:54:29 -0800174}
175
Bill Richardsonfeb25182013-03-07 12:54:29 -0800176/******************************************************************************/
Bill Richardson779796f2014-09-23 11:47:40 -0700177
178static const char *const usage = "\n"
179"Usage: " MYNAME " COMMAND [args...]\n"
180"\n"
181"This is the unified firmware utility, which will eventually replace\n"
182"most of the distinct verified boot tools formerly produced by the\n"
183"vboot_reference package.\n"
184"\n"
185"When symlinked under the name of one of those previous tools, it should\n"
186"fully implement the original behavior. It can also be invoked directly\n"
187"as " MYNAME ", followed by the original name as the first argument.\n"
188"\n";
189
190static void print_help(const char *cmd)
191{
192 puts(usage);
193}
194
195static const struct futil_cmd_t *find_command(const char *name)
196{
197 const struct futil_cmd_t *const *cmd;
198
199 for (cmd = futil_cmds; *cmd; cmd++)
200 if (0 == strcmp((*cmd)->name, name))
201 return *cmd;
202
203 return NULL;
204}
205
206static void list_commands(void)
207{
208 const struct futil_cmd_t *const *cmd;
209
210 for (cmd = futil_cmds; *cmd; cmd++)
211 printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
212}
213
214static int do_help(int argc, char *argv[])
215{
216 const struct futil_cmd_t *cmd;
217
218 if (argc >= 2) {
219 cmd = find_command(argv[1]);
220 if (cmd) {
221 printf("\n%s - %s\n", argv[1], cmd->shorthelp);
222 cmd->longhelp(argv[1]);
223 return 0;
224 }
225 }
226
227 fputs(usage, stdout);
228
229 printf("The following commands are built-in:\n\n");
230 list_commands();
231 printf("\nUse \"" MYNAME " help COMMAND\" for more information.\n\n");
232
233 return 0;
234}
235
236DECLARE_FUTIL_COMMAND(help, do_help,
237 "Show a bit of help (you're looking at it)",
238 print_help);
239
240/*
241 * These are built-in functions that we'd like to abandon completely someday.
242 * TODO: If no one complains, get rid of them.
243 */
244static const char *const dep_cmds[] = {
245 "dev_sign_file",
246};
247
248static const char *const dep_usage = "\n"
249"The program \"%s\" is deprecated and may go away soon.\n"
250"\n"
251"If you feel this is in error, please open a bug at\n"
252"\n"
253" http://dev.chromium.org/for-testers/bug-reporting-guidelines\n"
254"\n"
255"In the meantime, you may continue to use the program by invoking it as\n"
256"\n" MYNAME " %s [...]\n"
257"\n";
258
Bill Richardson36386252014-10-14 20:58:41 -0700259static int deprecated(const char *depname)
Bill Richardson779796f2014-09-23 11:47:40 -0700260{
261 fprintf(stderr, dep_usage, depname, depname);
Bill Richardson36386252014-10-14 20:58:41 -0700262 return 1;
Bill Richardson779796f2014-09-23 11:47:40 -0700263}
264
Bill Richardson36386252014-10-14 20:58:41 -0700265int run_command(const struct futil_cmd_t *cmd, int argc, char *argv[])
266{
267 /* Handle the "CMD --help" case ourselves */
268 if (2 == argc && 0 == strcmp(argv[1], "--help")) {
269 char *fake_argv[] = {"help",
270 (char *)cmd->name,
271 NULL};
272 return do_help(2, fake_argv);
273 }
274
275 return cmd->handler(argc, argv);
276}
277
278static char *simple_basename(char *str)
279{
280 char *s = strrchr(str, '/');
281 if (s)
282 s++;
283 else
284 s = str;
285 return s;
286}
287
Bill Richardsonfeb25182013-03-07 12:54:29 -0800288/* Here we go */
Bill Richardsonfeb25182013-03-07 12:54:29 -0800289int main(int argc, char *argv[], char *envp[])
290{
Bill Richardson36386252014-10-14 20:58:41 -0700291 char *progname;
Bill Richardson779796f2014-09-23 11:47:40 -0700292 const struct futil_cmd_t *cmd;
Bill Richardson31d95c22014-08-24 22:07:17 -0700293 int i;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800294
Bill Richardson31d95c22014-08-24 22:07:17 -0700295 log_args(argc, argv);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800296
Bill Richardson31d95c22014-08-24 22:07:17 -0700297 /* How were we invoked? */
Bill Richardson36386252014-10-14 20:58:41 -0700298 progname = simple_basename(argv[0]);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800299
Bill Richardson36386252014-10-14 20:58:41 -0700300 /* See if the program name is a command we recognize */
Bill Richardson779796f2014-09-23 11:47:40 -0700301 cmd = find_command(progname);
302 if (cmd) {
Bill Richardson36386252014-10-14 20:58:41 -0700303 /* Block any deprecated functions invoked directly. */
304 for (i = 0; i < ARRAY_SIZE(dep_cmds); i++)
305 if (0 == strcmp(dep_cmds[i], progname))
306 return deprecated(progname);
307
308 return run_command(cmd, argc, argv);
Bill Richardson779796f2014-09-23 11:47:40 -0700309 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800310
Bill Richardson36386252014-10-14 20:58:41 -0700311 /* The program name means nothing, so we require an argument. */
312 if (argc < 2) {
Bill Richardson31d95c22014-08-24 22:07:17 -0700313 do_help(0, 0);
Bill Richardson36386252014-10-14 20:58:41 -0700314 return 1;
Bill Richardson31d95c22014-08-24 22:07:17 -0700315 }
Bill Richardsonfeb25182013-03-07 12:54:29 -0800316
Bill Richardson36386252014-10-14 20:58:41 -0700317 /* The first arg should be a command we recognize */
318 argc--;
319 argv++;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800320
Bill Richardson36386252014-10-14 20:58:41 -0700321 /* For reasons I've forgotten, treat /blah/blah/CMD the same as CMD */
322 progname = simple_basename(argv[0]);
323 /* Oh, and treat "--foo" the same as "foo" */
324 while (*progname == '-')
325 progname++;
326
327 /* Do we recognize the command? */
328 cmd = find_command(progname);
329 if (cmd)
330 return run_command(cmd, argc, argv);
331
332 /* Nope. We've no clue what we're being asked to do. */
333 do_help(0, 0);
Bill Richardson31d95c22014-08-24 22:07:17 -0700334 return 1;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800335}