]>
Commit | Line | Data |
---|---|---|
7fe16fda KZ |
1 | /* |
2 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> | |
3 | */ | |
4 | ||
093b20ba KZ |
5 | #include <ctype.h> |
6 | ||
7fe16fda KZ |
7 | #include "c.h" |
8 | #include "at.h" | |
9 | #include "pathnames.h" | |
10 | #include "sysfs.h" | |
11 | ||
12 | char *sysfs_devno_attribute_path(dev_t devno, char *buf, | |
413993fc | 13 | size_t bufsiz, const char *attr) |
7fe16fda KZ |
14 | { |
15 | int len; | |
16 | ||
17 | if (attr) | |
413993fc | 18 | len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s", |
7fe16fda KZ |
19 | major(devno), minor(devno), attr); |
20 | else | |
413993fc | 21 | len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d", |
7fe16fda KZ |
22 | major(devno), minor(devno)); |
23 | ||
6b9166ce | 24 | return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf; |
7fe16fda KZ |
25 | } |
26 | ||
27 | int sysfs_devno_has_attribute(dev_t devno, const char *attr) | |
28 | { | |
29 | char path[PATH_MAX]; | |
30 | struct stat info; | |
31 | ||
32 | if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr)) | |
33 | return 0; | |
34 | if (stat(path, &info) == 0) | |
35 | return 1; | |
36 | return 0; | |
37 | } | |
38 | ||
413993fc | 39 | char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz) |
7fe16fda | 40 | { |
413993fc | 41 | return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL); |
7fe16fda KZ |
42 | } |
43 | ||
44 | dev_t sysfs_devname_to_devno(const char *name, const char *parent) | |
45 | { | |
46 | char buf[PATH_MAX], *path = NULL; | |
47 | dev_t dev = 0; | |
48 | ||
49 | if (strncmp("/dev/", name, 5) == 0) { | |
50 | /* | |
51 | * Read from /dev | |
52 | */ | |
53 | struct stat st; | |
54 | ||
55 | if (stat(name, &st) == 0) | |
56 | dev = st.st_rdev; | |
57 | else | |
58 | name += 5; /* unaccesible, or not node in /dev */ | |
59 | } | |
60 | ||
61 | if (!dev && parent) { | |
62 | /* | |
63 | * Create path to /sys/block/<parent>/<name>/dev | |
64 | */ | |
65 | int len = snprintf(buf, sizeof(buf), | |
66 | _PATH_SYS_BLOCK "/%s/%s/dev", parent, name); | |
6b9166ce | 67 | if (len < 0 || (size_t) len + 1 > sizeof(buf)) |
7fe16fda KZ |
68 | return 0; |
69 | path = buf; | |
70 | ||
71 | } else if (!dev) { | |
72 | /* | |
73 | * Create path to /sys/block/<name>/dev | |
74 | */ | |
75 | int len = snprintf(buf, sizeof(buf), | |
76 | _PATH_SYS_BLOCK "/%s/dev", name); | |
6b9166ce | 77 | if (len < 0 || (size_t) len + 1 > sizeof(buf)) |
7fe16fda KZ |
78 | return 0; |
79 | path = buf; | |
80 | } | |
81 | ||
82 | if (path) { | |
83 | /* | |
84 | * read devno from sysfs | |
85 | */ | |
86 | FILE *f; | |
87 | int maj = 0, min = 0; | |
88 | ||
89 | f = fopen(path, "r"); | |
90 | if (!f) | |
91 | return 0; | |
92 | ||
9d72cbc1 | 93 | if (fscanf(f, "%d:%d", &maj, &min) == 2) |
7fe16fda KZ |
94 | dev = makedev(maj, min); |
95 | fclose(f); | |
96 | } | |
97 | return dev; | |
98 | } | |
99 | ||
413993fc KZ |
100 | /* |
101 | * Returns devname (e.g. "/dev/sda1") for the given devno. | |
102 | * | |
103 | * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min> | |
104 | * symlinks. | |
105 | * | |
106 | * Please, use more robust blkid_devno_to_devname() in your applications. | |
107 | */ | |
108 | char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz) | |
109 | { | |
110 | struct sysfs_cxt cxt; | |
111 | char *name; | |
112 | size_t sz; | |
113 | struct stat st; | |
114 | ||
115 | if (sysfs_init(&cxt, devno, NULL)) | |
116 | return NULL; | |
117 | ||
118 | name = sysfs_get_devname(&cxt, buf, bufsiz); | |
119 | sysfs_deinit(&cxt); | |
120 | ||
121 | if (!name) | |
122 | return NULL; | |
123 | ||
124 | sz = strlen(name); | |
125 | ||
126 | if (sz + sizeof("/dev/") > bufsiz) | |
127 | return NULL; | |
128 | ||
129 | /* create the final "/dev/<name>" string */ | |
130 | memmove(buf + 5, name, sz + 1); | |
131 | memcpy(buf, "/dev/", 5); | |
132 | ||
133 | if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno) | |
134 | return buf; | |
135 | ||
136 | return NULL; | |
137 | } | |
138 | ||
7fe16fda KZ |
139 | int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent) |
140 | { | |
141 | char path[PATH_MAX]; | |
4c5bbc5d | 142 | int fd, rc; |
7fe16fda | 143 | |
413993fc | 144 | memset(cxt, 0, sizeof(*cxt)); |
3a18db62 | 145 | cxt->dir_fd = -1; |
413993fc | 146 | |
7fe16fda KZ |
147 | if (!sysfs_devno_path(devno, path, sizeof(path))) |
148 | goto err; | |
149 | ||
150 | fd = open(path, O_RDONLY); | |
151 | if (fd < 0) | |
152 | goto err; | |
06ae069b KZ |
153 | cxt->dir_fd = fd; |
154 | ||
7fe16fda KZ |
155 | cxt->dir_path = strdup(path); |
156 | if (!cxt->dir_path) | |
157 | goto err; | |
7fe16fda | 158 | cxt->devno = devno; |
7fe16fda KZ |
159 | cxt->parent = parent; |
160 | return 0; | |
161 | err: | |
4c5bbc5d | 162 | rc = errno > 0 ? -errno : -1; |
7fe16fda KZ |
163 | sysfs_deinit(cxt); |
164 | return rc; | |
165 | } | |
166 | ||
167 | void sysfs_deinit(struct sysfs_cxt *cxt) | |
168 | { | |
169 | if (!cxt) | |
170 | return; | |
171 | ||
172 | if (cxt->dir_fd >= 0) | |
173 | close(cxt->dir_fd); | |
87e77645 KZ |
174 | free(cxt->dir_path); |
175 | ||
7fe16fda KZ |
176 | cxt->devno = 0; |
177 | cxt->dir_fd = -1; | |
178 | cxt->parent = NULL; | |
87e77645 | 179 | cxt->dir_path = NULL; |
7fe16fda KZ |
180 | } |
181 | ||
182 | int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st) | |
183 | { | |
184 | int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0); | |
185 | ||
186 | if (rc != 0 && errno == ENOENT && | |
187 | strncmp(attr, "queue/", 6) == 0 && cxt->parent) { | |
188 | ||
189 | /* Exception for "queue/<attr>". These attributes are available | |
190 | * for parental devices only | |
191 | */ | |
192 | return fstat_at(cxt->parent->dir_fd, | |
193 | cxt->parent->dir_path, attr, st, 0); | |
194 | } | |
195 | return rc; | |
196 | } | |
197 | ||
d8a84552 KZ |
198 | int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr) |
199 | { | |
200 | struct stat st; | |
201 | ||
202 | return sysfs_stat(cxt, attr, &st) == 0; | |
203 | } | |
204 | ||
7fe16fda KZ |
205 | static int sysfs_open(struct sysfs_cxt *cxt, const char *attr) |
206 | { | |
207 | int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY); | |
208 | ||
209 | if (fd == -1 && errno == ENOENT && | |
210 | strncmp(attr, "queue/", 6) == 0 && cxt->parent) { | |
211 | ||
212 | /* Exception for "queue/<attr>". These attributes are available | |
213 | * for parental devices only | |
214 | */ | |
215 | fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY); | |
216 | } | |
217 | return fd; | |
218 | } | |
219 | ||
413993fc KZ |
220 | ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr, |
221 | char *buf, size_t bufsiz) | |
222 | { | |
cffee0de CW |
223 | if (!cxt->dir_path) |
224 | return -1; | |
225 | ||
413993fc KZ |
226 | if (attr) |
227 | return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz); | |
228 | ||
229 | /* read /sys/dev/block/<maj:min> link */ | |
230 | return readlink(cxt->dir_path, buf, bufsiz); | |
231 | } | |
232 | ||
7fe16fda KZ |
233 | DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr) |
234 | { | |
235 | DIR *dir; | |
236 | int fd; | |
237 | ||
238 | if (attr) | |
239 | fd = sysfs_open(cxt, attr); | |
413993fc | 240 | else |
7fe16fda KZ |
241 | /* request to open root of device in sysfs (/sys/block/<dev>) |
242 | * -- we cannot use cxt->sysfs_fd directly, because closedir() | |
243 | * will close this our persistent file descriptor. | |
244 | */ | |
245 | fd = dup(cxt->dir_fd); | |
7fe16fda KZ |
246 | |
247 | if (fd < 0) | |
248 | return NULL; | |
249 | ||
250 | dir = fdopendir(fd); | |
251 | if (!dir) { | |
252 | close(fd); | |
253 | return NULL; | |
254 | } | |
255 | if (!attr) | |
256 | rewinddir(dir); | |
257 | return dir; | |
258 | } | |
259 | ||
260 | ||
261 | static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr) | |
262 | { | |
263 | int fd = sysfs_open(cxt, attr); | |
264 | ||
265 | return fd < 0 ? NULL : fdopen(fd, "r"); | |
266 | } | |
267 | ||
268 | ||
269 | static struct dirent *xreaddir(DIR *dp) | |
270 | { | |
271 | struct dirent *d; | |
272 | ||
273 | while ((d = readdir(dp))) { | |
274 | if (!strcmp(d->d_name, ".") || | |
275 | !strcmp(d->d_name, "..")) | |
276 | continue; | |
277 | ||
278 | /* blacklist here? */ | |
279 | break; | |
280 | } | |
281 | return d; | |
282 | } | |
283 | ||
284 | int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name) | |
285 | { | |
286 | char path[256]; | |
287 | ||
288 | #ifdef _DIRENT_HAVE_D_TYPE | |
09a71aa1 PR |
289 | if (d->d_type != DT_DIR && |
290 | d->d_type != DT_LNK) | |
7fe16fda KZ |
291 | return 0; |
292 | #endif | |
093b20ba KZ |
293 | if (parent_name) { |
294 | const char *p = parent_name; | |
295 | size_t len; | |
296 | ||
297 | /* /dev/sda --> "sda" */ | |
298 | if (*parent_name == '/') { | |
299 | p = strrchr(parent_name, '/'); | |
300 | if (!p) | |
301 | return 0; | |
302 | p++; | |
303 | } | |
304 | ||
305 | len = strlen(p); | |
306 | if (strlen(d->d_name) <= len) | |
307 | return 0; | |
308 | ||
a3b78053 KZ |
309 | /* partitions subdir name is |
310 | * "<parent>[:digit:]" or "<parent>p[:digit:]" | |
311 | */ | |
312 | return strncmp(p, d->d_name, len) == 0 && | |
313 | ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1))) | |
314 | || isdigit(*(d->d_name + len))); | |
093b20ba | 315 | } |
7fe16fda KZ |
316 | |
317 | /* Cannot use /partition file, not supported on old sysfs */ | |
318 | snprintf(path, sizeof(path), "%s/start", d->d_name); | |
319 | ||
320 | return faccessat(dirfd(dir), path, R_OK, 0) == 0; | |
321 | } | |
322 | ||
778ad369 KZ |
323 | /* |
324 | * Copnverts @partno (partition number) to devno of the partition. The @cxt | |
325 | * handles wholedisk device. | |
326 | * | |
327 | * Note that this code does not expect any special format of the | |
328 | * partitions devnames. | |
329 | */ | |
330 | dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno) | |
331 | { | |
332 | DIR *dir; | |
333 | struct dirent *d; | |
334 | char path[256]; | |
335 | dev_t devno = 0; | |
336 | ||
337 | dir = sysfs_opendir(cxt, NULL); | |
338 | if (!dir) | |
339 | return 0; | |
340 | ||
341 | while ((d = xreaddir(dir))) { | |
342 | int n, maj, min; | |
343 | ||
344 | if (!sysfs_is_partition_dirent(dir, d, NULL)) | |
345 | continue; | |
346 | ||
347 | snprintf(path, sizeof(path), "%s/partition", d->d_name); | |
348 | if (sysfs_read_int(cxt, path, &n) || n != partno) | |
349 | continue; | |
350 | ||
351 | snprintf(path, sizeof(path), "%s/dev", d->d_name); | |
352 | if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2) | |
353 | devno = makedev(maj, min); | |
354 | break; | |
355 | } | |
356 | ||
357 | closedir(dir); | |
358 | return devno; | |
359 | } | |
360 | ||
361 | ||
7fe16fda KZ |
362 | int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...) |
363 | { | |
364 | FILE *f = sysfs_fopen(cxt, attr); | |
365 | va_list ap; | |
366 | int rc; | |
367 | ||
368 | if (!f) | |
369 | return -EINVAL; | |
370 | va_start(ap, fmt); | |
371 | rc = vfscanf(f, fmt, ap); | |
372 | va_end(ap); | |
373 | ||
374 | fclose(f); | |
375 | return rc; | |
376 | } | |
377 | ||
413993fc | 378 | |
90e9fcda | 379 | int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res) |
7fe16fda | 380 | { |
90e9fcda KZ |
381 | int64_t x = 0; |
382 | ||
383 | if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) { | |
384 | if (res) | |
385 | *res = x; | |
386 | return 0; | |
387 | } | |
388 | return -1; | |
7fe16fda KZ |
389 | } |
390 | ||
90e9fcda | 391 | int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res) |
7fe16fda | 392 | { |
90e9fcda KZ |
393 | uint64_t x = 0; |
394 | ||
395 | if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) { | |
396 | if (res) | |
397 | *res = x; | |
398 | return 0; | |
399 | } | |
400 | return -1; | |
7fe16fda KZ |
401 | } |
402 | ||
90e9fcda | 403 | int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res) |
7fe16fda | 404 | { |
90e9fcda KZ |
405 | int x = 0; |
406 | ||
407 | if (sysfs_scanf(cxt, attr, "%d", &x) == 1) { | |
408 | if (res) | |
409 | *res = x; | |
410 | return 0; | |
411 | } | |
412 | return -1; | |
7fe16fda KZ |
413 | } |
414 | ||
415 | char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr) | |
416 | { | |
417 | char buf[1024]; | |
657d9adb | 418 | return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ? |
7fe16fda KZ |
419 | strdup(buf) : NULL; |
420 | } | |
421 | ||
422 | int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr) | |
423 | { | |
424 | DIR *dir; | |
425 | int r = 0; | |
426 | ||
427 | if (!(dir = sysfs_opendir(cxt, attr))) | |
428 | return 0; | |
429 | ||
430 | while (xreaddir(dir)) r++; | |
431 | ||
432 | closedir(dir); | |
433 | return r; | |
434 | } | |
435 | ||
436 | int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname) | |
437 | { | |
438 | DIR *dir; | |
439 | struct dirent *d; | |
440 | int r = 0; | |
441 | ||
442 | if (!(dir = sysfs_opendir(cxt, NULL))) | |
443 | return 0; | |
444 | ||
445 | while ((d = xreaddir(dir))) { | |
446 | if (sysfs_is_partition_dirent(dir, d, devname)) | |
447 | r++; | |
448 | } | |
449 | ||
450 | closedir(dir); | |
451 | return r; | |
452 | } | |
453 | ||
413993fc KZ |
454 | /* |
455 | * Returns slave name if there is only one slave, otherwise returns NULL. | |
456 | * The result should be deallocated by free(). | |
457 | */ | |
458 | char *sysfs_get_slave(struct sysfs_cxt *cxt) | |
459 | { | |
460 | DIR *dir; | |
461 | struct dirent *d; | |
462 | char *name = NULL; | |
463 | ||
464 | if (!(dir = sysfs_opendir(cxt, "slaves"))) | |
465 | return NULL; | |
466 | ||
467 | while ((d = xreaddir(dir))) { | |
468 | if (name) | |
469 | goto err; /* more slaves */ | |
470 | ||
471 | name = strdup(d->d_name); | |
472 | } | |
473 | ||
474 | closedir(dir); | |
475 | return name; | |
476 | err: | |
477 | free(name); | |
5415374d | 478 | closedir(dir); |
413993fc KZ |
479 | return NULL; |
480 | } | |
481 | ||
482 | /* | |
483 | * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min> | |
484 | * symlinks. | |
485 | */ | |
486 | char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz) | |
487 | { | |
488 | char *name = NULL; | |
489 | ssize_t sz; | |
490 | ||
491 | sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1); | |
492 | if (sz < 0) | |
493 | return NULL; | |
494 | ||
495 | buf[sz] = '\0'; | |
496 | name = strrchr(buf, '/'); | |
497 | if (!name) | |
498 | return NULL; | |
499 | ||
500 | name++; | |
501 | sz = strlen(name); | |
502 | ||
503 | memmove(buf, name, sz + 1); | |
504 | return buf; | |
505 | } | |
7fe16fda | 506 | |
3b66f48e ML |
507 | /* returns basename and keeps dirname in the @path */ |
508 | static char *stripoff_last_component(char *path) | |
509 | { | |
510 | char *p = strrchr(path, '/'); | |
511 | ||
512 | if (!p) | |
513 | return NULL; | |
514 | *p = '\0'; | |
515 | return ++p; | |
516 | } | |
517 | ||
518 | static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname, | |
519 | size_t len, dev_t *diskdevno) | |
520 | { | |
521 | int rc = 0; | |
522 | char *name; | |
523 | ||
524 | /* Note, sysfs_get_slave() returns the first slave only, | |
525 | * if there is more slaves, then return NULL | |
526 | */ | |
527 | name = sysfs_get_slave(cxt); | |
528 | if (!name) | |
529 | return -1; | |
530 | ||
531 | if (diskname && len) { | |
532 | strncpy(diskname, name, len); | |
533 | diskname[len - 1] = '\0'; | |
534 | } | |
535 | ||
536 | if (diskdevno) { | |
537 | *diskdevno = sysfs_devname_to_devno(name, NULL); | |
538 | if (!*diskdevno) | |
539 | rc = -1; | |
540 | } | |
541 | ||
542 | free(name); | |
543 | return rc; | |
544 | } | |
545 | ||
546 | int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, | |
547 | size_t len, dev_t *diskdevno) | |
548 | { | |
549 | struct sysfs_cxt cxt; | |
550 | int is_part = 0; | |
551 | ||
552 | if (!dev || sysfs_init(&cxt, dev, NULL) != 0) | |
553 | return -1; | |
554 | ||
555 | is_part = sysfs_has_attribute(&cxt, "partition"); | |
556 | if (!is_part) { | |
557 | /* | |
558 | * Extra case for partitions mapped by device-mapper. | |
559 | * | |
560 | * All regualar partitions (added by BLKPG ioctl or kernel PT | |
561 | * parser) have the /sys/.../partition file. The partitions | |
562 | * mapped by DM don't have such file, but they have "part" | |
563 | * prefix in DM UUID. | |
564 | */ | |
565 | char *uuid = sysfs_strdup(&cxt, "dm/uuid"); | |
566 | char *tmp = uuid; | |
567 | char *prefix = uuid ? strsep(&tmp, "-") : NULL; | |
568 | ||
569 | if (prefix && strncasecmp(prefix, "part", 4) == 0) | |
570 | is_part = 1; | |
571 | free(uuid); | |
572 | ||
573 | if (is_part && | |
574 | get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0) | |
575 | /* | |
576 | * partitioned device, mapped by DM | |
577 | */ | |
578 | goto done; | |
579 | ||
580 | is_part = 0; | |
581 | } | |
582 | ||
583 | if (!is_part) { | |
584 | /* | |
585 | * unpartitioned device | |
586 | */ | |
587 | if (diskname && len) { | |
588 | if (!sysfs_get_devname(&cxt, diskname, len)) | |
589 | goto err; | |
590 | } | |
591 | if (diskdevno) | |
592 | *diskdevno = dev; | |
593 | ||
594 | } else { | |
595 | /* | |
596 | * partitioned device | |
597 | * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1 | |
598 | * - dirname ../../block/sda/sda1 = ../../block/sda | |
599 | * - basename ../../block/sda = sda | |
600 | */ | |
601 | char linkpath[PATH_MAX]; | |
602 | char *name; | |
603 | int linklen; | |
604 | ||
605 | linklen = sysfs_readlink(&cxt, NULL, | |
606 | linkpath, sizeof(linkpath) - 1); | |
607 | if (linklen < 0) | |
608 | goto err; | |
609 | linkpath[linklen] = '\0'; | |
610 | ||
611 | stripoff_last_component(linkpath); /* dirname */ | |
612 | name = stripoff_last_component(linkpath); /* basename */ | |
613 | if (!name) | |
614 | goto err; | |
615 | ||
616 | if (diskname && len) { | |
617 | strncpy(diskname, name, len); | |
618 | diskname[len - 1] = '\0'; | |
619 | } | |
620 | ||
621 | if (diskdevno) { | |
622 | *diskdevno = sysfs_devname_to_devno(name, NULL); | |
623 | if (!*diskdevno) | |
624 | goto err; | |
625 | } | |
626 | } | |
627 | ||
628 | done: | |
629 | sysfs_deinit(&cxt); | |
630 | return 0; | |
631 | err: | |
632 | sysfs_deinit(&cxt); | |
633 | return -1; | |
634 | } | |
635 | ||
e918cca5 | 636 | #ifdef TEST_PROGRAM_SYSFS |
7fe16fda KZ |
637 | #include <errno.h> |
638 | #include <err.h> | |
639 | #include <stdlib.h> | |
640 | ||
641 | int main(int argc, char *argv[]) | |
642 | { | |
4c5bbc5d | 643 | struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; |
7fe16fda KZ |
644 | char *devname; |
645 | dev_t devno; | |
646 | char path[PATH_MAX]; | |
778ad369 | 647 | int i, is_part; |
90e9fcda | 648 | uint64_t u64; |
413993fc | 649 | ssize_t len; |
7fe16fda KZ |
650 | |
651 | if (argc != 2) | |
652 | errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]); | |
653 | ||
654 | devname = argv[1]; | |
655 | devno = sysfs_devname_to_devno(devname, NULL); | |
656 | ||
657 | if (!devno) | |
658 | err(EXIT_FAILURE, "failed to read devno"); | |
659 | ||
778ad369 KZ |
660 | is_part = sysfs_devno_has_attribute(devno, "partition"); |
661 | ||
7fe16fda | 662 | printf("NAME: %s\n", devname); |
778ad369 | 663 | printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); |
7fe16fda | 664 | printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path))); |
413993fc | 665 | printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); |
778ad369 | 666 | printf("PARTITION: %s\n", is_part ? "YES" : "NOT"); |
7fe16fda | 667 | |
1aae31c0 KZ |
668 | if (sysfs_init(&cxt, devno, NULL)) |
669 | return EXIT_FAILURE; | |
7fe16fda | 670 | |
413993fc KZ |
671 | len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1); |
672 | if (len > 0) { | |
673 | path[len] = '\0'; | |
674 | printf("DEVNOLINK: %s\n", path); | |
675 | } | |
676 | ||
778ad369 KZ |
677 | if (!is_part) { |
678 | printf("First 5 partitions:\n"); | |
679 | for (i = 1; i <= 5; i++) { | |
680 | dev_t dev = sysfs_partno_to_devno(&cxt, i); | |
681 | if (dev) | |
682 | printf("\t#%d %d:%d\n", i, major(dev), minor(dev)); | |
683 | } | |
684 | } | |
685 | ||
7fe16fda | 686 | printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves")); |
90e9fcda KZ |
687 | |
688 | if (sysfs_read_u64(&cxt, "size", &u64)) | |
413993fc | 689 | printf("read SIZE failed\n"); |
90e9fcda KZ |
690 | else |
691 | printf("SIZE: %jd\n", u64); | |
692 | ||
693 | if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i)) | |
413993fc | 694 | printf("read SECTOR failed\n"); |
90e9fcda KZ |
695 | else |
696 | printf("SECTOR: %d\n", i); | |
7fe16fda | 697 | |
413993fc KZ |
698 | printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path))); |
699 | ||
fb4a9e54 | 700 | sysfs_deinit(&cxt); |
7fe16fda KZ |
701 | return EXIT_SUCCESS; |
702 | } | |
703 | #endif |