• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
write_human(FILE * out,char modname[],char devname[],char type,unsigned int maj,unsigned int min)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 
write_tmpfiles(FILE * out,char modname[],char devname[],char type,unsigned int maj,unsigned int min)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 
write_devname(FILE * out,char modname[],char devname[],char type,unsigned int maj,unsigned int min)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 
help(void)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 
do_static_nodes(int argc,char * argv[])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