1 /* 2 * kmod-static-nodes - manage modules.devname 3 * 4 * Copyright (C) 2004-2012 Kay Sievers <kay (at) vrfy.org> 5 * Copyright (C) 2011-2013 ProFUSION embedded systems 6 * Copyright (C) 2013 Tom Gundersen <teg (at) jklm.no> 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include <errno.h> 23 #include <getopt.h> 24 #include <limits.h> 25 #include <stddef.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <sys/stat.h> 31 #include <sys/types.h> 32 #include <sys/utsname.h> 33 34 #include <shared/util.h> 35 36 #include "kmod.h" 37 38 struct static_nodes_format { 39 const char *name; 40 int (*write)(FILE *, char[], char[], char, unsigned int, unsigned int); 41 const char *description; 42 }; 43 44 static const struct static_nodes_format static_nodes_format_human; 45 static const struct static_nodes_format static_nodes_format_tmpfiles; 46 static const struct static_nodes_format static_nodes_format_devname; 47 48 static const struct static_nodes_format *static_nodes_formats[] = { 49 &static_nodes_format_human, 50 &static_nodes_format_tmpfiles, 51 &static_nodes_format_devname, 52 }; 53 54 static const char cmdopts_s[] = "o:f:h"; 55 static const struct option cmdopts[] = { 56 { "output", required_argument, 0, 'o'}, 57 { "format", required_argument, 0, 'f'}, 58 { "help", no_argument, 0, 'h'}, 59 { }, 60 }; 61 62 static int write_human(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) 63 { 64 int ret; 65 66 ret = fprintf(out, 67 "Module: %s\n" 68 "\tDevice node: /dev/%s\n" 69 "\t\tType: %s device\n" 70 "\t\tMajor: %u\n" 71 "\t\tMinor: %u\n", 72 modname, devname, 73 (type == 'c') ? "character" : "block", maj, min); 74 if (ret >= 0) 75 return EXIT_SUCCESS; 76 else 77 return EXIT_FAILURE; 78 } 79 80 static const struct static_nodes_format static_nodes_format_human = { 81 .name = "human", 82 .write = write_human, 83 .description = "(default) a human readable format. Do not parse.", 84 }; 85 86 static int write_tmpfiles(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) 87 { 88 const char *dir; 89 int ret; 90 91 dir = strrchr(devname, '/'); 92 if (dir) { 93 ret = fprintf(out, "d /dev/%.*s 0755 - - -\n", 94 (int)(dir - devname), devname); 95 if (ret < 0) 96 return EXIT_FAILURE; 97 } 98 99 ret = fprintf(out, "%c! /dev/%s 0600 - - - %u:%u\n", 100 type, devname, maj, min); 101 if (ret < 0) 102 return EXIT_FAILURE; 103 104 return EXIT_SUCCESS; 105 } 106 107 static const struct static_nodes_format static_nodes_format_tmpfiles = { 108 .name = "tmpfiles", 109 .write = write_tmpfiles, 110 .description = "the tmpfiles.d(5) format used by systemd-tmpfiles.", 111 }; 112 113 static int write_devname(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) 114 { 115 int ret; 116 117 ret = fprintf(out, "%s %s %c%u:%u\n", modname, devname, type, maj, min); 118 if (ret >= 0) 119 return EXIT_SUCCESS; 120 else 121 return EXIT_FAILURE; 122 } 123 124 static const struct static_nodes_format static_nodes_format_devname = { 125 .name = "devname", 126 .write = write_devname, 127 .description = "the modules.devname format.", 128 }; 129 130 static void help(void) 131 { 132 size_t i; 133 134 printf("Usage:\n" 135 "\t%s static-nodes [options]\n" 136 "\n" 137 "kmod static-nodes outputs the static-node information of the currently running kernel.\n" 138 "\n" 139 "Options:\n" 140 "\t-f, --format=FORMAT choose format to use: see \"Formats\"\n" 141 "\t-o, --output=FILE write output to file\n" 142 "\t-h, --help show this help\n" 143 "\n" 144 "Formats:\n", 145 program_invocation_short_name); 146 147 for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) { 148 if (static_nodes_formats[i]->description != NULL) { 149 printf("\t%-12s %s\n", static_nodes_formats[i]->name, 150 static_nodes_formats[i]->description); 151 } 152 } 153 } 154 155 static int do_static_nodes(int argc, char *argv[]) 156 { 157 struct utsname kernel; 158 char modules[PATH_MAX], buf[4096]; 159 const char *output = "/dev/stdout"; 160 FILE *in = NULL, *out = NULL; 161 const struct static_nodes_format *format = &static_nodes_format_human; 162 int r, ret = EXIT_SUCCESS; 163 164 for (;;) { 165 int c, idx = 0, valid; 166 size_t i; 167 168 c = getopt_long(argc, argv, cmdopts_s, cmdopts, &idx); 169 if (c == -1) { 170 break; 171 } 172 switch (c) { 173 case 'o': 174 output = optarg; 175 break; 176 case 'f': 177 valid = 0; 178 179 for (i = 0; i < ARRAY_SIZE(static_nodes_formats); i++) { 180 if (streq(static_nodes_formats[i]->name, optarg)) { 181 format = static_nodes_formats[i]; 182 valid = 1; 183 } 184 } 185 186 if (!valid) { 187 fprintf(stderr, "Unknown format: '%s'.\n", 188 optarg); 189 help(); 190 ret = EXIT_FAILURE; 191 goto finish; 192 } 193 break; 194 case 'h': 195 help(); 196 goto finish; 197 case '?': 198 ret = EXIT_FAILURE; 199 goto finish; 200 default: 201 fprintf(stderr, "Unexpected commandline option '%c'.\n", 202 c); 203 help(); 204 ret = EXIT_FAILURE; 205 goto finish; 206 } 207 } 208 209 if (uname(&kernel) < 0) { 210 fputs("Error: uname failed!\n", stderr); 211 ret = EXIT_FAILURE; 212 goto finish; 213 } 214 215 snprintf(modules, sizeof(modules), "/lib/modules/%s/modules.devname", kernel.release); 216 in = fopen(modules, "re"); 217 if (in == NULL) { 218 if (errno == ENOENT) { 219 fprintf(stderr, "Warning: /lib/modules/%s/modules.devname not found - ignoring\n", 220 kernel.release); 221 ret = EXIT_SUCCESS; 222 } else { 223 fprintf(stderr, "Error: could not open /lib/modules/%s/modules.devname - %m\n", 224 kernel.release); 225 ret = EXIT_FAILURE; 226 } 227 goto finish; 228 } 229 230 r = mkdir_parents(output, 0755); 231 if (r < 0) { 232 fprintf(stderr, "Error: could not create parent directory for %s - %m.\n", output); 233 ret = EXIT_FAILURE; 234 goto finish; 235 } 236 237 out = fopen(output, "we"); 238 if (out == NULL) { 239 fprintf(stderr, "Error: could not create %s - %m\n", output); 240 ret = EXIT_FAILURE; 241 goto finish; 242 } 243 244 while (fgets(buf, sizeof(buf), in) != NULL) { 245 char modname[PATH_MAX]; 246 char devname[PATH_MAX]; 247 char type; 248 unsigned int maj, min; 249 int matches; 250 251 if (buf[0] == '#') 252 continue; 253 254 matches = sscanf(buf, "%s %s %c%u:%u", modname, devname, 255 &type, &maj, &min); 256 if (matches != 5 || (type != 'c' && type != 'b')) { 257 fprintf(stderr, "Error: invalid devname entry: %s", buf); 258 ret = EXIT_FAILURE; 259 continue; 260 } 261 262 format->write(out, modname, devname, type, maj, min); 263 } 264 265 finish: 266 if (in) 267 fclose(in); 268 if (out) 269 fclose(out); 270 return ret; 271 } 272 273 const struct kmod_cmd kmod_cmd_static_nodes = { 274 .name = "static-nodes", 275 .cmd = do_static_nodes, 276 .help = "outputs the static-node information installed with the currently running kernel", 277 }; 278