|
1 | 1 | #define _GNU_SOURCE |
2 | 2 | #include <errno.h> |
3 | 3 | #include <fcntl.h> |
| 4 | +#include <ftw.h> |
4 | 5 | #include <getopt.h> |
| 6 | +#include <libkmod.h> |
5 | 7 | #include <net/if.h> |
6 | 8 | #include <netinet/ip.h> |
7 | 9 | #include <signal.h> |
|
14 | 16 | #include <sys/stat.h> |
15 | 17 | #include <sys/sysmacros.h> |
16 | 18 | #include <sys/types.h> |
| 19 | +#include <sys/utsname.h> |
17 | 20 | #include <sys/wait.h> |
18 | 21 | #include <unistd.h> |
19 | 22 | #include "../vsockexec/vsock.h" |
@@ -57,15 +60,20 @@ static int opentcp(unsigned short port) |
57 | 60 | #define RNDADDENTROPY _IOW( 'R', 0x03, int [2] ) |
58 | 61 |
|
59 | 62 | #define DEFAULT_PATH_ENV "PATH=/sbin:/usr/sbin:/bin:/usr/bin" |
| 63 | +#define OPEN_FDS 15 |
60 | 64 |
|
61 | 65 | const char *const default_envp[] = { |
62 | 66 | DEFAULT_PATH_ENV, |
63 | 67 | NULL, |
64 | 68 | }; |
65 | 69 |
|
| 70 | +// global kmod k_ctx so we can access it in the file tree traversal |
| 71 | +struct kmod_ctx *k_ctx; |
| 72 | + |
66 | 73 | // When nothing is passed, default to the LCOWv1 behavior. |
67 | 74 | const char *const default_argv[] = { "/bin/gcs", "-loglevel", "debug", "-logfile=/run/gcs/gcs.log" }; |
68 | 75 | const char *const default_shell = "/bin/sh"; |
| 76 | +const char *const lib_modules = "/lib/modules"; |
69 | 77 |
|
70 | 78 | struct Mount { |
71 | 79 | const char *source, *target, *type; |
@@ -403,6 +411,110 @@ int reap_until(pid_t until_pid) { |
403 | 411 | } |
404 | 412 | } |
405 | 413 |
|
| 414 | +// load_module gets the module from the absolute path to the module and then |
| 415 | +// inserts into the kernel. |
| 416 | +int load_module(struct kmod_ctx *ctx, const char *module_path) { |
| 417 | + struct kmod_module *mod = NULL; |
| 418 | + int err; |
| 419 | + |
| 420 | + #ifdef DEBUG |
| 421 | + printf("loading module: %s\n", module_path); |
| 422 | + #endif |
| 423 | + |
| 424 | + err = kmod_module_new_from_path(ctx, module_path, &mod); |
| 425 | + if (err < 0) { |
| 426 | + return err; |
| 427 | + } |
| 428 | + |
| 429 | + err = kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL); |
| 430 | + if (err < 0) { |
| 431 | + kmod_module_unref(mod); |
| 432 | + return err; |
| 433 | + } |
| 434 | + |
| 435 | + kmod_module_unref(mod); |
| 436 | + return 0; |
| 437 | +} |
| 438 | + |
| 439 | +// parse_tree_entry is called by ftw for each directory and file in the file tree. |
| 440 | +// If this entry is a file and has a .ko file extension, attempt to load into kernel. |
| 441 | +int parse_tree_entry(const char *fpath, const struct stat *sb, int typeflag) { |
| 442 | + int result; |
| 443 | + const char *ext; |
| 444 | + |
| 445 | + if (typeflag != FTW_F) { |
| 446 | + // do nothing if this isn't a file |
| 447 | + return 0; |
| 448 | + } |
| 449 | + |
| 450 | + ext = strrchr(fpath, '.'); |
| 451 | + if (!ext || ext == fpath) { |
| 452 | + // no file extension found in the filepath |
| 453 | + return 0; |
| 454 | + } |
| 455 | + |
| 456 | + if ((result = strcmp(ext, ".ko")) != 0) { |
| 457 | + // file does not have .ko extension so it is not a kernel module |
| 458 | + return 0; |
| 459 | + } |
| 460 | + |
| 461 | + // print warning if we fail to load the module, but don't fail fn so |
| 462 | + // we keep trying to load the rest of the modules. |
| 463 | + result = load_module(k_ctx, fpath); |
| 464 | + if (result != 0) { |
| 465 | + warn2("failed to load module", fpath); |
| 466 | + } |
| 467 | + return 0; |
| 468 | +} |
| 469 | + |
| 470 | +// load_all_modules finds the modules in the image and loads them using kmod, |
| 471 | +// which accounts for ordering requirements. |
| 472 | +void load_all_modules() { |
| 473 | + int max_path = 256; |
| 474 | + char modules_dir[max_path]; |
| 475 | + struct utsname uname_data; |
| 476 | + int ret; |
| 477 | + |
| 478 | + // get information on the running kernel |
| 479 | + ret = uname(&uname_data); |
| 480 | + if (ret != 0) { |
| 481 | + die("failed to get kernel information"); |
| 482 | + } |
| 483 | + |
| 484 | + // create the absolute path of the modules directory this looks |
| 485 | + // like /lib/modules/<uname.release> |
| 486 | + ret = snprintf(modules_dir, max_path, "%s/%s", lib_modules, uname_data.release); |
| 487 | + if (ret < 0) { |
| 488 | + die("failed to create the modules directory path"); |
| 489 | + } else if (ret > max_path) { |
| 490 | + die("modules directory buffer larger than expected"); |
| 491 | + } |
| 492 | + |
| 493 | + if (k_ctx == NULL) { |
| 494 | + k_ctx = kmod_new(NULL, NULL); |
| 495 | + if (k_ctx == NULL) { |
| 496 | + die("failed to create kmod context"); |
| 497 | + } |
| 498 | + } |
| 499 | + |
| 500 | + kmod_load_resources(k_ctx); |
| 501 | + ret = ftw(modules_dir, parse_tree_entry, OPEN_FDS); |
| 502 | + if (ret < 0) { |
| 503 | + kmod_unref(k_ctx); |
| 504 | + die("failed to load kmod resources"); |
| 505 | + } else if (ret != 0) { |
| 506 | + // Don't fail on error from walking the file tree and loading modules right now. |
| 507 | + // ftw may return an error if the modules directory doesn't exist, which |
| 508 | + // may be the case for some images. Additionally, we don't currently support |
| 509 | + // using a denylist when loading modules, so we may try to load modules |
| 510 | + // that cannot be loaded until later, such as nvidia modules which fail to |
| 511 | + // load if no device is present. |
| 512 | + warn("error adding modules"); |
| 513 | + } |
| 514 | + |
| 515 | + kmod_unref(k_ctx); |
| 516 | +} |
| 517 | + |
406 | 518 | #ifdef DEBUG |
407 | 519 | int debug_main(int argc, char **argv) { |
408 | 520 | unsigned int ports[3] = {2056, 2056, 2056}; |
@@ -533,6 +645,11 @@ int main(int argc, char **argv) { |
533 | 645 | init_entropy(entropy_port); |
534 | 646 | } |
535 | 647 |
|
| 648 | + #ifdef DEBUG |
| 649 | + printf("loading modules\n"); |
| 650 | + #endif |
| 651 | + load_all_modules(); |
| 652 | + |
536 | 653 | pid_t pid = launch(child_argc, child_argv); |
537 | 654 | if (debug_shell != NULL) { |
538 | 655 | // The debug shell takes over as the primary child. |
|
0 commit comments