diff options
Diffstat (limited to 'tools/static-nodes.c')
-rw-r--r-- | tools/static-nodes.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/tools/static-nodes.c b/tools/static-nodes.c new file mode 100644 index 0000000..5ef3743 --- /dev/null +++ b/tools/static-nodes.c @@ -0,0 +1,277 @@ +/* + * kmod-static-nodes - manage modules.devname + * + * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> + * Copyright (C) 2011-2013 ProFUSION embedded systems + * Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#include <shared/util.h> + +#include "kmod.h" + +struct static_nodes_format { + const char *name; + int (*write)(FILE *, char[], char[], char, unsigned int, unsigned int); + const char *description; +}; + +static const struct static_nodes_format static_nodes_format_human; +static const struct static_nodes_format static_nodes_format_tmpfiles; +static const struct static_nodes_format static_nodes_format_devname; + +static const struct static_nodes_format *static_nodes_formats[] = { + &static_nodes_format_human, + &static_nodes_format_tmpfiles, + &static_nodes_format_devname, +}; + +static const char cmdopts_s[] = "o:f:h"; +static const struct option cmdopts[] = { + { "output", required_argument, 0, 'o'}, + { "format", required_argument, 0, 'f'}, + { "help", no_argument, 0, 'h'}, + { }, +}; + +static int write_human(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) +{ + int ret; + + ret = fprintf(out, + "Module: %s\n" + "\tDevice node: /dev/%s\n" + "\t\tType: %s device\n" + "\t\tMajor: %u\n" + "\t\tMinor: %u\n", + modname, devname, + (type == 'c') ? "character" : "block", maj, min); + if (ret >= 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static const struct static_nodes_format static_nodes_format_human = { + .name = "human", + .write = write_human, + .description = "(default) a human readable format. Do not parse.", +}; + +static int write_tmpfiles(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) +{ + const char *dir; + int ret; + + dir = strrchr(devname, '/'); + if (dir) { + ret = fprintf(out, "d /dev/%.*s 0755 - - -\n", + (int)(dir - devname), devname); + if (ret < 0) + return EXIT_FAILURE; + } + + ret = fprintf(out, "%c! /dev/%s 0600 - - - %u:%u\n", + type, devname, maj, min); + if (ret < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static const struct static_nodes_format static_nodes_format_tmpfiles = { + .name = "tmpfiles", + .write = write_tmpfiles, + .description = "the tmpfiles.d(5) format used by systemd-tmpfiles.", +}; + +static int write_devname(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) +{ + int ret; + + ret = fprintf(out, "%s %s %c%u:%u\n", modname, devname, type, maj, min); + if (ret >= 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} + +static const struct static_nodes_format static_nodes_format_devname = { + .name = "devname", + .write = write_devname, + .description = "the modules.devname format.", +}; + +static void help(void) +{ + size_t i; + + printf("Usage:\n" + "\t%s static-nodes [options]\n" + "\n" + "kmod static-nodes outputs the static-node information of the currently running kernel.\n" + "\n" + "Options:\n" + "\t-f, --format=FORMAT choose format to use: see \"Formats\"\n" + "\t-o, --output=FILE write output to file\n" + "\t-h, --help show this help\n" + "\n" + "Formats:\n", + program_invocation_short_name); + + for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) { + if (static_nodes_formats[i]->description != NULL) { + printf("\t%-12s %s\n", static_nodes_formats[i]->name, + static_nodes_formats[i]->description); + } + } +} + +static int do_static_nodes(int argc, char *argv[]) +{ + struct utsname kernel; + char modules[PATH_MAX], buf[4096]; + const char *output = "/dev/stdout"; + FILE *in = NULL, *out = NULL; + const struct static_nodes_format *format = &static_nodes_format_human; + int r, ret = EXIT_SUCCESS; + + for (;;) { + int c, idx = 0, valid; + size_t i; + + c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx); + if (c == -1) { + break; + } + switch (c) { + case 'o': + output = optarg; + break; + case 'f': + valid = 0; + + for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) { + if (streq(static_nodes_formats[i]->name, optarg)) { + format = static_nodes_formats[i]; + valid = 1; + } + } + + if (!valid) { + fprintf(stderr, "Unknown format: '%s'.\n", + optarg); + help(); + ret = EXIT_FAILURE; + goto finish; + } + break; + case 'h': + help(); + goto finish; + case '?': + ret = EXIT_FAILURE; + goto finish; + default: + fprintf(stderr, "Unexpected commandline option '%c'.\n", + c); + help(); + ret = EXIT_FAILURE; + goto finish; + } + } + + if (uname(&kernel) < 0) { + fputs("Error: uname failed!\n", stderr); + ret = EXIT_FAILURE; + goto finish; + } + + snprintf(modules, sizeof(modules), MODULE_DIRECTORY "/%s/modules.devname", kernel.release); + in = fopen(modules, "re"); + if (in == NULL) { + if (errno == ENOENT) { + fprintf(stderr, "Warning: " MODULE_DIRECTORY "/%s/modules.devname not found - ignoring\n", + kernel.release); + ret = EXIT_SUCCESS; + } else { + fprintf(stderr, "Error: could not open " MODULE_DIRECTORY "/%s/modules.devname - %m\n", + kernel.release); + ret = EXIT_FAILURE; + } + goto finish; + } + + r = mkdir_parents(output, 0755); + if (r < 0) { + fprintf(stderr, "Error: could not create parent directory for %s - %m.\n", output); + ret = EXIT_FAILURE; + goto finish; + } + + out = fopen(output, "we"); + if (out == NULL) { + fprintf(stderr, "Error: could not create %s - %m\n", output); + ret = EXIT_FAILURE; + goto finish; + } + + while (fgets(buf, sizeof(buf), in) != NULL) { + char modname[PATH_MAX]; + char devname[PATH_MAX]; + char type; + unsigned int maj, min; + int matches; + + if (buf[0] == '#') + continue; + + matches = sscanf(buf, "%s %s %c%u:%u", modname, devname, + &type, &maj, &min); + if (matches != 5 || (type != 'c' && type != 'b')) { + fprintf(stderr, "Error: invalid devname entry: %s", buf); + ret = EXIT_FAILURE; + continue; + } + + format->write(out, modname, devname, type, maj, min); + } + +finish: + if (in) + fclose(in); + if (out) + fclose(out); + return ret; +} + +const struct kmod_cmd kmod_cmd_static_nodes = { + .name = "static-nodes", + .cmd = do_static_nodes, + .help = "outputs the static-node information installed with the currently running kernel", +}; |