blob: 59145c3ab128c1c05d98c310c48f570b5fee6497 [file] [log] [blame]
Bill Richardsonfeb25182013-03-07 12:54:29 -08001/*
2 * Copyright (c) 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
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
19#define MYNAME "futility"
Bill Richardson6db8c752013-04-05 13:30:43 -070020#define MYNAME_S MYNAME "_s"
Bill Richardsonfeb25182013-03-07 12:54:29 -080021#ifdef OLDDIR
22#define XSTR(A) STR(A)
23#define STR(A) #A
24#define SUBDIR XSTR(OLDDIR)
25#else
26#define SUBDIR "old_bins"
27#endif
28
29/* File to use for logging, if present */
30#define LOGFILE "/tmp/futility.log"
31
32/* Normally logging will only happen if the logfile already exists. Uncomment
33 * this to force log file creation (and thus logging) always. */
34/* #define FORCE_LOGGING_ON */
35
36/******************************************************************************/
37
38static const char * const usage= "\n\
39Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\
40\n\
41This is the unified firmware utility, which will eventually replace\n\
42all the distinct userspace tools formerly produced by the\n\
43vboot_reference package.\n\
44\n\
45When symlinked under the name of one of those previous tools, it can\n\
46do one of two things: either it will fully implement the original\n\
47behavior, or (until that functionality is complete) it will just exec\n\
48the original binary.\n\
49\n\
50In either case it can append some usage information to " LOGFILE "\n\
51to help improve coverage and correctness.\n\
52\n\
53If you invoke it directly instead of via a symlink, it requires one\n\
54argument, which is the name of the old binary to exec. That binary\n\
55must be located in a directory named \"" SUBDIR "\" underneath\n\
56the " MYNAME " executable.\n\
57\n";
58
Bill Richardson7d028c42014-07-12 10:46:56 -070059static int do_help(int argc, char *argv[])
Bill Richardsonfeb25182013-03-07 12:54:29 -080060{
Bill Richardson7d028c42014-07-12 10:46:56 -070061 struct futil_cmd_t **cmd;
Bill Richardsonfeb25182013-03-07 12:54:29 -080062 int i;
63
64 fputs(usage, stdout);
65
66 printf("The following commands are built-in:\n");
67
Bill Richardson7d028c42014-07-12 10:46:56 -070068 for (cmd = futil_cmds; *cmd; cmd++)
69 printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
Bill Richardsonfeb25182013-03-07 12:54:29 -080070 printf("\n");
71
Bill Richardson6db8c752013-04-05 13:30:43 -070072 if (argc) {
73 printf("FYI, you added these args that I'm ignoring:\n");
74 for (i = 0; i < argc; i++)
75 printf("argv[%d] = %s\n", i, argv[i]);
76 }
Bill Richardsonfeb25182013-03-07 12:54:29 -080077
78 return 0;
79}
Bill Richardson7d028c42014-07-12 10:46:56 -070080DECLARE_FUTIL_COMMAND(help, do_help, "show a bit of help");
Bill Richardsonfeb25182013-03-07 12:54:29 -080081
82
83/******************************************************************************/
84/* Logging stuff */
85
86static int log_fd = -1;
87
88/* Write the string and a newline. Silently give up on errors */
89static void log_str(char *str)
90{
91 int len, done, n;
92
93 if (log_fd < 0)
94 return;
95
96 if (!str)
97 str = "(NULL)";
98
99 len = strlen(str);
100 if (len == 0) {
101 str = "(EMPTY)";
102 len = strlen(str);
103 }
104
105 for (done = 0; done < len; done += n) {
106 n = write(log_fd, str + done, len - done);
107 if (n < 0)
108 return;
109 }
110
Simon Glass3401fdc2013-08-15 21:32:08 -0600111 if (write(log_fd, "\n", 1) < 0)
112 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800113}
114
115static void log_close(void)
116{
117 struct flock lock;
118
119 if (log_fd >= 0) {
120 memset(&lock, 0, sizeof(lock));
121 lock.l_type = F_UNLCK;
122 lock.l_whence = SEEK_SET;
123 if (fcntl(log_fd, F_SETLKW, &lock))
124 perror("Unable to unlock log file");
125
126 close(log_fd);
127 log_fd = -1;
128 }
129}
130
131static void log_open(void)
132{
133 struct flock lock;
134 int ret;
135
136#ifdef FORCE_LOGGING_ON
137 log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666);
138#else
139 log_fd = open(LOGFILE, O_WRONLY|O_APPEND);
140#endif
141 if (log_fd < 0) {
142
143 if (errno != EACCES)
144 return;
145
146 /* Permission problems should improve shortly ... */
147 sleep(1);
148 log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666);
149 if (log_fd < 0) /* Nope, they didn't */
150 return;
151 }
152
153 /* Let anyone have a turn */
154 fchmod(log_fd, 0666);
155
156 /* But only one at a time */
157 memset(&lock, 0, sizeof(lock));
158 lock.l_type = F_WRLCK;
159 lock.l_whence = SEEK_END;
160
161 ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
162 if (ret < 0)
163 log_close();
164}
165
166#define CALLER_PREFIX "CALLER:"
167static void log_args(int argc, char *argv[])
168{
169 int i;
170 ssize_t r;
171 pid_t parent;
172 char buf[80];
173 char str_caller[PATH_MAX + sizeof(CALLER_PREFIX)] = CALLER_PREFIX;
174 char *truename = str_caller + sizeof(CALLER_PREFIX) - 1;
175 /* Note: truename starts on the \0 from CALLER_PREFIX, so we can write
176 * PATH_MAX chars into truename and still append a \0 at the end. */
177
178 log_open();
179
180 /* delimiter */
181 log_str("##### HEY #####");
182
183 /* Can we tell who called us? */
184 parent = getppid();
185 snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
186 r = readlink(buf, truename, PATH_MAX);
187 if (r >= 0) {
188 truename[r] = '\0';
189 log_str(str_caller);
190 }
191
192 /* Now log the stuff about ourselves */
193 for (i = 0; i < argc; i++)
194 log_str(argv[i]);
195
196 log_close();
197}
198
199
200/******************************************************************************/
201/* Here we go */
202
Bill Richardsond2d08b22014-07-08 16:31:40 -0700203#ifdef COVERAGE
204void __gcov_flush(void);
205#endif
206
Bill Richardsonfeb25182013-03-07 12:54:29 -0800207int main(int argc, char *argv[], char *envp[])
208{
209 char *progname;
210 char truename[PATH_MAX];
211 char oldname[PATH_MAX];
212 char buf[80];
213 pid_t myproc;
214 ssize_t r;
215 char *s;
Bill Richardson7d028c42014-07-12 10:46:56 -0700216 struct futil_cmd_t **cmd;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800217
218 log_args(argc, argv);
219
220 /* How were we invoked? */
221 progname = strrchr(argv[0], '/');
222 if (progname)
223 progname++;
224 else
225 progname = argv[0];
226
227 /* Invoked directly by name */
Bill Richardson6db8c752013-04-05 13:30:43 -0700228 if (0 == strcmp(progname, MYNAME) || 0 == strcmp(progname, MYNAME_S)) {
Bill Richardsonfeb25182013-03-07 12:54:29 -0800229 if (argc < 2) { /* must have an argument */
Bill Richardson7d028c42014-07-12 10:46:56 -0700230 do_help(0, 0);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800231 exit(1);
232 }
233
234 /* We can just pass the rest along, then */
235 argc--;
236 argv++;
237
238 /* So now what name do we want to invoke? */
239 progname = strrchr(argv[0], '/');
240 if (progname)
241 progname++;
242 else
243 progname = argv[0];
244 }
245
246 /* See if it's asking for something we know how to do ourselves */
Bill Richardson7d028c42014-07-12 10:46:56 -0700247 for (cmd = futil_cmds; *cmd; cmd++)
248 if (0 == strcmp((*cmd)->name, progname))
249 return (*cmd)->handler(argc, argv);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800250
251 /* Nope, it must be wrapped */
252
253 /* The old binaries live under the true executable. Find out where that is. */
254 myproc = getpid();
255 snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc);
256 r = readlink(buf, truename, PATH_MAX - 1);
257 if (r < 0) {
258 fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0],
259 buf, strerror(errno));
260 exit(1);
261 } else if (r == PATH_MAX - 1) {
262 /* Yes, it might _just_ fit, but we'll count that as wrong anyway. We can't
263 * determine the right size using the example in the readlink manpage,
264 * because the /proc symlink returns an st_size of 0. */
265 fprintf(stderr, "%s is too long: %s => %s\n", MYNAME, argv[0], buf);
266 exit(1);
267 }
268
269 truename[r] = '\0';
270 s = strrchr(truename, '/'); /* Find the true directory */
271 if (s) {
272 *s = '\0';
273 } else { /* I don't think this can happen */
274 fprintf(stderr, "%s says %s doesn't make sense\n", MYNAME, truename);
275 exit(1);
276 }
277 /* If the old binary path doesn't fit, just give up. */
278 r = snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname);
279 if (r >= PATH_MAX) {
280 fprintf(stderr, "%s/%s/%s is too long\n", truename, SUBDIR, progname);
281 exit(1);
282 }
283
284 fflush(0);
Bill Richardsond2d08b22014-07-08 16:31:40 -0700285#ifdef COVERAGE
286 /* Write gcov data prior to exec. */
287 __gcov_flush();
288#endif
Bill Richardsonfeb25182013-03-07 12:54:29 -0800289 execve(oldname, argv, envp);
290
291 fprintf(stderr, "%s failed to exec %s: %s\n", MYNAME,
292 oldname, strerror(errno));
293 return 1;
294}