]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/path.c
lib/path lib/sysfs: add debug
[thirdparty/util-linux.git] / lib / path.c
CommitLineData
8148217b 1/*
b295bdb1
BS
2 * Simple functions to access files. Paths can be globally prefixed to read
3 * data from an alternative source (e.g. a /proc dump for regression tests).
8148217b 4 *
1ed21c80
KZ
5 * No copyright is claimed. This code is in the public domain; do with
6 * it what you wish.
8148217b 7
1ed21c80
KZ
8 * Written by Karel Zak <kzak@redhat.com> [February 2018]
9 */
8148217b
HC
10#include <stdarg.h>
11#include <string.h>
12#include <unistd.h>
13#include <stdio.h>
37a5c7ee 14#include <inttypes.h>
8148217b
HC
15#include <errno.h>
16
1ed21c80
KZ
17#include "c.h"
18#include "fileutils.h"
e12c9866 19#include "all-io.h"
8148217b 20#include "path.h"
bcf445fd
KZ
21#include "debug.h"
22
23/*
24 * Debug stuff (based on include/debug.h)
25 */
26static UL_DEBUG_DEFINE_MASK(ulpath);
27UL_DEBUG_DEFINE_MASKNAMES(ulpath) = UL_DEBUG_EMPTY_MASKNAMES;
28
29#define ULPATH_DEBUG_INIT (1 << 1)
30#define ULPATH_DEBUG_CXT (1 << 2)
31
32#define DBG(m, x) __UL_DBG(ulpath, ULPATH_DEBUG_, m, x)
33#define ON_DBG(m, x) __UL_DBG_CALL(ulpath, ULPATH_DEBUG_, m, x)
34
35#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpath)
36#include "debugobj.h"
37
38void ul_path_init_debug(void)
39{
40 if (ulpath_debug_mask)
41 return;
42 __UL_INIT_DEBUG_FROM_ENV(ulpath, ULPATH_DEBUG_, 0, ULPATH_DEBUG);
43}
8148217b 44
1ed21c80
KZ
45struct path_cxt *ul_new_path(const char *dir)
46{
47 struct path_cxt *pc = calloc(1, sizeof(*pc));
48
49 if (!pc)
50 return NULL;
51
bcf445fd
KZ
52 DBG(CXT, ul_debugobj(pc, "alloc"));
53
1ed21c80
KZ
54 pc->refcount = 1;
55 pc->dir_fd = -1;
56
57 if (dir) {
58 pc->dir_path = strdup(dir);
59 if (!pc->dir_path)
60 goto fail;
61 }
62 return pc;
63fail:
64 ul_unref_path(pc);
65 return NULL;
66}
67
68void ul_ref_path(struct path_cxt *pc)
69{
70 if (pc)
71 pc->refcount++;
72}
8148217b 73
1ed21c80 74void ul_unref_path(struct path_cxt *pc)
d182565b 75{
1ed21c80
KZ
76 if (!pc)
77 return;
d182565b 78
1ed21c80
KZ
79 pc->refcount--;
80
81 if (pc->refcount <= 0) {
bcf445fd 82 DBG(CXT, ul_debugobj(pc, "dealloc"));
1ed21c80
KZ
83 if (pc->dialect)
84 pc->free_dialect(pc);
85 if (pc->dir_fd >= 0)
86 close(pc->dir_fd);
87 free(pc->dir_path);
88 free(pc->prefix);
89 free(pc);
d182565b 90 }
1ed21c80
KZ
91}
92
93int ul_path_set_prefix(struct path_cxt *pc, const char *prefix)
94{
95 char *p = NULL;
96
97 assert(pc->dir_fd < 0);
98
99 if (prefix) {
100 p = strdup(prefix);
101 if (!p)
102 return -ENOMEM;
103 }
104
105 free(pc->prefix);
106 pc->prefix = p;
bcf445fd 107 DBG(CXT, ul_debugobj(pc, "new prefix: '%s'", p));
d182565b
KZ
108 return 0;
109}
110
1ed21c80 111const char *ul_path_get_prefix(struct path_cxt *pc)
8148217b 112{
1ed21c80
KZ
113 return pc ? pc->prefix : NULL;
114}
115
116int ul_path_set_dir(struct path_cxt *pc, const char *dir)
117{
118 char *p = NULL;
119
120 if (dir) {
121 p = strdup(dir);
122 if (!p)
123 return -ENOMEM;
124 }
f567220b 125
1ed21c80
KZ
126 if (pc->dir_fd >= 0) {
127 close(pc->dir_fd);
128 pc->dir_fd = -1;
129 }
130
131 free(pc->dir_path);
132 pc->dir_path = p;
bcf445fd 133 DBG(CXT, ul_debugobj(pc, "new dir: '%s'", p));
1ed21c80
KZ
134 return 0;
135}
136
137const char *ul_path_get_dir(struct path_cxt *pc)
138{
139 return pc ? pc->dir_path : NULL;
140}
141
142int ul_path_set_dialect(struct path_cxt *pc, void *data, void free_data(struct path_cxt *))
143{
144 pc->dialect = data;
145 pc->free_dialect = free_data;
bcf445fd 146 DBG(CXT, ul_debugobj(pc, "new dialect"));
1ed21c80
KZ
147 return 0;
148}
149
150void *ul_path_get_dialect(struct path_cxt *pc)
151{
152 return pc ? pc->dialect : NULL;
153}
154
155int ul_path_set_enoent_redirect(struct path_cxt *pc, int (*func)(struct path_cxt *, const char *, int *))
156{
157 pc->redirect_on_enoent = func;
158 return 0;
159}
160
161static const char *get_absdir(struct path_cxt *pc)
162{
163 int rc;
164
165 if (!pc->prefix)
166 return pc->dir_path;
167
168 rc = snprintf(pc->path_buffer, sizeof(pc->path_buffer), "%s/%s", pc->prefix, pc->dir_path);
f567220b
RM
169 if (rc < 0)
170 return NULL;
1ed21c80 171 if ((size_t)rc >= sizeof(pc->path_buffer)) {
f567220b
RM
172 errno = ENAMETOOLONG;
173 return NULL;
174 }
1ed21c80
KZ
175
176 return pc->path_buffer;
177}
178
179
180int ul_path_get_dirfd(struct path_cxt *pc)
181{
182 assert(pc);
183 assert(pc->dir_path);
184
185 if (pc->dir_fd < 0) {
186 const char *path = get_absdir(pc);
187 if (!path)
188 return -errno;
bcf445fd
KZ
189
190 DBG(CXT, ul_debugobj(pc, "opening dir: '%s'", path));
1ed21c80
KZ
191 pc->dir_fd = open(path, O_RDONLY|O_CLOEXEC);
192 }
193
194 return pc->dir_fd;
195}
196
197static const char *ul_path_mkpath(struct path_cxt *pc, const char *path, va_list ap)
198{
199 int rc = vsnprintf(pc->path_buffer, sizeof(pc->path_buffer), path, ap);
200
201 if (rc < 0)
202 return NULL;
203
204 if ((size_t)rc >= sizeof(pc->path_buffer)) {
205 errno = ENAMETOOLONG;
206 return NULL;
207 }
208
209 return pc->path_buffer;
210}
211
212int ul_path_access(struct path_cxt *pc, int mode, const char *path)
213{
214 int dir, rc;
215
216 dir = ul_path_get_dirfd(pc);
217 if (dir < 0)
218 return dir;
219
220 rc = faccessat(dir, path, mode, 0);
221
222 if (rc && errno == ENOENT
223 && pc->redirect_on_enoent
224 && pc->redirect_on_enoent(pc, path, &dir) == 0)
225 rc = faccessat(dir, path, mode, 0);
226
227 return rc;
8148217b
HC
228}
229
1ed21c80 230int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...)
dd3bc51a 231{
1ed21c80 232 va_list ap;
dd3bc51a 233 const char *p;
1ed21c80
KZ
234
235 va_start(ap, path);
236 p = ul_path_mkpath(pc, path, ap);
237 va_end(ap);
238
239 return ul_path_access(pc, mode, p);
240}
241
242int ul_path_open(struct path_cxt *pc, int flags, const char *path)
243{
244 int dir, fd;
245
246 dir = ul_path_get_dirfd(pc);
247 if (dir < 0)
248 return dir;
249
250 fd = openat(dir, path, flags);
251
252 if (fd < 0 && errno == ENOENT
253 && pc->redirect_on_enoent
254 && pc->redirect_on_enoent(pc, path, &dir) == 0)
255 fd = openat(dir, path, flags);
256
bcf445fd 257 DBG(CXT, ul_debugobj(pc, "opening '%s'", path));
1ed21c80
KZ
258 return fd;
259}
260
261int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap)
262{
263 const char *p;
264
265 p = ul_path_mkpath(pc, path, ap);
266 if (!p)
267 return -errno;
268
269 return ul_path_open(pc, flags, p);
270}
271
272int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...)
273{
dd3bc51a 274 va_list ap;
1ed21c80 275 int rc;
dd3bc51a
KZ
276
277 va_start(ap, path);
1ed21c80 278 rc = ul_path_vopenf(pc, flags, path, ap);
dd3bc51a
KZ
279 va_end(ap);
280
1ed21c80 281 return rc;
dd3bc51a
KZ
282}
283
1ed21c80
KZ
284/*
285 * Maybe stupid, but good enough ;-)
286 */
287static int mode2flags(const char *mode)
8148217b 288{
1ed21c80
KZ
289 int flags = 0;
290 const char *p;
291
292 for (p = mode; p && *p; p++) {
293 if (*p == 'r' && *(p + 1) == '+')
294 flags |= O_RDWR;
295 else if (*p == 'r')
296 flags |= O_RDONLY;
297
298 else if (*p == 'w' && *(p + 1) == '+')
299 flags |= O_RDWR | O_TRUNC;
300 else if (*p == 'w')
301 flags |= O_WRONLY | O_TRUNC;
302
303 else if (*p == 'a' && *(p + 1) == '+')
304 flags |= O_RDWR | O_APPEND;
305 else if (*p == 'a')
306 flags |= O_WRONLY | O_APPEND;
307#ifdef O_CLOEXEC
308 else if (*p == *UL_CLOEXECSTR)
309 flags |= O_CLOEXEC;
310#endif
311 }
312
313 return flags;
314}
315
316FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path)
317{
318 int flags = mode2flags(mode);
319 int fd = ul_path_open(pc, flags, path);
320
321 if (fd < 0)
322 return NULL;
323
324 return fdopen(fd, mode);
325}
326
327
328FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap)
329{
330 const char *p;
331
332 p = ul_path_mkpath(pc, path, ap);
f567220b 333 if (!p)
1ed21c80 334 return NULL;
8148217b 335
1ed21c80
KZ
336 return ul_path_fopen(pc, mode, p);
337}
338
339FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...)
340{
341 FILE *f;
342 va_list ap;
343
344 va_start(ap, path);
345 f = ul_path_vfopenf(pc, mode, path, ap);
346 va_end(ap);
f567220b 347
8148217b
HC
348 return f;
349}
350
1ed21c80
KZ
351/*
352 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
353 * to the directory addressed by @pc.
354 */
355DIR *ul_path_opendir(struct path_cxt *pc, const char *path)
9bc2b4b1 356{
1ed21c80
KZ
357 DIR *dir;
358 int fd = -1;
359
360 if (path)
361 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
bcf445fd
KZ
362 else if (pc->dir_path) {
363 DBG(CXT, ul_debugobj(pc, "duplicate dir path"));
1ed21c80 364 fd = dup_fd_cloexec(ul_path_get_dirfd(pc), STDERR_FILENO + 1);
bcf445fd 365 }
1ed21c80
KZ
366
367 if (fd < 0)
368 return NULL;
369
370 dir = fdopendir(fd);
371 if (!dir) {
372 close(fd);
373 return NULL;
374 }
375 if (!path)
376 rewinddir(dir);
377 return dir;
378}
379
380
381/*
382 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
383 * to the directory addressed by @pc.
384 */
385DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap)
386{
387 const char *p;
388
389 p = ul_path_mkpath(pc, path, ap);
390 if (!p)
391 return NULL;
392
393 return ul_path_opendir(pc, p);
394}
395
396/*
397 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
398 * to the directory addressed by @pc.
399 */
400DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...)
401{
402 va_list ap;
403 DIR *dir;
404
405 va_start(ap, path);
406 dir = ul_path_vopendirf(pc, path, ap);
407 va_end(ap);
408
409 return dir;
410}
411
412/*
413 * If @path is NULL then readlink is called on @pc directory.
414 */
415ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path)
416{
417 int dirfd;
418
419 if (!path) {
420 const char *p = get_absdir(pc);
421 if (!p)
422 return -errno;
423 return readlink(p, buf, bufsiz);
424 }
425
426 dirfd = ul_path_get_dirfd(pc);
427 if (dirfd < 0)
428 return dirfd;
429
430 return readlinkat(dirfd, path, buf, bufsiz);
431}
432
433/*
434 * If @path is NULL then readlink is called on @pc directory.
435 */
436ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...)
437{
438 const char *p;
439 va_list ap;
440
441 va_start(ap, path);
442 p = ul_path_mkpath(pc, path, ap);
443 va_end(ap);
444
445 if (!p)
446 return -errno;
447
448 return ul_path_readlink(pc, buf, bufsiz, p);
449}
450
451int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path)
452{
453 int rc, errsv;
9bc2b4b1 454 int fd;
1ed21c80
KZ
455
456 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
457 if (fd < 0)
458 return -errno;
459
460 rc = read_all(fd, buf, len);
461
462 errsv = errno;
463 close(fd);
464 errno = errsv;
465 return rc;
466}
467
468int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap)
469{
470 const char *p;
471
472 p = ul_path_mkpath(pc, path, ap);
f567220b 473 if (!p)
1ed21c80 474 return -EINVAL;
9bc2b4b1 475
1ed21c80
KZ
476 return ul_path_read(pc, buf, len, p);
477}
f567220b 478
1ed21c80
KZ
479int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...)
480{
481 va_list ap;
482 int rc;
483
484 va_start(ap, path);
485 rc = ul_path_vreadf(pc, buf, len, path, ap);
486 va_end(ap);
487
488 return rc;
489}
490
491
492/*
493 * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ
494 * (send patch if you need something bigger;-)
495 *
496 */
497int ul_path_read_string(struct path_cxt *pc, char **str, const char *path)
498{
499 char buf[BUFSIZ];
500 int rc;
501
502 *str = NULL;
503
504 rc = ul_path_read(pc, buf, sizeof(buf) - 1, path);
505 if (rc < 0 || !str)
506 return rc;;
507
508 buf[rc] = '\0';
509 *str = strdup(buf);
510 if (!*str)
511 rc = -ENOMEM;
512
513 return rc;
9bc2b4b1
HC
514}
515
1ed21c80 516int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...)
8148217b 517{
1ed21c80 518 const char *p;
8148217b
HC
519 va_list ap;
520
521 va_start(ap, path);
1ed21c80 522 p = ul_path_mkpath(pc, path, ap);
8148217b
HC
523 va_end(ap);
524
1ed21c80
KZ
525 if (!p)
526 return -EINVAL;
527
528 return ul_path_read_string(pc, str, p);
8148217b
HC
529}
530
1ed21c80 531int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...)
8148217b 532{
1ed21c80
KZ
533 FILE *f;
534 va_list fmt_ap;
535 int rc;
536
537 f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path);
538 if (!f)
539 return -EINVAL;
540
541 va_start(fmt_ap, fmt);
542 rc = vfscanf(f, fmt, fmt_ap);
543 va_end(fmt_ap);
544
545 fclose(f);
546 return rc;
547}
548
549int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
550{
551 FILE *f;
552 va_list fmt_ap;
553 int rc;
554
555 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
556 if (!f)
557 return -EINVAL;
558
559 va_start(fmt_ap, fmt);
560 rc = vfscanf(f, fmt, fmt_ap);
561 va_end(fmt_ap);
562
563 fclose(f);
564 return rc;
565}
566
567
568int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path)
569{
570 int64_t x = 0;
571 int rc;
572
573 rc = ul_path_scanf(pc, path, "%"SCNd64, &x);
574 if (rc != 1)
575 return -1;
576 if (res)
577 *res = x;
578 return 0;
579}
580
581int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
582{
583 const char *p;
8148217b
HC
584 va_list ap;
585
586 va_start(ap, path);
1ed21c80 587 p = ul_path_mkpath(pc, path, ap);
8148217b
HC
588 va_end(ap);
589
1ed21c80
KZ
590 if (!p)
591 return -EINVAL;;
592
593 return ul_path_read_s64(pc, res, p);
594}
595
596int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path)
597{
598 uint64_t x = 0;
599 int rc;
8148217b 600
1ed21c80
KZ
601 rc = ul_path_scanf(pc, path, "%"SCNu64, &x);
602 if (rc != 1)
603 return -1;
604 if (res)
605 *res = x;
606 return 0;
8148217b
HC
607}
608
1ed21c80 609int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
8148217b 610{
1ed21c80 611 const char *p;
8148217b 612 va_list ap;
8148217b
HC
613
614 va_start(ap, path);
1ed21c80 615 p = ul_path_mkpath(pc, path, ap);
8148217b
HC
616 va_end(ap);
617
1ed21c80
KZ
618 if (!p)
619 return -EINVAL;
620
621 return ul_path_read_u64(pc, res, p);
8148217b
HC
622}
623
1ed21c80 624int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path)
37a5c7ee 625{
1ed21c80
KZ
626 int rc, x = 0;
627
628 rc = ul_path_scanf(pc, path, "%d", &x);
629 if (rc != 1)
630 return -1;
631 if (res)
632 *res = x;
633 return 0;
634}
635
636int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...)
637{
638 const char *p;
37a5c7ee 639 va_list ap;
37a5c7ee
KZ
640
641 va_start(ap, path);
1ed21c80 642 p = ul_path_mkpath(pc, path, ap);
37a5c7ee
KZ
643 va_end(ap);
644
1ed21c80
KZ
645 if (!p)
646 return -EINVAL;
647
648 return ul_path_read_s32(pc, res, p);
649}
650
651int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path)
652{
653 int rc;
654 unsigned int x;
655
656 rc = ul_path_scanf(pc, path, "%u", &x);
657 if (rc != 1)
658 return -1;
659 if (res)
660 *res = x;
661 return 0;
662}
663
664int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...)
665{
666 const char *p;
667 va_list ap;
668
669 va_start(ap, path);
670 p = ul_path_mkpath(pc, path, ap);
671 va_end(ap);
672
673 if (!p)
674 return -EINVAL;
675
676 return ul_path_read_u32(pc, res, p);
37a5c7ee
KZ
677}
678
1ed21c80 679int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path)
9bc2b4b1 680{
1ed21c80
KZ
681 int rc, maj, min;
682
683 rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min);
684 if (rc != 2)
685 return -1;
686 if (res)
687 *res = makedev(maj, min);
688 return 0;
689}
690
691int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
692{
693 const char *p;
9bc2b4b1
HC
694 va_list ap;
695
696 va_start(ap, path);
1ed21c80 697 p = ul_path_mkpath(pc, path, ap);
9bc2b4b1 698 va_end(ap);
1ed21c80
KZ
699
700 if (!p)
701 return -EINVAL;
702
703 return ul_path_read_majmin(pc, res, p);
704}
705
706int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path)
707{
708 int rc, errsv;
709 int fd;
710
711 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
712 if (fd < 0)
713 return -errno;
714
715 rc = write_all(fd, str, strlen(str));
716
717 errsv = errno;
9bc2b4b1 718 close(fd);
1ed21c80
KZ
719 errno = errsv;
720 return rc;
9bc2b4b1
HC
721}
722
1ed21c80 723int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
8148217b 724{
1ed21c80 725 const char *p;
8148217b 726 va_list ap;
1ed21c80
KZ
727
728 va_start(ap, path);
729 p = ul_path_mkpath(pc, path, ap);
730 va_end(ap);
731
732 if (!p)
733 return -EINVAL;
734
735 return ul_path_write_string(pc, str, p);
736}
737
738int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path)
739{
740 char buf[sizeof(stringify_value(ULLONG_MAX))];
741 int rc, errsv;
742 int fd, len;
743
744 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
745 if (fd < 0)
746 return -errno;
747
748 len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
749 if (len < 0 || (size_t) len >= sizeof(buf))
750 rc = len < 0 ? -errno : -E2BIG;
751 else
752 rc = write_all(fd, buf, len);
753
754 errsv = errno;
755 close(fd);
756 errno = errsv;
757 return rc;
758}
759
760int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
761{
8148217b 762 const char *p;
1ed21c80 763 va_list ap;
8148217b
HC
764
765 va_start(ap, path);
1ed21c80 766 p = ul_path_mkpath(pc, path, ap);
8148217b
HC
767 va_end(ap);
768
1ed21c80
KZ
769 if (!p)
770 return -EINVAL;
771
772 return ul_path_write_u64(pc, num, p);
773
8148217b
HC
774}
775
1ed21c80
KZ
776static struct dirent *xreaddir(DIR *dp)
777{
778 struct dirent *d;
779
780 while ((d = readdir(dp))) {
781 if (!strcmp(d->d_name, ".") ||
782 !strcmp(d->d_name, ".."))
783 continue;
784
785 /* blacklist here? */
786 break;
787 }
788 return d;
789}
e21e6d26 790
1ed21c80 791int ul_path_count_dirents(struct path_cxt *pc, const char *path)
8148217b 792{
1ed21c80
KZ
793 DIR *dir;
794 int r = 0;
795
796 dir = ul_path_opendir(pc, path);
797 if (!dir)
798 return 0;
799
800 while (xreaddir(dir)) r++;
801
802 closedir(dir);
803 return r;
804}
805
806int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
807{
808 const char *p;
809 va_list ap;
810
811 va_start(ap, path);
812 p = ul_path_mkpath(pc, path, ap);
813 va_end(ap);
814
815 if (!p)
816 return 0;
817
818 return ul_path_count_dirents(pc, p);
819}
820
821#ifdef HAVE_CPU_SET_T
822static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
823{
824 FILE *f;
8148217b
HC
825 size_t setsize, len = maxcpus * 7;
826 char buf[len];
827
1ed21c80
KZ
828 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
829 if (!f)
830 return -errno;
8148217b 831
1ed21c80
KZ
832 if (!fgets(buf, len, f))
833 return -errno;
834 fclose(f);
8148217b
HC
835
836 len = strlen(buf);
837 if (buf[len - 1] == '\n')
838 buf[len - 1] = '\0';
839
1ed21c80
KZ
840 *set = cpuset_alloc(maxcpus, &setsize, NULL);
841 if (!*set)
842 return -ENOMEM;
8148217b
HC
843
844 if (islist) {
1ed21c80
KZ
845 if (cpulist_parse(buf, *set, setsize, 0))
846 return -EINVAL;
8148217b 847 } else {
1ed21c80
KZ
848 if (cpumask_parse(buf, *set, setsize))
849 return -EINVAL;
8148217b 850 }
1ed21c80 851 return 0;
8148217b
HC
852}
853
1ed21c80 854int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
8148217b
HC
855{
856 va_list ap;
1ed21c80 857 int rc = 0;
8148217b
HC
858
859 va_start(ap, path);
1ed21c80 860 rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
8148217b
HC
861 va_end(ap);
862
1ed21c80 863 return rc;
8148217b
HC
864}
865
1ed21c80 866int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
8148217b
HC
867{
868 va_list ap;
1ed21c80 869 int rc = 0;
8148217b
HC
870
871 va_start(ap, path);
1ed21c80 872 rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
8148217b
HC
873 va_end(ap);
874
1ed21c80 875 return rc;
8148217b 876}
1ed21c80 877
4a04d7f3 878#endif /* HAVE_CPU_SET_T */
1ed21c80
KZ
879
880
881#ifdef TEST_PROGRAM_PATH
882#include <getopt.h>
883
884static void __attribute__((__noreturn__)) usage(void)
885{
886 fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
887 fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
888
889 fputs(" Commands:\n", stdout);
890 fputs(" read-u64 <file> read uint64_t from file\n", stdout);
891 fputs(" read-s64 <file> read int64_t from file\n", stdout);
892 fputs(" read-u32 <file> read uint32_t from file\n", stdout);
893 fputs(" read-s32 <file> read int32_t from file\n", stdout);
894 fputs(" read-string <file> read string from file\n", stdout);
895 fputs(" read-majmin <file> read devno from file\n", stdout);
896 fputs(" read-link <file> read symlink\n", stdout);
897 fputs(" write-string <file> <str> write string from file\n", stdout);
898 fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
899
900 exit(EXIT_SUCCESS);
901}
902
903int main(int argc, char *argv[])
904{
905 int c;
906 const char *prefix = NULL, *dir, *file, *command;
907 struct path_cxt *pc = NULL;
908
909 static const struct option longopts[] = {
910 { "prefix", 1, NULL, 'p' },
911 { "help", 0, NULL, 'h' },
912 { NULL, 0, NULL, 0 },
913 };
914
915 while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
916 switch(c) {
917 case 'p':
918 prefix = optarg, "failed to parse range start";
919 break;
920 case 'h':
921 usage();
922 break;
923 default:
924 err(EXIT_FAILURE, "try --help");
925 }
926 }
927
928 if (optind == argc)
929 errx(EXIT_FAILURE, "<dir> not defined");
930 dir = argv[optind++];
931
bcf445fd
KZ
932 ul_path_init_debug();
933
1ed21c80
KZ
934 pc = ul_new_path(dir);
935 if (!pc)
936 err(EXIT_FAILURE, "failed to initialize path context");
937 if (prefix)
938 ul_path_set_prefix(pc, prefix);
939
940 if (optind == argc)
941 errx(EXIT_FAILURE, "<command> not defined");
942 command = argv[optind++];
943
944 if (strcmp(command, "read-u32") == 0) {
945 uint32_t res;
946
947 if (optind == argc)
948 errx(EXIT_FAILURE, "<file> not defined");
949 file = argv[optind++];
950
951 if (ul_path_read_u32(pc, &res, file) != 0)
952 err(EXIT_FAILURE, "read u64 failed");
953 printf("read: %s: %u\n", file, res);
954
955 if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
956 err(EXIT_FAILURE, "readf u64 failed");
957 printf("readf: %s: %u\n", file, res);
958
959 } else if (strcmp(command, "read-s32") == 0) {
960 int32_t res;
961
962 if (optind == argc)
963 errx(EXIT_FAILURE, "<file> not defined");
964 file = argv[optind++];
965
966 if (ul_path_read_s32(pc, &res, file) != 0)
967 err(EXIT_FAILURE, "read u64 failed");
968 printf("read: %s: %d\n", file, res);
969
970 if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
971 err(EXIT_FAILURE, "readf u64 failed");
972 printf("readf: %s: %d\n", file, res);
973
974 } else if (strcmp(command, "read-u64") == 0) {
975 uint64_t res;
976
977 if (optind == argc)
978 errx(EXIT_FAILURE, "<file> not defined");
979 file = argv[optind++];
980
981 if (ul_path_read_u64(pc, &res, file) != 0)
982 err(EXIT_FAILURE, "read u64 failed");
983 printf("read: %s: %" PRIu64 "\n", file, res);
984
985 if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
986 err(EXIT_FAILURE, "readf u64 failed");
987 printf("readf: %s: %" PRIu64 "\n", file, res);
988
989 } else if (strcmp(command, "read-s64") == 0) {
990 int64_t res;
991
992 if (optind == argc)
993 errx(EXIT_FAILURE, "<file> not defined");
994 file = argv[optind++];
995
996 if (ul_path_read_s64(pc, &res, file) != 0)
997 err(EXIT_FAILURE, "read u64 failed");
998 printf("read: %s: %" PRIu64 "\n", file, res);
999
1000 if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
1001 err(EXIT_FAILURE, "readf u64 failed");
1002 printf("readf: %s: %" PRIu64 "\n", file, res);
1003
1004 } else if (strcmp(command, "read-majmin") == 0) {
1005 dev_t res;
1006
1007 if (optind == argc)
1008 errx(EXIT_FAILURE, "<file> not defined");
1009 file = argv[optind++];
1010
1011 if (ul_path_read_majmin(pc, &res, file) != 0)
1012 err(EXIT_FAILURE, "read maj:min failed");
1013 printf("read: %s: %d\n", file, (int) res);
1014
1015 if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
1016 err(EXIT_FAILURE, "readf maj:min failed");
1017 printf("readf: %s: %d\n", file, (int) res);
1018
1019 } else if (strcmp(command, "read-string") == 0) {
1020 char *res;
1021
1022 if (optind == argc)
1023 errx(EXIT_FAILURE, "<file> not defined");
1024 file = argv[optind++];
1025
1026 if (ul_path_read_string(pc, &res, file) != 0)
1027 err(EXIT_FAILURE, "read u64 failed");
1028 printf("read: %s: %s\n", file, res);
1029
1030 if (ul_path_readf_string(pc, &res, "%s", file) != 0)
1031 err(EXIT_FAILURE, "readf u64 failed");
1032 printf("readf: %s: %s\n", file, res);
1033
1034 } else if (strcmp(command, "read-link") == 0) {
1035 char res[PATH_MAX];
1036
1037 if (optind == argc)
1038 errx(EXIT_FAILURE, "<file> not defined");
1039 file = argv[optind++];
1040
1041 if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
1042 err(EXIT_FAILURE, "read symlink failed");
1043 printf("read: %s: %s\n", file, res);
1044
1045 if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
1046 err(EXIT_FAILURE, "readf symlink failed");
1047 printf("readf: %s: %s\n", file, res);
1048
1049 } else if (strcmp(command, "write-string") == 0) {
1050 char *str;
1051
1052 if (optind + 1 == argc)
1053 errx(EXIT_FAILURE, "<file> <string> not defined");
1054 file = argv[optind++];
1055 str = argv[optind++];
1056
1057 if (ul_path_write_string(pc, str, file) != 0)
1058 err(EXIT_FAILURE, "write string failed");
1059 if (ul_path_writef_string(pc, str, "%s", file) != 0)
1060 err(EXIT_FAILURE, "writef string failed");
1061
1062 } else if (strcmp(command, "write-u64") == 0) {
1063 uint64_t num;
1064
1065 if (optind + 1 == argc)
1066 errx(EXIT_FAILURE, "<file> <num> not defined");
1067 file = argv[optind++];
1068 num = strtoumax(argv[optind++], NULL, 0);
1069
1070 if (ul_path_write_u64(pc, num, file) != 0)
1071 err(EXIT_FAILURE, "write u64 failed");
1072 if (ul_path_writef_u64(pc, num, "%s", file) != 0)
1073 err(EXIT_FAILURE, "writef u64 failed");
1074 }
1075
1076 ul_unref_path(pc);
1077 return EXIT_SUCCESS;
1078}
1079#endif /* TEST_PROGRAM_PATH */
1080