]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/sysfs.c
lib: [at.c] fix compiler warnings [-Wunused-parameter]
[thirdparty/util-linux.git] / lib / sysfs.c
CommitLineData
7fe16fda
KZ
1/*
2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
3 */
4
5#include "c.h"
6#include "at.h"
7#include "pathnames.h"
8#include "sysfs.h"
9
10char *sysfs_devno_attribute_path(dev_t devno, char *buf,
413993fc 11 size_t bufsiz, const char *attr)
7fe16fda
KZ
12{
13 int len;
14
15 if (attr)
413993fc 16 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
7fe16fda
KZ
17 major(devno), minor(devno), attr);
18 else
413993fc 19 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
7fe16fda
KZ
20 major(devno), minor(devno));
21
413993fc 22 return (len < 0 || len + 1 > bufsiz) ? NULL : buf;
7fe16fda
KZ
23}
24
25int sysfs_devno_has_attribute(dev_t devno, const char *attr)
26{
27 char path[PATH_MAX];
28 struct stat info;
29
30 if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
31 return 0;
32 if (stat(path, &info) == 0)
33 return 1;
34 return 0;
35}
36
413993fc 37char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
7fe16fda 38{
413993fc 39 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
7fe16fda
KZ
40}
41
42dev_t sysfs_devname_to_devno(const char *name, const char *parent)
43{
44 char buf[PATH_MAX], *path = NULL;
45 dev_t dev = 0;
46
47 if (strncmp("/dev/", name, 5) == 0) {
48 /*
49 * Read from /dev
50 */
51 struct stat st;
52
53 if (stat(name, &st) == 0)
54 dev = st.st_rdev;
55 else
56 name += 5; /* unaccesible, or not node in /dev */
57 }
58
59 if (!dev && parent) {
60 /*
61 * Create path to /sys/block/<parent>/<name>/dev
62 */
63 int len = snprintf(buf, sizeof(buf),
64 _PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
65 if (len < 0 || len + 1 > sizeof(buf))
66 return 0;
67 path = buf;
68
69 } else if (!dev) {
70 /*
71 * Create path to /sys/block/<name>/dev
72 */
73 int len = snprintf(buf, sizeof(buf),
74 _PATH_SYS_BLOCK "/%s/dev", name);
75 if (len < 0 || len + 1 > sizeof(buf))
76 return 0;
77 path = buf;
78 }
79
80 if (path) {
81 /*
82 * read devno from sysfs
83 */
84 FILE *f;
85 int maj = 0, min = 0;
86
87 f = fopen(path, "r");
88 if (!f)
89 return 0;
90
91 if (fscanf(f, "%u:%u", &maj, &min) == 2)
92 dev = makedev(maj, min);
93 fclose(f);
94 }
95 return dev;
96}
97
413993fc
KZ
98/*
99 * Returns devname (e.g. "/dev/sda1") for the given devno.
100 *
101 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
102 * symlinks.
103 *
104 * Please, use more robust blkid_devno_to_devname() in your applications.
105 */
106char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
107{
108 struct sysfs_cxt cxt;
109 char *name;
110 size_t sz;
111 struct stat st;
112
113 if (sysfs_init(&cxt, devno, NULL))
114 return NULL;
115
116 name = sysfs_get_devname(&cxt, buf, bufsiz);
117 sysfs_deinit(&cxt);
118
119 if (!name)
120 return NULL;
121
122 sz = strlen(name);
123
124 if (sz + sizeof("/dev/") > bufsiz)
125 return NULL;
126
127 /* create the final "/dev/<name>" string */
128 memmove(buf + 5, name, sz + 1);
129 memcpy(buf, "/dev/", 5);
130
131 if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
132 return buf;
133
134 return NULL;
135}
136
7fe16fda
KZ
137int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
138{
139 char path[PATH_MAX];
140 int fd, rc = 0;
141
413993fc
KZ
142 memset(cxt, 0, sizeof(*cxt));
143
7fe16fda
KZ
144 if (!sysfs_devno_path(devno, path, sizeof(path)))
145 goto err;
146
147 fd = open(path, O_RDONLY);
148 if (fd < 0)
149 goto err;
7fe16fda
KZ
150 cxt->dir_path = strdup(path);
151 if (!cxt->dir_path)
152 goto err;
7fe16fda
KZ
153 cxt->devno = devno;
154 cxt->dir_fd = fd;
155 cxt->parent = parent;
156 return 0;
157err:
158 rc = -errno;
159 sysfs_deinit(cxt);
160 return rc;
161}
162
163void sysfs_deinit(struct sysfs_cxt *cxt)
164{
165 if (!cxt)
166 return;
167
168 if (cxt->dir_fd >= 0)
169 close(cxt->dir_fd);
87e77645
KZ
170 free(cxt->dir_path);
171
7fe16fda
KZ
172 cxt->devno = 0;
173 cxt->dir_fd = -1;
174 cxt->parent = NULL;
87e77645 175 cxt->dir_path = NULL;
7fe16fda
KZ
176}
177
178int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
179{
180 int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
181
182 if (rc != 0 && errno == ENOENT &&
183 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
184
185 /* Exception for "queue/<attr>". These attributes are available
186 * for parental devices only
187 */
188 return fstat_at(cxt->parent->dir_fd,
189 cxt->parent->dir_path, attr, st, 0);
190 }
191 return rc;
192}
193
d8a84552
KZ
194int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
195{
196 struct stat st;
197
198 return sysfs_stat(cxt, attr, &st) == 0;
199}
200
7fe16fda
KZ
201static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
202{
203 int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY);
204
205 if (fd == -1 && errno == ENOENT &&
206 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
207
208 /* Exception for "queue/<attr>". These attributes are available
209 * for parental devices only
210 */
211 fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY);
212 }
213 return fd;
214}
215
413993fc
KZ
216ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
217 char *buf, size_t bufsiz)
218{
219 if (attr)
220 return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
221
222 /* read /sys/dev/block/<maj:min> link */
223 return readlink(cxt->dir_path, buf, bufsiz);
224}
225
7fe16fda
KZ
226DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
227{
228 DIR *dir;
229 int fd;
230
231 if (attr)
232 fd = sysfs_open(cxt, attr);
413993fc 233 else
7fe16fda
KZ
234 /* request to open root of device in sysfs (/sys/block/<dev>)
235 * -- we cannot use cxt->sysfs_fd directly, because closedir()
236 * will close this our persistent file descriptor.
237 */
238 fd = dup(cxt->dir_fd);
7fe16fda
KZ
239
240 if (fd < 0)
241 return NULL;
242
243 dir = fdopendir(fd);
244 if (!dir) {
245 close(fd);
246 return NULL;
247 }
248 if (!attr)
249 rewinddir(dir);
250 return dir;
251}
252
253
254static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
255{
256 int fd = sysfs_open(cxt, attr);
257
258 return fd < 0 ? NULL : fdopen(fd, "r");
259}
260
261
262static struct dirent *xreaddir(DIR *dp)
263{
264 struct dirent *d;
265
266 while ((d = readdir(dp))) {
267 if (!strcmp(d->d_name, ".") ||
268 !strcmp(d->d_name, ".."))
269 continue;
270
271 /* blacklist here? */
272 break;
273 }
274 return d;
275}
276
277int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
278{
279 char path[256];
280
281#ifdef _DIRENT_HAVE_D_TYPE
282 if (d->d_type != DT_DIR)
283 return 0;
284#endif
285 if (strncmp(parent_name, d->d_name, strlen(parent_name)))
286 return 0;
287
288 /* Cannot use /partition file, not supported on old sysfs */
289 snprintf(path, sizeof(path), "%s/start", d->d_name);
290
291 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
292}
293
294int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
295{
296 FILE *f = sysfs_fopen(cxt, attr);
297 va_list ap;
298 int rc;
299
300 if (!f)
301 return -EINVAL;
302 va_start(ap, fmt);
303 rc = vfscanf(f, fmt, ap);
304 va_end(ap);
305
306 fclose(f);
307 return rc;
308}
309
413993fc 310
90e9fcda 311int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
7fe16fda 312{
90e9fcda
KZ
313 int64_t x = 0;
314
315 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
316 if (res)
317 *res = x;
318 return 0;
319 }
320 return -1;
7fe16fda
KZ
321}
322
90e9fcda 323int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
7fe16fda 324{
90e9fcda
KZ
325 uint64_t x = 0;
326
327 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
328 if (res)
329 *res = x;
330 return 0;
331 }
332 return -1;
7fe16fda
KZ
333}
334
90e9fcda 335int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
7fe16fda 336{
90e9fcda
KZ
337 int x = 0;
338
339 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
340 if (res)
341 *res = x;
342 return 0;
343 }
344 return -1;
7fe16fda
KZ
345}
346
347char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
348{
349 char buf[1024];
350 return sysfs_scanf(cxt, attr, "%1024[^\n]", buf) == 1 ?
351 strdup(buf) : NULL;
352}
353
354int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
355{
356 DIR *dir;
357 int r = 0;
358
359 if (!(dir = sysfs_opendir(cxt, attr)))
360 return 0;
361
362 while (xreaddir(dir)) r++;
363
364 closedir(dir);
365 return r;
366}
367
368int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
369{
370 DIR *dir;
371 struct dirent *d;
372 int r = 0;
373
374 if (!(dir = sysfs_opendir(cxt, NULL)))
375 return 0;
376
377 while ((d = xreaddir(dir))) {
378 if (sysfs_is_partition_dirent(dir, d, devname))
379 r++;
380 }
381
382 closedir(dir);
383 return r;
384}
385
413993fc
KZ
386/*
387 * Returns slave name if there is only one slave, otherwise returns NULL.
388 * The result should be deallocated by free().
389 */
390char *sysfs_get_slave(struct sysfs_cxt *cxt)
391{
392 DIR *dir;
393 struct dirent *d;
394 char *name = NULL;
395
396 if (!(dir = sysfs_opendir(cxt, "slaves")))
397 return NULL;
398
399 while ((d = xreaddir(dir))) {
400 if (name)
401 goto err; /* more slaves */
402
403 name = strdup(d->d_name);
404 }
405
406 closedir(dir);
407 return name;
408err:
409 free(name);
410 return NULL;
411}
412
413/*
414 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
415 * symlinks.
416 */
417char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
418{
419 char *name = NULL;
420 ssize_t sz;
421
422 sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
423 if (sz < 0)
424 return NULL;
425
426 buf[sz] = '\0';
427 name = strrchr(buf, '/');
428 if (!name)
429 return NULL;
430
431 name++;
432 sz = strlen(name);
433
434 memmove(buf, name, sz + 1);
435 return buf;
436}
7fe16fda 437
e918cca5 438#ifdef TEST_PROGRAM_SYSFS
7fe16fda
KZ
439#include <errno.h>
440#include <err.h>
441#include <stdlib.h>
442
443int main(int argc, char *argv[])
444{
445 struct sysfs_cxt cxt;
446 char *devname;
447 dev_t devno;
448 char path[PATH_MAX];
90e9fcda
KZ
449 int i;
450 uint64_t u64;
413993fc 451 ssize_t len;
7fe16fda
KZ
452
453 if (argc != 2)
454 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
455
456 devname = argv[1];
457 devno = sysfs_devname_to_devno(devname, NULL);
458
459 if (!devno)
460 err(EXIT_FAILURE, "failed to read devno");
461
462 printf("NAME: %s\n", devname);
463 printf("DEVNO: %u\n", (unsigned int) devno);
464 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
413993fc 465 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
7fe16fda
KZ
466 printf("PARTITION: %s\n",
467 sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT");
468
469 sysfs_init(&cxt, devno, NULL);
470
413993fc
KZ
471 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
472 if (len > 0) {
473 path[len] = '\0';
474 printf("DEVNOLINK: %s\n", path);
475 }
476
7fe16fda 477 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
90e9fcda
KZ
478
479 if (sysfs_read_u64(&cxt, "size", &u64))
413993fc 480 printf("read SIZE failed\n");
90e9fcda
KZ
481 else
482 printf("SIZE: %jd\n", u64);
483
484 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
413993fc 485 printf("read SECTOR failed\n");
90e9fcda
KZ
486 else
487 printf("SECTOR: %d\n", i);
7fe16fda 488
413993fc
KZ
489 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
490
fb4a9e54 491 sysfs_deinit(&cxt);
7fe16fda
KZ
492 return EXIT_SUCCESS;
493}
494#endif