]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/sysfs.c
Merge branch 'sys-utils' of https://github.com/kerolasa/lelux-utiliteetit
[thirdparty/util-linux.git] / lib / sysfs.c
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
10 char *sysfs_devno_attribute_path(dev_t devno, char *buf,
11 size_t bufsiz, const char *attr)
12 {
13 int len;
14
15 if (attr)
16 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
17 major(devno), minor(devno), attr);
18 else
19 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
20 major(devno), minor(devno));
21
22 return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
23 }
24
25 int 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
37 char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
38 {
39 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
40 }
41
42 dev_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 || (size_t) 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 || (size_t) 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
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 */
106 char *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
137 int 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
142 memset(cxt, 0, sizeof(*cxt));
143 cxt->dir_fd = -1;
144
145 if (!sysfs_devno_path(devno, path, sizeof(path)))
146 goto err;
147
148 fd = open(path, O_RDONLY);
149 if (fd < 0)
150 goto err;
151 cxt->dir_path = strdup(path);
152 if (!cxt->dir_path)
153 goto err;
154 cxt->devno = devno;
155 cxt->dir_fd = fd;
156 cxt->parent = parent;
157 return 0;
158 err:
159 rc = -errno;
160 sysfs_deinit(cxt);
161 return rc;
162 }
163
164 void sysfs_deinit(struct sysfs_cxt *cxt)
165 {
166 if (!cxt)
167 return;
168
169 if (cxt->dir_fd >= 0)
170 close(cxt->dir_fd);
171 free(cxt->dir_path);
172
173 cxt->devno = 0;
174 cxt->dir_fd = -1;
175 cxt->parent = NULL;
176 cxt->dir_path = NULL;
177 }
178
179 int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
180 {
181 int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
182
183 if (rc != 0 && errno == ENOENT &&
184 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
185
186 /* Exception for "queue/<attr>". These attributes are available
187 * for parental devices only
188 */
189 return fstat_at(cxt->parent->dir_fd,
190 cxt->parent->dir_path, attr, st, 0);
191 }
192 return rc;
193 }
194
195 int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
196 {
197 struct stat st;
198
199 return sysfs_stat(cxt, attr, &st) == 0;
200 }
201
202 static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
203 {
204 int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY);
205
206 if (fd == -1 && errno == ENOENT &&
207 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
208
209 /* Exception for "queue/<attr>". These attributes are available
210 * for parental devices only
211 */
212 fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY);
213 }
214 return fd;
215 }
216
217 ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
218 char *buf, size_t bufsiz)
219 {
220 if (attr)
221 return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
222
223 /* read /sys/dev/block/<maj:min> link */
224 return readlink(cxt->dir_path, buf, bufsiz);
225 }
226
227 DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
228 {
229 DIR *dir;
230 int fd;
231
232 if (attr)
233 fd = sysfs_open(cxt, attr);
234 else
235 /* request to open root of device in sysfs (/sys/block/<dev>)
236 * -- we cannot use cxt->sysfs_fd directly, because closedir()
237 * will close this our persistent file descriptor.
238 */
239 fd = dup(cxt->dir_fd);
240
241 if (fd < 0)
242 return NULL;
243
244 dir = fdopendir(fd);
245 if (!dir) {
246 close(fd);
247 return NULL;
248 }
249 if (!attr)
250 rewinddir(dir);
251 return dir;
252 }
253
254
255 static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
256 {
257 int fd = sysfs_open(cxt, attr);
258
259 return fd < 0 ? NULL : fdopen(fd, "r");
260 }
261
262
263 static struct dirent *xreaddir(DIR *dp)
264 {
265 struct dirent *d;
266
267 while ((d = readdir(dp))) {
268 if (!strcmp(d->d_name, ".") ||
269 !strcmp(d->d_name, ".."))
270 continue;
271
272 /* blacklist here? */
273 break;
274 }
275 return d;
276 }
277
278 int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
279 {
280 char path[256];
281
282 #ifdef _DIRENT_HAVE_D_TYPE
283 if (d->d_type != DT_DIR)
284 return 0;
285 #endif
286 if (strncmp(parent_name, d->d_name, strlen(parent_name)))
287 return 0;
288
289 /* Cannot use /partition file, not supported on old sysfs */
290 snprintf(path, sizeof(path), "%s/start", d->d_name);
291
292 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
293 }
294
295 int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
296 {
297 FILE *f = sysfs_fopen(cxt, attr);
298 va_list ap;
299 int rc;
300
301 if (!f)
302 return -EINVAL;
303 va_start(ap, fmt);
304 rc = vfscanf(f, fmt, ap);
305 va_end(ap);
306
307 fclose(f);
308 return rc;
309 }
310
311
312 int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
313 {
314 int64_t x = 0;
315
316 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
317 if (res)
318 *res = x;
319 return 0;
320 }
321 return -1;
322 }
323
324 int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
325 {
326 uint64_t x = 0;
327
328 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
329 if (res)
330 *res = x;
331 return 0;
332 }
333 return -1;
334 }
335
336 int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
337 {
338 int x = 0;
339
340 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
341 if (res)
342 *res = x;
343 return 0;
344 }
345 return -1;
346 }
347
348 char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
349 {
350 char buf[1024];
351 return sysfs_scanf(cxt, attr, "%1024[^\n]", buf) == 1 ?
352 strdup(buf) : NULL;
353 }
354
355 int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
356 {
357 DIR *dir;
358 int r = 0;
359
360 if (!(dir = sysfs_opendir(cxt, attr)))
361 return 0;
362
363 while (xreaddir(dir)) r++;
364
365 closedir(dir);
366 return r;
367 }
368
369 int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
370 {
371 DIR *dir;
372 struct dirent *d;
373 int r = 0;
374
375 if (!(dir = sysfs_opendir(cxt, NULL)))
376 return 0;
377
378 while ((d = xreaddir(dir))) {
379 if (sysfs_is_partition_dirent(dir, d, devname))
380 r++;
381 }
382
383 closedir(dir);
384 return r;
385 }
386
387 /*
388 * Returns slave name if there is only one slave, otherwise returns NULL.
389 * The result should be deallocated by free().
390 */
391 char *sysfs_get_slave(struct sysfs_cxt *cxt)
392 {
393 DIR *dir;
394 struct dirent *d;
395 char *name = NULL;
396
397 if (!(dir = sysfs_opendir(cxt, "slaves")))
398 return NULL;
399
400 while ((d = xreaddir(dir))) {
401 if (name)
402 goto err; /* more slaves */
403
404 name = strdup(d->d_name);
405 }
406
407 closedir(dir);
408 return name;
409 err:
410 free(name);
411 return NULL;
412 }
413
414 /*
415 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
416 * symlinks.
417 */
418 char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
419 {
420 char *name = NULL;
421 ssize_t sz;
422
423 sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
424 if (sz < 0)
425 return NULL;
426
427 buf[sz] = '\0';
428 name = strrchr(buf, '/');
429 if (!name)
430 return NULL;
431
432 name++;
433 sz = strlen(name);
434
435 memmove(buf, name, sz + 1);
436 return buf;
437 }
438
439 #ifdef TEST_PROGRAM_SYSFS
440 #include <errno.h>
441 #include <err.h>
442 #include <stdlib.h>
443
444 int main(int argc, char *argv[])
445 {
446 struct sysfs_cxt cxt;
447 char *devname;
448 dev_t devno;
449 char path[PATH_MAX];
450 int i;
451 uint64_t u64;
452 ssize_t len;
453
454 if (argc != 2)
455 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
456
457 devname = argv[1];
458 devno = sysfs_devname_to_devno(devname, NULL);
459
460 if (!devno)
461 err(EXIT_FAILURE, "failed to read devno");
462
463 printf("NAME: %s\n", devname);
464 printf("DEVNO: %u\n", (unsigned int) devno);
465 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
466 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
467 printf("PARTITION: %s\n",
468 sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT");
469
470 sysfs_init(&cxt, devno, NULL);
471
472 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
473 if (len > 0) {
474 path[len] = '\0';
475 printf("DEVNOLINK: %s\n", path);
476 }
477
478 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
479
480 if (sysfs_read_u64(&cxt, "size", &u64))
481 printf("read SIZE failed\n");
482 else
483 printf("SIZE: %jd\n", u64);
484
485 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
486 printf("read SECTOR failed\n");
487 else
488 printf("SECTOR: %d\n", i);
489
490 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
491
492 sysfs_deinit(&cxt);
493 return EXIT_SUCCESS;
494 }
495 #endif