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 */ 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 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 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 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 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 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 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 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 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 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 */ 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