• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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