]>
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); | |
2c308875 | 111 | close_stdout_atexit(); |
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 | 121 | case 'V': |
2c308875 | 122 | print_version(EXIT_SUCCESS); |
eb63b9b8 | 123 | case 'h': |
5118d1be | 124 | usage(); |
eb63b9b8 | 125 | default: |
677ec86c | 126 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 | 127 | } |
5dd48fb2 | 128 | |
eb63b9b8 KZ |
129 | /* |
130 | * Check for, and open, the master raw device, /dev/raw | |
131 | */ | |
eb63b9b8 | 132 | open_raw_ctl(); |
5dd48fb2 | 133 | |
eb63b9b8 | 134 | if (do_query_all) { |
5118d1be RM |
135 | if (optind < argc) { |
136 | warnx(_("bad usage")); | |
137 | errtryhelp(EXIT_FAILURE); | |
138 | } | |
34ab1590 | 139 | for (i = 1; i < RAW_NR_MINORS; i++) |
b41f637c | 140 | query(i, NULL, 1); |
44e53535 | 141 | exit(EXIT_SUCCESS); |
eb63b9b8 | 142 | } |
5dd48fb2 | 143 | |
eb63b9b8 KZ |
144 | /* |
145 | * It's a bind or a single query. Either way we need a raw device. | |
146 | */ | |
147 | ||
5118d1be RM |
148 | if (optind >= argc) { |
149 | warnx(_("bad usage")); | |
150 | errtryhelp(EXIT_FAILURE); | |
151 | } | |
eb63b9b8 KZ |
152 | raw_name = argv[optind++]; |
153 | ||
34ab1590 KZ |
154 | /* |
155 | * try to check the device name before stat(), because on systems with | |
156 | * udev the raw0 causes a create udev event for char 162/0, which | |
157 | * causes udev to *remove* /dev/rawctl | |
158 | */ | |
16d8a9c9 | 159 | rc = sscanf(raw_name, _PATH_RAWDEVDIR "raw%d", &raw_minor); |
5118d1be RM |
160 | if (rc != 1) { |
161 | warnx(_("bad usage")); | |
162 | errtryhelp(EXIT_FAILURE); | |
163 | } | |
82b7ef36 SK |
164 | if (raw_minor == 0) |
165 | errx(EXIT_RAW_ACCESS, | |
166 | _("Device '%s' is the control raw device " | |
167 | "(use raw<N> where <N> is greater than zero)"), | |
168 | raw_name); | |
34ab1590 | 169 | |
eb63b9b8 | 170 | if (do_query) |
b41f637c | 171 | return query(raw_minor, raw_name, 0); |
34ab1590 | 172 | |
5dd48fb2 | 173 | /* |
cd121363 SK |
174 | * It's not a query, so we still have some parsing to do. Have we been |
175 | * given a block device filename or a major/minor pair? | |
eb63b9b8 | 176 | */ |
eb63b9b8 KZ |
177 | switch (argc - optind) { |
178 | case 1: | |
179 | block_name = argv[optind]; | |
82b7ef36 SK |
180 | retval = stat(block_name, &statbuf); |
181 | if (retval) | |
182 | err(EXIT_RAW_ACCESS, | |
cd121363 | 183 | _("Cannot locate block device '%s'"), block_name); |
82b7ef36 SK |
184 | if (!S_ISBLK(statbuf.st_mode)) |
185 | errx(EXIT_RAW_ACCESS, | |
186 | _("Device '%s' is not a block device"), | |
187 | block_name); | |
eb63b9b8 KZ |
188 | block_major = major(statbuf.st_rdev); |
189 | block_minor = minor(statbuf.st_rdev); | |
190 | break; | |
5dd48fb2 | 191 | |
eb63b9b8 | 192 | case 2: |
d5fc1b15 SK |
193 | block_major = |
194 | strtol_octal_or_err(argv[optind], | |
195 | _("failed to parse argument")); | |
196 | block_minor = | |
197 | strtol_octal_or_err(argv[optind + 1], | |
198 | _("failed to parse argument")); | |
eb63b9b8 | 199 | break; |
5dd48fb2 | 200 | |
eb63b9b8 | 201 | default: |
5118d1be RM |
202 | warnx(_("bad usage")); |
203 | errtryhelp(EXIT_FAILURE); | |
eb63b9b8 | 204 | } |
5dd48fb2 | 205 | |
eb63b9b8 | 206 | return bind(raw_minor, block_major, block_minor); |
eb63b9b8 KZ |
207 | } |
208 | ||
eb63b9b8 KZ |
209 | void open_raw_ctl(void) |
210 | { | |
16d8a9c9 | 211 | master_fd = open(_PATH_RAWDEVCTL, O_RDWR, 0); |
eb63b9b8 | 212 | if (master_fd < 0) { |
16d8a9c9 | 213 | master_fd = open(_PATH_RAWDEVCTL_OLD, O_RDWR, 0); |
82b7ef36 SK |
214 | if (master_fd < 0) |
215 | err(EXIT_RAW_ACCESS, | |
16d8a9c9 SK |
216 | _("Cannot open master raw device '%s'"), |
217 | _PATH_RAWDEVCTL); | |
eb63b9b8 KZ |
218 | } |
219 | } | |
220 | ||
89c50053 | 221 | static int query(int minor_raw, const char *raw_name, int quiet) |
eb63b9b8 KZ |
222 | { |
223 | struct raw_config_request rq; | |
34ab1590 | 224 | static int has_worked = 0; |
34ab1590 | 225 | |
b41f637c JM |
226 | if (raw_name) { |
227 | struct stat statbuf; | |
228 | ||
a434e239 | 229 | if (stat(raw_name, &statbuf) != 0) |
82b7ef36 SK |
230 | err(EXIT_RAW_ACCESS, |
231 | _("Cannot locate raw device '%s'"), raw_name); | |
232 | if (!S_ISCHR(statbuf.st_mode)) | |
233 | errx(EXIT_RAW_ACCESS, | |
234 | _("Raw device '%s' is not a character dev"), | |
235 | raw_name); | |
236 | if (major(statbuf.st_rdev) != RAW_MAJOR) | |
237 | errx(EXIT_RAW_ACCESS, | |
238 | _("Device '%s' is not a raw dev"), raw_name); | |
89c50053 | 239 | minor_raw = minor(statbuf.st_rdev); |
b41f637c JM |
240 | } |
241 | ||
89c50053 | 242 | rq.raw_minor = minor_raw; |
cd121363 | 243 | if (ioctl(master_fd, RAW_GETBIND, &rq) < 0) { |
eb63b9b8 KZ |
244 | if (quiet && errno == ENODEV) |
245 | return 3; | |
34ab1590 KZ |
246 | if (has_worked && errno == EINVAL) |
247 | return 0; | |
82b7ef36 | 248 | err(EXIT_RAW_IOCTL, _("Error querying raw device")); |
eb63b9b8 | 249 | } |
82b7ef36 | 250 | |
cd121363 SK |
251 | /* If one query has worked, mark that fact so that we don't report |
252 | * spurious fatal errors if raw(8) has been built to support more raw | |
253 | * minor numbers than the kernel has. */ | |
34ab1590 | 254 | has_worked = 1; |
eb63b9b8 KZ |
255 | if (quiet && !rq.block_major && !rq.block_minor) |
256 | return 0; | |
16d8a9c9 SK |
257 | printf(_("%sraw%d: bound to major %d, minor %d\n"), |
258 | _PATH_RAWDEVDIR, minor_raw, (int)rq.block_major, | |
259 | (int)rq.block_minor); | |
eb63b9b8 KZ |
260 | return 0; |
261 | } | |
262 | ||
89c50053 | 263 | static int bind(int minor_raw, int block_major, int block_minor) |
eb63b9b8 KZ |
264 | { |
265 | struct raw_config_request rq; | |
5dd48fb2 | 266 | |
cd121363 | 267 | rq.raw_minor = minor_raw; |
eb63b9b8 KZ |
268 | rq.block_major = block_major; |
269 | rq.block_minor = block_minor; | |
60d72321 | 270 | if (ioctl(master_fd, RAW_SETBIND, &rq) < 0) |
82b7ef36 | 271 | err(EXIT_RAW_IOCTL, _("Error setting raw device")); |
16d8a9c9 SK |
272 | printf(_("%sraw%d: bound to major %d, minor %d\n"), |
273 | _PATH_RAWDEVDIR, raw_minor, (int)rq.block_major, | |
274 | (int)rq.block_minor); | |
eb63b9b8 KZ |
275 | return 0; |
276 | } |