blob: 5e852c3381fa72b67330649208c3653a6e3a1b3d [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
Bill Richardsone1550442014-07-17 11:32:17 -070082/* Deprecated functions can't be invoked through symlinks. */
83static char *dep_cmds[] = {
84 "eficompress",
85 "efidecompress",
86};
87
88static const char * const dep_usage= "\n\
89The program \"%s\" is deprecated.\n\
90\n\
91If you feel this is in error, please open a bug at\n\
92\n\
93 http://dev.chromium.org/for-testers/bug-reporting-guidelines\n\
94\n\
95In the meantime, you may continue to use the program by invoking it as\n\
96\n\
97 " MYNAME " %s [...]\n\
98\n";
99
100static void deprecated(const char *depname)
101{
102 fprintf(stderr, dep_usage, depname, depname);
103 exit(1);
104}
Bill Richardsonfeb25182013-03-07 12:54:29 -0800105
106/******************************************************************************/
107/* Logging stuff */
108
109static int log_fd = -1;
110
111/* Write the string and a newline. Silently give up on errors */
112static void log_str(char *str)
113{
114 int len, done, n;
115
116 if (log_fd < 0)
117 return;
118
119 if (!str)
120 str = "(NULL)";
121
122 len = strlen(str);
123 if (len == 0) {
124 str = "(EMPTY)";
125 len = strlen(str);
126 }
127
128 for (done = 0; done < len; done += n) {
129 n = write(log_fd, str + done, len - done);
130 if (n < 0)
131 return;
132 }
133
Simon Glass3401fdc2013-08-15 21:32:08 -0600134 if (write(log_fd, "\n", 1) < 0)
135 return;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800136}
137
138static void log_close(void)
139{
140 struct flock lock;
141
142 if (log_fd >= 0) {
143 memset(&lock, 0, sizeof(lock));
144 lock.l_type = F_UNLCK;
145 lock.l_whence = SEEK_SET;
146 if (fcntl(log_fd, F_SETLKW, &lock))
147 perror("Unable to unlock log file");
148
149 close(log_fd);
150 log_fd = -1;
151 }
152}
153
154static void log_open(void)
155{
156 struct flock lock;
157 int ret;
158
159#ifdef FORCE_LOGGING_ON
160 log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666);
161#else
162 log_fd = open(LOGFILE, O_WRONLY|O_APPEND);
163#endif
164 if (log_fd < 0) {
165
166 if (errno != EACCES)
167 return;
168
169 /* Permission problems should improve shortly ... */
170 sleep(1);
171 log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666);
172 if (log_fd < 0) /* Nope, they didn't */
173 return;
174 }
175
176 /* Let anyone have a turn */
177 fchmod(log_fd, 0666);
178
179 /* But only one at a time */
180 memset(&lock, 0, sizeof(lock));
181 lock.l_type = F_WRLCK;
182 lock.l_whence = SEEK_END;
183
184 ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */
185 if (ret < 0)
186 log_close();
187}
188
189#define CALLER_PREFIX "CALLER:"
190static void log_args(int argc, char *argv[])
191{
192 int i;
193 ssize_t r;
194 pid_t parent;
195 char buf[80];
196 char str_caller[PATH_MAX + sizeof(CALLER_PREFIX)] = CALLER_PREFIX;
197 char *truename = str_caller + sizeof(CALLER_PREFIX) - 1;
198 /* Note: truename starts on the \0 from CALLER_PREFIX, so we can write
199 * PATH_MAX chars into truename and still append a \0 at the end. */
200
201 log_open();
202
203 /* delimiter */
204 log_str("##### HEY #####");
205
206 /* Can we tell who called us? */
207 parent = getppid();
208 snprintf(buf, sizeof(buf), "/proc/%d/exe", parent);
209 r = readlink(buf, truename, PATH_MAX);
210 if (r >= 0) {
211 truename[r] = '\0';
212 log_str(str_caller);
213 }
214
215 /* Now log the stuff about ourselves */
216 for (i = 0; i < argc; i++)
217 log_str(argv[i]);
218
219 log_close();
220}
221
222
223/******************************************************************************/
224/* Here we go */
225
Bill Richardsond2d08b22014-07-08 16:31:40 -0700226#ifdef COVERAGE
227void __gcov_flush(void);
228#endif
229
Bill Richardsonfeb25182013-03-07 12:54:29 -0800230int main(int argc, char *argv[], char *envp[])
231{
232 char *progname;
233 char truename[PATH_MAX];
234 char oldname[PATH_MAX];
235 char buf[80];
236 pid_t myproc;
237 ssize_t r;
238 char *s;
Bill Richardson7d028c42014-07-12 10:46:56 -0700239 struct futil_cmd_t **cmd;
Bill Richardsone1550442014-07-17 11:32:17 -0700240 int i;
Bill Richardsonfeb25182013-03-07 12:54:29 -0800241
242 log_args(argc, argv);
243
244 /* How were we invoked? */
245 progname = strrchr(argv[0], '/');
246 if (progname)
247 progname++;
248 else
249 progname = argv[0];
250
251 /* Invoked directly by name */
Bill Richardson6db8c752013-04-05 13:30:43 -0700252 if (0 == strcmp(progname, MYNAME) || 0 == strcmp(progname, MYNAME_S)) {
Bill Richardsonfeb25182013-03-07 12:54:29 -0800253 if (argc < 2) { /* must have an argument */
Bill Richardson7d028c42014-07-12 10:46:56 -0700254 do_help(0, 0);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800255 exit(1);
256 }
257
258 /* We can just pass the rest along, then */
259 argc--;
260 argv++;
261
262 /* So now what name do we want to invoke? */
263 progname = strrchr(argv[0], '/');
264 if (progname)
265 progname++;
266 else
267 progname = argv[0];
Bill Richardsone1550442014-07-17 11:32:17 -0700268 } else { /* Invoked by symlink */
269
270 /* Block any deprecated functions. */
271 for (i = 0; i < ARRAY_SIZE(dep_cmds); i++)
272 if (0 == strcmp(dep_cmds[i], progname))
273 deprecated(progname);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800274 }
275
276 /* See if it's asking for something we know how to do ourselves */
Bill Richardson7d028c42014-07-12 10:46:56 -0700277 for (cmd = futil_cmds; *cmd; cmd++)
278 if (0 == strcmp((*cmd)->name, progname))
279 return (*cmd)->handler(argc, argv);
Bill Richardsonfeb25182013-03-07 12:54:29 -0800280
281 /* Nope, it must be wrapped */
282
283 /* The old binaries live under the true executable. Find out where that is. */
284 myproc = getpid();
285 snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc);
286 r = readlink(buf, truename, PATH_MAX - 1);
287 if (r < 0) {
288 fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0],
289 buf, strerror(errno));
290 exit(1);
291 } else if (r == PATH_MAX - 1) {
292 /* Yes, it might _just_ fit, but we'll count that as wrong anyway. We can't
293 * determine the right size using the example in the readlink manpage,
294 * because the /proc symlink returns an st_size of 0. */
295 fprintf(stderr, "%s is too long: %s => %s\n", MYNAME, argv[0], buf);
296 exit(1);
297 }
298
299 truename[r] = '\0';
300 s = strrchr(truename, '/'); /* Find the true directory */
301 if (s) {
302 *s = '\0';
303 } else { /* I don't think this can happen */
304 fprintf(stderr, "%s says %s doesn't make sense\n", MYNAME, truename);
305 exit(1);
306 }
307 /* If the old binary path doesn't fit, just give up. */
308 r = snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname);
309 if (r >= PATH_MAX) {
310 fprintf(stderr, "%s/%s/%s is too long\n", truename, SUBDIR, progname);
311 exit(1);
312 }
313
314 fflush(0);
Bill Richardsond2d08b22014-07-08 16:31:40 -0700315#ifdef COVERAGE
316 /* Write gcov data prior to exec. */
317 __gcov_flush();
318#endif
Bill Richardsonfeb25182013-03-07 12:54:29 -0800319 execve(oldname, argv, envp);
320
321 fprintf(stderr, "%s failed to exec %s: %s\n", MYNAME,
322 oldname, strerror(errno));
323 return 1;
324}