]>
Commit | Line | Data |
---|---|---|
eb63b9b8 KZ |
1 | /* |
2 | * raw.c: User mode tool to bind and query raw character devices. | |
3 | * | |
22853e4a | 4 | * Stephen Tweedie, 1999, 2000 |
eb63b9b8 KZ |
5 | * |
6 | * This file may be redistributed under the terms of the GNU General | |
7 | * Public License, version 2. | |
cd121363 | 8 | * |
22853e4a | 9 | * Copyright Red Hat Software, 1999, 2000 |
eb63b9b8 KZ |
10 | * |
11 | */ | |
12 | ||
eb63b9b8 | 13 | #include <errno.h> |
d5c28ebc | 14 | #include <fcntl.h> |
cd121363 SK |
15 | #include <getopt.h> |
16 | #include <linux/major.h> | |
17 | #include <linux/raw.h> | |
18 | #include <stdio.h> | |
19 | #include <stdlib.h> | |
20 | #include <string.h> | |
eb63b9b8 | 21 | #include <sys/ioctl.h> |
cd121363 | 22 | #include <sys/stat.h> |
cd121363 | 23 | #include <unistd.h> |
7c7c8bc5 SK |
24 | |
25 | #include "c.h" | |
45ca68ec | 26 | #include "closestream.h" |
5dd48fb2 | 27 | #include "nls.h" |
16d8a9c9 | 28 | #include "pathnames.h" |
eb63b9b8 | 29 | |
44e53535 SK |
30 | #define EXIT_RAW_ACCESS 3 |
31 | #define EXIT_RAW_IOCTL 4 | |
6db46818 | 32 | |
34ab1590 | 33 | #define RAW_NR_MINORS 8192 |
eb63b9b8 | 34 | |
2ba641e5 SK |
35 | static int do_query; |
36 | static int do_query_all; | |
eb63b9b8 | 37 | |
2ba641e5 SK |
38 | static int master_fd; |
39 | static int raw_minor; | |
eb63b9b8 KZ |
40 | |
41 | void open_raw_ctl(void); | |
89c50053 | 42 | static int query(int minor_raw, const char *raw_name, int quiet); |
43 | static int bind(int minor_raw, int block_major, int block_minor); | |
eb63b9b8 | 44 | |
5118d1be | 45 | static void __attribute__((__noreturn__)) usage(void) |
eb63b9b8 | 46 | { |
5118d1be | 47 | FILE *out = stdout; |
7c7c8bc5 SK |
48 | fputs(USAGE_HEADER, out); |
49 | fprintf(out, | |
50 | _(" %1$s %2$srawN <major> <minor>\n" | |
51 | " %1$s %2$srawN /dev/<blockdevice>\n" | |
52 | " %1$s -q %2$srawN\n" | |
16d8a9c9 SK |
53 | " %1$s -qa\n"), program_invocation_short_name, |
54 | _PATH_RAWDEVDIR); | |
451dbcfa BS |
55 | |
56 | fputs(USAGE_SEPARATOR, out); | |
57 | fputs(_("Bind a raw character device to a block device.\n"), out); | |
58 | ||
7c7c8bc5 | 59 | fputs(USAGE_OPTIONS, out); |
1e0aef82 SK |
60 | fputs(_(" -q, --query set query mode\n"), out); |
61 | fputs(_(" -a, --all query all raw devices\n"), out); | |
f45f3ec3 RM |
62 | printf(USAGE_HELP_OPTIONS(16)); |
63 | printf(USAGE_MAN_TAIL("raw(8)")); | |
5118d1be | 64 | exit(EXIT_SUCCESS); |
eb63b9b8 KZ |
65 | } |
66 | ||
16bade89 | 67 | static long strtol_octal_or_err(const char *str, const char *errmesg) |
d5fc1b15 SK |
68 | { |
69 | long num; | |
70 | char *end = NULL; | |
71 | ||
72 | if (str == NULL || *str == '\0') | |
73 | goto err; | |
74 | errno = 0; | |
75 | num = strtol(str, &end, 0); | |
76 | ||
77 | if (errno || str == end || (end && *end)) | |
78 | goto err; | |
79 | ||
80 | return num; | |
81 | err: | |
82 | if (errno) | |
83 | err(EXIT_FAILURE, "%s: '%s'", errmesg, str); | |
84 | else | |
85 | errx(EXIT_FAILURE, "%s: '%s'", errmesg, str); | |
86 | return 0; | |
87 | } | |
88 | ||
eb63b9b8 KZ |
89 | int main(int argc, char *argv[]) |
90 | { | |
cd121363 SK |
91 | int c; |
92 | char *raw_name; | |
93 | char *block_name; | |
94 | int retval; | |
95 | int block_major, block_minor; | |
96 | int i, rc; | |
eb63b9b8 KZ |
97 | |
98 | struct stat statbuf; | |
5dd48fb2 | 99 | |
1e0aef82 | 100 | static const struct option longopts[] = { |
87918040 SK |
101 | {"query", no_argument, NULL, 'q'}, |
102 | {"all", no_argument, NULL, 'a'}, | |
103 | {"version", no_argument, NULL, 'V'}, | |
104 | {"help", no_argument, NULL, 'h'}, | |
105 | {NULL, 0, NULL, '0'}, | |
1e0aef82 SK |
106 | }; |
107 | ||
9d2b339a | 108 | setlocale(LC_ALL, ""); |
5dd48fb2 PR |
109 | bindtextdomain(PACKAGE, LOCALEDIR); |
110 | textdomain(PACKAGE); | |
45ca68ec | 111 | atexit(close_stdout); |
5dd48fb2 | 112 | |
cd121363 | 113 | while ((c = getopt_long(argc, argv, "qaVh", longopts, NULL)) != -1) |
eb63b9b8 | 114 | switch (c) { |
1e0aef82 SK |
115 | case 'q': |
116 | do_query = 1; | |
117 | break; | |
eb63b9b8 KZ |
118 | case 'a': |
119 | do_query_all = 1; | |
120 | break; | |
1e0aef82 SK |
121 | case 'V': |
122 | printf(UTIL_LINUX_VERSION); | |
123 | return EXIT_SUCCESS; | |
eb63b9b8 | 124 | case 'h': |
5118d1be | 125 | usage(); |
eb63b9b8 | 126 | default: |
677ec86c | 127 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 | 128 | } |
5dd48fb2 | 129 | |
eb63b9b8 KZ |
130 | /* |
131 | * Check for, and open, the master raw device, /dev/raw | |
132 | */ | |
eb63b9b8 | 133 | open_raw_ctl(); |
5dd48fb2 | 134 | |
eb63b9b8 | 135 | if (do_query_all) { |
5118d1be RM |
136 | if (optind < argc) { |
137 | warnx(_("bad usage")); | |
138 | errtryhelp(EXIT_FAILURE); | |
139 | } | |
34ab1590 | 140 | for (i = 1; i < RAW_NR_MINORS; i++) |
b41f637c | 141 | query(i, NULL, 1); |
44e53535 | 142 | exit(EXIT_SUCCESS); |
eb63b9b8 | 143 | } |
5dd48fb2 | 144 | |
eb63b9b8 KZ |
145 | /* |
146 | * It's a bind or a single query. Either way we need a raw device. | |
147 | */ | |
148 | ||
5118d1be RM |
149 | if (optind >= argc) { |
150 | warnx(_("bad usage")); | |
151 | errtryhelp(EXIT_FAILURE); | |
152 | } | |
eb63b9b8 KZ |
153 | raw_name = argv[optind++]; |
154 | ||
34ab1590 KZ |
155 | /* |
156 | * try to check the device name before stat(), because on systems with | |
157 | * udev the raw0 causes a create udev event for char 162/0, which | |
158 | * causes udev to *remove* /dev/rawctl | |
159 | */ | |
16d8a9c9 | 160 | rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor); |
5118d1be RM |
161 | if (rc != 1) { |
162 | warnx(_("bad usage")); | |
163 | errtryhelp(EXIT_FAILURE); | |
164 | } | |
82b7ef36 SK |
165 | if (raw_minor == 0) |
166 | errx(EXIT_RAW_ACCESS, | |
167 | _("Device '%s' is the control raw device " | |
168 | "(use raw<N> where <N> is greater than zero)"), | |
169 | raw_name); | |
34ab1590 | 170 | |
eb63b9b8 | 171 | if (do_query) |
b41f637c | 172 | return query(raw_minor, raw_name, 0); |
34ab1590 | 173 | |
5dd48fb2 | 174 | /* |
cd121363 SK |
175 | * It's not a query, so we still have some parsing to do. Have we been |
176 | * given a block device filename or a major/minor pair? | |
eb63b9b8 | 177 | */ |
eb63b9b8 KZ |
178 | switch (argc - optind) { |
179 | case 1: | |
180 | block_name = argv[optind]; | |
82b7ef36 SK |
181 | retval = stat(block_name, &statbuf); |
182 | if (retval) | |
183 | err(EXIT_RAW_ACCESS, | |
cd121363 | 184 | _("Cannot locate block device '%s'"), block_name); |
82b7ef36 SK |
185 | if (!S_ISBLK(statbuf.st_mode)) |
186 | errx(EXIT_RAW_ACCESS, | |
187 | _("Device '%s' is not a block device"), | |
188 | block_name); | |
eb63b9b8 KZ |
189 | block_major = major(statbuf.st_rdev); |
190 | block_minor = minor(statbuf.st_rdev); | |
191 | break; | |
5dd48fb2 | 192 | |
eb63b9b8 | 193 | case 2: |
d5fc1b15 SK |
194 | block_major = |
195 | strtol_octal_or_err(argv[optind], | |
196 | _("failed to parse argument")); | |
197 | block_minor = | |
198 | strtol_octal_or_err(argv[optind + 1], | |
199 | _("failed to parse argument")); | |
eb63b9b8 | 200 | break; |
5dd48fb2 | 201 | |
eb63b9b8 | 202 | default: |
5118d1be RM |
203 | warnx(_("bad usage")); |
204 | errtryhelp(EXIT_FAILURE); | |
eb63b9b8 | 205 | } |
5dd48fb2 | 206 | |
eb63b9b8 | 207 | return bind(raw_minor, block_major, block_minor); |
eb63b9b8 KZ |
208 | } |
209 | ||
eb63b9b8 KZ |
210 | void open_raw_ctl(void) |
211 | { | |
16d8a9c9 | 212 | master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0); |
eb63b9b8 | 213 | if (master_fd < 0) { |
16d8a9c9 | 214 | master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0); |
82b7ef36 SK |
215 | if (master_fd < 0) |
216 | err(EXIT_RAW_ACCESS, | |
16d8a9c9 SK |
217 | _("Cannot open master raw device '%s'"), |
218 | _PATH_RAWDEVCTL); | |
eb63b9b8 KZ |
219 | } |
220 | } | |
221 | ||
89c50053 | 222 | static int query(int minor_raw, const char *raw_name, int quiet) |
eb63b9b8 KZ |
223 | { |
224 | struct raw_config_request rq; | |
34ab1590 | 225 | static int has_worked = 0; |
34ab1590 | 226 | |
b41f637c JM |
227 | if (raw_name) { |
228 | struct stat statbuf; | |
229 | ||
a434e239 | 230 | if (stat(raw_name, &statbuf) != 0) |
82b7ef36 SK |
231 | err(EXIT_RAW_ACCESS, |
232 | _("Cannot locate raw device '%s'"), raw_name); | |
233 | if (!S_ISCHR(statbuf.st_mode)) | |
234 | errx(EXIT_RAW_ACCESS, | |
235 | _("Raw device '%s' is not a character dev"), | |
236 | raw_name); | |
237 | if (major(statbuf.st_rdev) != RAW_MAJOR) | |
238 | errx(EXIT_RAW_ACCESS, | |
239 | _("Device '%s' is not a raw dev"), raw_name); | |
89c50053 | 240 | minor_raw = minor(statbuf.st_rdev); |
b41f637c JM |
241 | } |
242 | ||
89c50053 | 243 | rq.raw_minor = minor_raw; |
cd121363 | 244 | if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) { |
eb63b9b8 KZ |
245 | if (quiet && errno == ENODEV) |
246 | return 3; | |
34ab1590 KZ |
247 | if (has_worked && errno == EINVAL) |
248 | return 0; | |
82b7ef36 | 249 | err(EXIT_RAW_IOCTL, _("Error querying raw device")); |
eb63b9b8 | 250 | } |
82b7ef36 | 251 | |
cd121363 SK |
252 | /* If one query has worked, mark that fact so that we don't report |
253 | * spurious fatal errors if raw(8) has been built to support more raw | |
254 | * minor numbers than the kernel has. */ | |
34ab1590 | 255 | has_worked = 1; |
eb63b9b8 KZ |
256 | if (quiet && !rq.block_major && !rq.block_minor) |
257 | return 0; | |
16d8a9c9 SK |
258 | printf(_("%sraw%d: bound to major %d, minor %d\n"), |
259 | _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major, | |
260 | (int)rq.block_minor); | |
eb63b9b8 KZ |
261 | return 0; |
262 | } | |
263 | ||
89c50053 | 264 | static int bind(int minor_raw, int block_major, int block_minor) |
eb63b9b8 KZ |
265 | { |
266 | struct raw_config_request rq; | |
5dd48fb2 | 267 | |
cd121363 | 268 | rq.raw_minor = minor_raw; |
eb63b9b8 KZ |
269 | rq.block_major = block_major; |
270 | rq.block_minor = block_minor; | |
60d72321 | 271 | if (ioctl(master_fd, RAW_SETBIND, &rq) < 0) |
82b7ef36 | 272 | err(EXIT_RAW_IOCTL, _("Error setting raw device")); |
16d8a9c9 SK |
273 | printf(_("%sraw%d: bound to major %d, minor %d\n"), |
274 | _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major, | |
275 | (int)rq.block_minor); | |
eb63b9b8 KZ |
276 | return 0; |
277 | } |