]>
Commit | Line | Data |
---|---|---|
db6f2fc7 TG |
1 | /* |
2 | * kmod-static-nodes - manage modules.devname | |
3 | * | |
4 | * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> | |
5 | * Copyright (C) 2011-2013 ProFUSION embedded systems | |
6 | * Copyright (C) 2013 Tom Gundersen <teg@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 | ||
c2e4286b LDM |
22 | #include <errno.h> |
23 | #include <getopt.h> | |
24 | #include <limits.h> | |
25 | #include <stddef.h> | |
db6f2fc7 TG |
26 | #include <stdio.h> |
27 | #include <stdlib.h> | |
db6f2fc7 | 28 | #include <string.h> |
c2e4286b | 29 | #include <unistd.h> |
db6f2fc7 TG |
30 | #include <sys/stat.h> |
31 | #include <sys/types.h> | |
c2e4286b | 32 | #include <sys/utsname.h> |
96573a02 LDM |
33 | |
34 | #include <shared/util.h> | |
db6f2fc7 TG |
35 | |
36 | #include "kmod.h" | |
37 | ||
38 | struct static_nodes_format { | |
19ac5bd8 LDM |
39 | const char *name; |
40 | int (*write)(FILE *, char[], char[], char, unsigned int, unsigned int); | |
41 | const char *description; | |
db6f2fc7 TG |
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[] = { | |
19ac5bd8 LDM |
49 | &static_nodes_format_human, |
50 | &static_nodes_format_tmpfiles, | |
51 | &static_nodes_format_devname, | |
db6f2fc7 TG |
52 | }; |
53 | ||
54 | static const char cmdopts_s[] = "o:f:h"; | |
55 | static const struct option cmdopts[] = { | |
19ac5bd8 LDM |
56 | { "output", required_argument, 0, 'o'}, |
57 | { "format", required_argument, 0, 'f'}, | |
58 | { "help", no_argument, 0, 'h'}, | |
59 | { }, | |
db6f2fc7 TG |
60 | }; |
61 | ||
62 | static int write_human(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) | |
63 | { | |
19ac5bd8 LDM |
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; | |
db6f2fc7 TG |
78 | } |
79 | ||
80 | static const struct static_nodes_format static_nodes_format_human = { | |
81 | .name = "human", | |
82 | .write = write_human, | |
19ac5bd8 | 83 | .description = "(default) a human readable format. Do not parse.", |
db6f2fc7 TG |
84 | }; |
85 | ||
db6f2fc7 TG |
86 | static int write_tmpfiles(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) |
87 | { | |
19ac5bd8 LDM |
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; | |
db6f2fc7 TG |
105 | } |
106 | ||
107 | static const struct static_nodes_format static_nodes_format_tmpfiles = { | |
108 | .name = "tmpfiles", | |
109 | .write = write_tmpfiles, | |
19ac5bd8 | 110 | .description = "the tmpfiles.d(5) format used by systemd-tmpfiles.", |
db6f2fc7 TG |
111 | }; |
112 | ||
113 | static int write_devname(FILE *out, char modname[], char devname[], char type, unsigned int maj, unsigned int min) | |
114 | { | |
19ac5bd8 | 115 | int ret; |
db6f2fc7 | 116 | |
19ac5bd8 LDM |
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; | |
db6f2fc7 TG |
122 | } |
123 | ||
124 | static const struct static_nodes_format static_nodes_format_devname = { | |
125 | .name = "devname", | |
126 | .write = write_devname, | |
19ac5bd8 | 127 | .description = "the modules.devname format.", |
db6f2fc7 TG |
128 | }; |
129 | ||
130 | static void help(void) | |
131 | { | |
19ac5bd8 LDM |
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" | |
a5cbde4b | 140 | "\t-f, --format=FORMAT choose format to use: see \"Formats\"\n" |
19ac5bd8 LDM |
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 | } | |
db6f2fc7 TG |
153 | } |
154 | ||
155 | static int do_static_nodes(int argc, char *argv[]) | |
156 | { | |
19ac5bd8 | 157 | struct utsname kernel; |
232bf4d8 | 158 | char modules[PATH_MAX], buf[4096]; |
ae177101 TG |
159 | const char *output = "/dev/stdout"; |
160 | FILE *in = NULL, *out = NULL; | |
19ac5bd8 | 161 | const struct static_nodes_format *format = &static_nodes_format_human; |
232bf4d8 | 162 | int r, ret = EXIT_SUCCESS; |
19ac5bd8 LDM |
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': | |
ae177101 | 174 | output = optarg; |
19ac5bd8 LDM |
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 | ||
ae177101 | 215 | snprintf(modules, sizeof(modules), "/lib/modules/%s/modules.devname", kernel.release); |
19ac5bd8 LDM |
216 | in = fopen(modules, "re"); |
217 | if (in == NULL) { | |
ae177101 TG |
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 | ||
232bf4d8 TG |
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 | ||
ae177101 TG |
237 | out = fopen(output, "we"); |
238 | if (out == NULL) { | |
239 | fprintf(stderr, "Error: could not create %s - %m\n", output); | |
19ac5bd8 LDM |
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 | } | |
db6f2fc7 TG |
264 | |
265 | finish: | |
19ac5bd8 LDM |
266 | if (in) |
267 | fclose(in); | |
268 | if (out) | |
269 | fclose(out); | |
270 | return ret; | |
db6f2fc7 TG |
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 | }; |