1 /*
2 * Copyright 2013 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <limits.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17
18 #include "futility.h"
19
20
21 /******************************************************************************/
22 /* Logging stuff */
23
24 /* File to use for logging, if present */
25 #define LOGFILE "/tmp/futility.log"
26
27 /* Normally logging will only happen if the logfile already exists. Uncomment
28 * this to force log file creation (and thus logging) always. */
29
30 /* #define FORCE_LOGGING_ON */
31
32 static int log_fd = -1;
33
34 /* Write the string and a newline. Silently give up on errors */
log_str(char * prefix,char * str)35 static void log_str(char *prefix, char *str)
36 {
37 int len, done, n;
38
39 if (log_fd < 0)
40 return;
41
42 if (!str)
43 str = "(NULL)";
44
45 if (prefix && *prefix) {
46 len = strlen(prefix);
47 for (done = 0; done < len; done += n) {
48 n = write(log_fd, prefix + done, len - done);
49 if (n < 0)
50 return;
51 }
52 }
53
54 len = strlen(str);
55 if (len == 0) {
56 str = "(EMPTY)";
57 len = strlen(str);
58 }
59
60 for (done = 0; done < len; done += n) {
61 n = write(log_fd, str + done, len - done);
62 if (n < 0)
63 return;
64 }
65
66 if (write(log_fd, "\n", 1) < 0)
67 return;
68 }
69
log_close(void)70 static void log_close(void)
71 {
72 struct flock lock;
73
74 if (log_fd >= 0) {
75 memset(&lock, 0, sizeof(lock));
76 lock.l_type = F_UNLCK;
77 lock.l_whence = SEEK_SET;
78 if (fcntl(log_fd, F_SETLKW, &lock))
79 perror("Unable to unlock log file");
80
81 close(log_fd);
82 log_fd = -1;
83 }
84 }
85
log_open(void)86 static void log_open(void)
87 {
88 struct flock lock;
89 int ret;
90
91 #ifdef FORCE_LOGGING_ON
92 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
93 #else
94 log_fd = open(LOGFILE, O_WRONLY | O_APPEND);
95 #endif
96 if (log_fd < 0) {
97
98 if (errno != EACCES)
99 return;
100
101 /* Permission problems should improve shortly ... */
102 sleep(1);
103 log_fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0666);
104 if (log_fd < 0) /* Nope, they didn't */
105 return;
106 }
107
108 /* Let anyone have a turn */
109 fchmod(log_fd, 0666);
110
111 /* But only one at a time */
112 memset(&lock, 0, sizeof(lock));
113 lock.l_type = F_WRLCK;
114 lock.l_whence = SEEK_END;
115
116 ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
117 if (ret < 0)
118 log_close();
119 }
120
log_args(int argc,char * argv[])121 static void log_args(int argc, char *argv[])
122 {
123 int i;
124 ssize_t r;
125 pid_t parent;
126 char buf[80];
127 FILE *fp;
128 char caller_buf[PATH_MAX];
129
130 log_open();
131
132 /* delimiter */
133 log_str(NULL, "##### LOG #####");
134
135 /* Can we tell who called us? */
136 parent = getppid();
137 snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
138 r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
139 if (r >= 0) {
140 caller_buf[r] = '\0';
141 log_str("CALLER:", caller_buf);
142 }
143
144 /* From where? */
145 snprintf(buf, sizeof(buf), "/proc/%d/cwd", parent);
146 r = readlink(buf, caller_buf, sizeof(caller_buf) - 1);
147 if (r >= 0) {
148 caller_buf[r] = '\0';
149 log_str("DIR:", caller_buf);
150 }
151
152 /* And maybe the args? */
153 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", parent);
154 fp = fopen(buf, "r");
155 if (fp) {
156 memset(caller_buf, 0, sizeof(caller_buf));
157 r = fread(caller_buf, 1, sizeof(caller_buf) - 1, fp);
158 if (r > 0) {
159 char *s = caller_buf;
160 for (i = 0; i < r && *s; ) {
161 log_str("CMDLINE:", s);
162 while (i < r && *s)
163 i++, s++;
164 i++, s++;
165 }
166 }
167 fclose(fp);
168 }
169
170 /* Now log the stuff about ourselves */
171 for (i = 0; i < argc; i++)
172 log_str(NULL, argv[i]);
173
174 log_close();
175 }
176
177 /******************************************************************************/
178
179 /* Default is to support everything we can */
180 enum vboot_version vboot_version = VBOOT_VERSION_ALL;
181
182 static const char *const usage = "\n"
183 "Usage: " MYNAME " [options] COMMAND [args...]\n"
184 "\n"
185 "This is the unified firmware utility, which will eventually replace\n"
186 "most of the distinct verified boot tools formerly produced by the\n"
187 "vboot_reference package.\n"
188 "\n"
189 "When symlinked under the name of one of those previous tools, it should\n"
190 "fully implement the original behavior. It can also be invoked directly\n"
191 "as " MYNAME ", followed by the original name as the first argument.\n"
192 "\n";
193
194 static const char *const options =
195 "Global options:\n"
196 "\n"
197 " --vb1 Use only vboot v1.0 binary formats\n"
198 " --vb21 Use only vboot v2.1 binary formats\n"
199 "\n";
200
find_command(const char * name)201 static const struct futil_cmd_t *find_command(const char *name)
202 {
203 const struct futil_cmd_t *const *cmd;
204
205 for (cmd = futil_cmds; *cmd; cmd++)
206 if (0 == strcmp((*cmd)->name, name))
207 return *cmd;
208
209 return NULL;
210 }
211
list_commands(void)212 static void list_commands(void)
213 {
214 const struct futil_cmd_t *const *cmd;
215
216 for (cmd = futil_cmds; *cmd; cmd++)
217 if (vboot_version & (*cmd)->version)
218 printf(" %-20s %s\n",
219 (*cmd)->name, (*cmd)->shorthelp);
220 }
221
do_help(int argc,char * argv[])222 static int do_help(int argc, char *argv[])
223 {
224 const struct futil_cmd_t *cmd;
225 const char *vstr;
226
227 if (argc >= 2) {
228 cmd = find_command(argv[1]);
229 if (cmd) {
230 printf("\n%s - %s\n", argv[1], cmd->shorthelp);
231 if (cmd->longhelp)
232 cmd->longhelp(argv[1]);
233 return 0;
234 }
235 }
236
237 fputs(usage, stdout);
238
239 if (vboot_version == VBOOT_VERSION_ALL)
240 fputs(options, stdout);
241
242 switch (vboot_version) {
243 case VBOOT_VERSION_1_0:
244 vstr = "version 1.0 ";
245 break;
246 case VBOOT_VERSION_2_1:
247 vstr = "version 2.1 ";
248 break;
249 case VBOOT_VERSION_ALL:
250 vstr = "";
251 break;
252 }
253 printf("The following %scommands are built-in:\n\n", vstr);
254 list_commands();
255 printf("\nUse \"" MYNAME " help COMMAND\" for more information.\n\n");
256
257 return 0;
258 }
259
260 DECLARE_FUTIL_COMMAND(help, do_help, VBOOT_VERSION_ALL,
261 "Show a bit of help (you're looking at it)",
262 NULL);
263
do_version(int argc,char * argv[])264 static int do_version(int argc, char *argv[])
265 {
266 printf("%s\n", futility_version);
267 return 0;
268 }
269
270 DECLARE_FUTIL_COMMAND(version, do_version, VBOOT_VERSION_ALL,
271 "Show the futility source revision and build date",
272 NULL);
273
run_command(const struct futil_cmd_t * cmd,int argc,char * argv[])274 int run_command(const struct futil_cmd_t *cmd, int argc, char *argv[])
275 {
276 /* Handle the "CMD --help" case ourselves */
277 if (2 == argc && 0 == strcmp(argv[1], "--help")) {
278 char *fake_argv[] = {"help",
279 (char *)cmd->name,
280 NULL};
281 return do_help(2, fake_argv);
282 }
283
284 return cmd->handler(argc, argv);
285 }
286
simple_basename(char * str)287 static char *simple_basename(char *str)
288 {
289 char *s = strrchr(str, '/');
290 if (s)
291 s++;
292 else
293 s = str;
294 return s;
295 }
296
297 /* Here we go */
main(int argc,char * argv[],char * envp[])298 int main(int argc, char *argv[], char *envp[])
299 {
300 char *progname;
301 const struct futil_cmd_t *cmd;
302 int i, errorcnt = 0;
303 int vb_ver = VBOOT_VERSION_ALL;
304 struct option long_opts[] = {
305 {"vb1" , 0, &vb_ver, VBOOT_VERSION_1_0},
306 {"vb21", 0, &vb_ver, VBOOT_VERSION_2_1},
307 { 0, 0, 0, 0},
308 };
309
310 log_args(argc, argv);
311
312 /* How were we invoked? */
313 progname = simple_basename(argv[0]);
314
315 /* See if the program name is a command we recognize */
316 cmd = find_command(progname);
317 if (cmd)
318 /* Yep, just do that */
319 return run_command(cmd, argc, argv);
320
321 /* Parse the global options, stopping at the first non-option. */
322 opterr = 0; /* quiet, you. */
323 while ((i = getopt_long(argc, argv, "+:", long_opts, NULL)) != -1) {
324 switch (i) {
325 case '?':
326 if (optopt)
327 fprintf(stderr, "Unrecognized option: -%c\n",
328 optopt);
329 else
330 fprintf(stderr, "Unrecognized option: %s\n",
331 argv[optind - 1]);
332 errorcnt++;
333 break;
334 case ':':
335 fprintf(stderr, "Missing argument to -%c\n", optopt);
336 errorcnt++;
337 break;
338 case 0: /* handled option */
339 break;
340 default:
341 Debug("i=%d\n", i);
342 DIE;
343 }
344 }
345 vboot_version = vb_ver;
346
347 /* Reset the getopt state so commands can parse their own options. */
348 argc -= optind;
349 argv += optind;
350 optind = 0;
351
352 /* We require a command name. */
353 if (errorcnt || argc < 1) {
354 do_help(0, 0);
355 return 1;
356 }
357
358 /* For reasons I've forgotten, treat /blah/blah/CMD the same as CMD */
359 progname = simple_basename(argv[0]);
360
361 /* Do we recognize the command? */
362 cmd = find_command(progname);
363 if (cmd)
364 return run_command(cmd, argc, argv);
365
366 /* Nope. We've no clue what we're being asked to do. */
367 do_help(0, 0);
368 return 1;
369 }
370