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