2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
31 #include "alloc-util.h"
38 #include "hexdecoct.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "process-util.h"
45 #include "random-util.h"
46 #include "stdio-util.h"
47 #include "string-util.h"
49 #include "time-util.h"
50 #include "umask-util.h"
53 #define READ_FULL_BYTES_MAX (4U*1024U*1024U)
55 int write_string_stream_ts(
58 WriteStringFileFlags flags
,
59 struct timespec
*ts
) {
65 if (!(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
) && !endswith(line
, "\n"))
69 struct timespec twice
[2] = {*ts
, *ts
};
71 if (futimens(fileno(f
), twice
) < 0)
75 if (flags
& WRITE_STRING_FILE_SYNC
)
76 return fflush_sync_and_check(f
);
78 return fflush_and_check(f
);
81 static int write_string_file_atomic(
84 WriteStringFileFlags flags
,
85 struct timespec
*ts
) {
87 _cleanup_fclose_
FILE *f
= NULL
;
88 _cleanup_free_
char *p
= NULL
;
94 r
= fopen_temporary(fn
, &f
, &p
);
98 (void) fchmod_umask(fileno(f
), 0644);
100 r
= write_string_stream_ts(f
, line
, flags
, ts
);
104 if (rename(p
, fn
) < 0) {
116 int write_string_file_ts(
119 WriteStringFileFlags flags
,
120 struct timespec
*ts
) {
122 _cleanup_fclose_
FILE *f
= NULL
;
128 /* We don't know how to verify whether the file contents was already on-disk. */
129 assert(!((flags
& WRITE_STRING_FILE_VERIFY_ON_FAILURE
) && (flags
& WRITE_STRING_FILE_SYNC
)));
131 if (flags
& WRITE_STRING_FILE_ATOMIC
) {
132 assert(flags
& WRITE_STRING_FILE_CREATE
);
134 r
= write_string_file_atomic(fn
, line
, flags
, ts
);
142 if (flags
& WRITE_STRING_FILE_CREATE
) {
151 /* We manually build our own version of fopen(..., "we") that
152 * works without O_CREAT */
153 fd
= open(fn
, O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
159 f
= fdopen(fd
, "we");
167 r
= write_string_stream_ts(f
, line
, flags
, ts
);
174 if (!(flags
& WRITE_STRING_FILE_VERIFY_ON_FAILURE
))
179 /* OK, the operation failed, but let's see if the right
180 * contents in place already. If so, eat up the error. */
182 q
= verify_file(fn
, line
, !(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
));
189 int read_one_line_file(const char *fn
, char **line
) {
190 _cleanup_fclose_
FILE *f
= NULL
;
191 char t
[LINE_MAX
], *c
;
200 if (!fgets(t
, sizeof(t
), f
)) {
203 return errno
> 0 ? -errno
: -EIO
;
217 int verify_file(const char *fn
, const char *blob
, bool accept_extra_nl
) {
218 _cleanup_fclose_
FILE *f
= NULL
;
219 _cleanup_free_
char *buf
= NULL
;
227 if (accept_extra_nl
&& endswith(blob
, "\n"))
228 accept_extra_nl
= false;
230 buf
= malloc(l
+ accept_extra_nl
+ 1);
238 /* We try to read one byte more than we need, so that we know whether we hit eof */
240 k
= fread(buf
, 1, l
+ accept_extra_nl
+ 1, f
);
242 return errno
> 0 ? -errno
: -EIO
;
244 if (k
!= l
&& k
!= l
+ accept_extra_nl
)
246 if (memcmp(buf
, blob
, l
) != 0)
248 if (k
> l
&& buf
[l
] != '\n')
254 int read_full_stream(FILE *f
, char **contents
, size_t *size
) {
256 _cleanup_free_
char *buf
= NULL
;
262 if (fstat(fileno(f
), &st
) < 0)
267 if (S_ISREG(st
.st_mode
)) {
270 if (st
.st_size
> READ_FULL_BYTES_MAX
)
273 /* Start with the right file size, but be prepared for files from /proc which generally report a file
274 * size of 0. Note that we increase the size to read here by one, so that the first read attempt
275 * already makes us notice the EOF. */
285 t
= realloc(buf
, n
+ 1);
291 k
= fread(buf
+ l
, 1, n
- l
, f
);
296 return errno
> 0 ? -errno
: -EIO
;
301 /* We aren't expecting fread() to return a short read outside
302 * of (error && eof), assert buffer is full and enlarge buffer.
307 if (n
>= READ_FULL_BYTES_MAX
)
310 n
= MIN(n
* 2, READ_FULL_BYTES_MAX
);
315 buf
= NULL
; /* do not free */
323 int read_full_file(const char *fn
, char **contents
, size_t *size
) {
324 _cleanup_fclose_
FILE *f
= NULL
;
333 return read_full_stream(f
, contents
, size
);
336 static int parse_env_file_internal(
340 int (*push
) (const char *filename
, unsigned line
,
341 const char *key
, char *value
, void *userdata
, int *n_pushed
),
345 _cleanup_free_
char *contents
= NULL
, *key
= NULL
;
346 size_t key_alloc
= 0, n_key
= 0, value_alloc
= 0, n_value
= 0, last_value_whitespace
= (size_t) -1, last_key_whitespace
= (size_t) -1;
347 char *p
, *value
= NULL
;
358 SINGLE_QUOTE_VALUE_ESCAPE
,
360 DOUBLE_QUOTE_VALUE_ESCAPE
,
368 r
= read_full_stream(f
, &contents
, NULL
);
370 r
= read_full_file(fname
, &contents
, NULL
);
374 for (p
= contents
; *p
; p
++) {
380 if (strchr(COMMENTS
, c
))
382 else if (!strchr(WHITESPACE
, c
)) {
384 last_key_whitespace
= (size_t) -1;
386 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
396 if (strchr(newline
, c
)) {
400 } else if (c
== '=') {
402 last_value_whitespace
= (size_t) -1;
404 if (!strchr(WHITESPACE
, c
))
405 last_key_whitespace
= (size_t) -1;
406 else if (last_key_whitespace
== (size_t) -1)
407 last_key_whitespace
= n_key
;
409 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
420 if (strchr(newline
, c
)) {
428 /* strip trailing whitespace from key */
429 if (last_key_whitespace
!= (size_t) -1)
430 key
[last_key_whitespace
] = 0;
432 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
438 value_alloc
= n_value
= 0;
440 } else if (c
== '\'')
441 state
= SINGLE_QUOTE_VALUE
;
443 state
= DOUBLE_QUOTE_VALUE
;
445 state
= VALUE_ESCAPE
;
446 else if (!strchr(WHITESPACE
, c
)) {
449 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
454 value
[n_value
++] = c
;
460 if (strchr(newline
, c
)) {
469 /* Chomp off trailing whitespace from value */
470 if (last_value_whitespace
!= (size_t) -1)
471 value
[last_value_whitespace
] = 0;
473 /* strip trailing whitespace from key */
474 if (last_key_whitespace
!= (size_t) -1)
475 key
[last_key_whitespace
] = 0;
477 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
483 value_alloc
= n_value
= 0;
485 } else if (c
== '\\') {
486 state
= VALUE_ESCAPE
;
487 last_value_whitespace
= (size_t) -1;
489 if (!strchr(WHITESPACE
, c
))
490 last_value_whitespace
= (size_t) -1;
491 else if (last_value_whitespace
== (size_t) -1)
492 last_value_whitespace
= n_value
;
494 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
499 value
[n_value
++] = c
;
507 if (!strchr(newline
, c
)) {
508 /* Escaped newlines we eat up entirely */
509 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
514 value
[n_value
++] = c
;
518 case SINGLE_QUOTE_VALUE
:
522 state
= SINGLE_QUOTE_VALUE_ESCAPE
;
524 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
529 value
[n_value
++] = c
;
534 case SINGLE_QUOTE_VALUE_ESCAPE
:
535 state
= SINGLE_QUOTE_VALUE
;
537 if (!strchr(newline
, c
)) {
538 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
543 value
[n_value
++] = c
;
547 case DOUBLE_QUOTE_VALUE
:
551 state
= DOUBLE_QUOTE_VALUE_ESCAPE
;
553 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
558 value
[n_value
++] = c
;
563 case DOUBLE_QUOTE_VALUE_ESCAPE
:
564 state
= DOUBLE_QUOTE_VALUE
;
566 if (!strchr(newline
, c
)) {
567 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
572 value
[n_value
++] = c
;
578 state
= COMMENT_ESCAPE
;
579 else if (strchr(newline
, c
)) {
596 SINGLE_QUOTE_VALUE_ESCAPE
,
598 DOUBLE_QUOTE_VALUE_ESCAPE
)) {
606 if (last_value_whitespace
!= (size_t) -1)
607 value
[last_value_whitespace
] = 0;
609 /* strip trailing whitespace from key */
610 if (last_key_whitespace
!= (size_t) -1)
611 key
[last_key_whitespace
] = 0;
613 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
625 static int check_utf8ness_and_warn(
626 const char *filename
, unsigned line
,
627 const char *key
, char *value
) {
629 if (!utf8_is_valid(key
)) {
630 _cleanup_free_
char *p
= NULL
;
632 p
= utf8_escape_invalid(key
);
633 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename
), line
, p
);
637 if (value
&& !utf8_is_valid(value
)) {
638 _cleanup_free_
char *p
= NULL
;
640 p
= utf8_escape_invalid(value
);
641 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, p
);
648 static int parse_env_file_push(
649 const char *filename
, unsigned line
,
650 const char *key
, char *value
,
655 va_list aq
, *ap
= userdata
;
658 r
= check_utf8ness_and_warn(filename
, line
, key
, value
);
664 while ((k
= va_arg(aq
, const char *))) {
667 v
= va_arg(aq
, char **);
689 const char *newline
, ...) {
697 va_start(ap
, newline
);
698 r
= parse_env_file_internal(NULL
, fname
, newline
, parse_env_file_push
, &ap
, &n_pushed
);
701 return r
< 0 ? r
: n_pushed
;
704 static int load_env_file_push(
705 const char *filename
, unsigned line
,
706 const char *key
, char *value
,
709 char ***m
= userdata
;
713 r
= check_utf8ness_and_warn(filename
, line
, key
, value
);
717 p
= strjoin(key
, "=", value
);
721 r
= strv_env_replace(m
, p
);
734 int load_env_file(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
741 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push
, &m
, NULL
);
751 static int load_env_file_push_pairs(
752 const char *filename
, unsigned line
,
753 const char *key
, char *value
,
756 char ***m
= userdata
;
759 r
= check_utf8ness_and_warn(filename
, line
, key
, value
);
763 r
= strv_extend(m
, key
);
768 r
= strv_extend(m
, "");
772 r
= strv_push(m
, value
);
783 int load_env_file_pairs(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
790 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push_pairs
, &m
, NULL
);
800 static int merge_env_file_push(
801 const char *filename
, unsigned line
,
802 const char *key
, char *value
,
806 char ***env
= userdata
;
807 char *expanded_value
;
812 log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename
), line
, key
);
816 if (!env_name_is_valid(key
)) {
817 log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename
), line
, key
);
822 expanded_value
= replace_env(value
, *env
,
823 REPLACE_ENV_USE_ENVIRONMENT
|
824 REPLACE_ENV_ALLOW_BRACELESS
|
825 REPLACE_ENV_ALLOW_EXTENDED
);
829 free_and_replace(value
, expanded_value
);
831 return load_env_file_push(filename
, line
, key
, value
, env
, n_pushed
);
839 /* NOTE: this function supports braceful and braceless variable expansions,
840 * plus "extended" substitutions, unlike other exported parsing functions.
843 return parse_env_file_internal(f
, fname
, NEWLINE
, merge_env_file_push
, env
, NULL
);
846 static void write_env_var(FILE *f
, const char *v
) {
852 fputs_unlocked(v
, f
);
853 fputc_unlocked('\n', f
);
858 fwrite_unlocked(v
, 1, p
-v
, f
);
860 if (string_has_cc(p
, NULL
) || chars_intersect(p
, WHITESPACE SHELL_NEED_QUOTES
)) {
861 fputc_unlocked('\"', f
);
864 if (strchr(SHELL_NEED_ESCAPE
, *p
))
865 fputc_unlocked('\\', f
);
867 fputc_unlocked(*p
, f
);
870 fputc_unlocked('\"', f
);
872 fputs_unlocked(p
, f
);
874 fputc_unlocked('\n', f
);
877 int write_env_file(const char *fname
, char **l
) {
878 _cleanup_fclose_
FILE *f
= NULL
;
879 _cleanup_free_
char *p
= NULL
;
885 r
= fopen_temporary(fname
, &f
, &p
);
889 fchmod_umask(fileno(f
), 0644);
892 write_env_var(f
, *i
);
894 r
= fflush_and_check(f
);
896 if (rename(p
, fname
) >= 0)
906 int executable_is_script(const char *path
, char **interpreter
) {
908 _cleanup_free_
char *line
= NULL
;
914 r
= read_one_line_file(path
, &line
);
918 if (!startswith(line
, "#!"))
921 ans
= strstrip(line
+ 2);
922 len
= strcspn(ans
, " \t");
927 ans
= strndup(ans
, len
);
936 * Retrieve one field from a file like /proc/self/status. pattern
937 * should not include whitespace or the delimiter (':'). pattern matches only
938 * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
939 * zeros after the ':' will be skipped. field must be freed afterwards.
940 * terminator specifies the terminating characters of the field value (not
941 * included in the value).
943 int get_proc_field(const char *filename
, const char *pattern
, const char *terminator
, char **field
) {
944 _cleanup_free_
char *status
= NULL
;
954 r
= read_full_file(filename
, &status
, NULL
);
964 t
= strstr(t
, pattern
);
968 /* Check that pattern occurs in beginning of line. */
969 pattern_ok
= (t
== status
|| t
[-1] == '\n');
971 t
+= strlen(pattern
);
973 } while (!pattern_ok
);
975 t
+= strspn(t
, " \t");
984 t
+= strspn(t
, " \t");
986 /* Also skip zeros, because when this is used for
987 * capabilities, we don't want the zeros. This way the
988 * same capability set always maps to the same string,
989 * irrespective of the total capability set size. For
990 * other numbers it shouldn't matter. */
992 /* Back off one char if there's nothing but whitespace
994 if (!*t
|| isspace(*t
))
998 len
= strcspn(t
, terminator
);
1000 f
= strndup(t
, len
);
1008 DIR *xopendirat(int fd
, const char *name
, int flags
) {
1012 assert(!(flags
& O_CREAT
));
1014 nfd
= openat(fd
, name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|flags
, 0);
1027 static int search_and_fopen_internal(const char *path
, const char *mode
, const char *root
, char **search
, FILE **_f
) {
1034 if (!path_strv_resolve_uniq(search
, root
))
1037 STRV_FOREACH(i
, search
) {
1038 _cleanup_free_
char *p
= NULL
;
1042 p
= strjoin(root
, *i
, "/", path
);
1044 p
= strjoin(*i
, "/", path
);
1054 if (errno
!= ENOENT
)
1061 int search_and_fopen(const char *path
, const char *mode
, const char *root
, const char **search
, FILE **_f
) {
1062 _cleanup_strv_free_
char **copy
= NULL
;
1068 if (path_is_absolute(path
)) {
1071 f
= fopen(path
, mode
);
1080 copy
= strv_copy((char**) search
);
1084 return search_and_fopen_internal(path
, mode
, root
, copy
, _f
);
1087 int search_and_fopen_nulstr(const char *path
, const char *mode
, const char *root
, const char *search
, FILE **_f
) {
1088 _cleanup_strv_free_
char **s
= NULL
;
1090 if (path_is_absolute(path
)) {
1093 f
= fopen(path
, mode
);
1102 s
= strv_split_nulstr(search
);
1106 return search_and_fopen_internal(path
, mode
, root
, s
, _f
);
1109 int fopen_temporary(const char *path
, FILE **_f
, char **_temp_path
) {
1118 r
= tempfn_xxxxxx(path
, NULL
, &t
);
1122 fd
= mkostemp_safe(t
);
1128 f
= fdopen(fd
, "we");
1142 int fflush_and_check(FILE *f
) {
1149 return errno
> 0 ? -errno
: -EIO
;
1154 int fflush_sync_and_check(FILE *f
) {
1159 r
= fflush_and_check(f
);
1163 if (fsync(fileno(f
)) < 0)
1169 /* This is much like mkostemp() but is subject to umask(). */
1170 int mkostemp_safe(char *pattern
) {
1171 _cleanup_umask_ mode_t u
= 0;
1178 fd
= mkostemp(pattern
, O_CLOEXEC
);
1185 int tempfn_xxxxxx(const char *p
, const char *extra
, char **ret
) {
1197 * /foo/bar/.#<extra>waldoXXXXXX
1201 if (!filename_is_valid(fn
))
1207 t
= new(char, strlen(p
) + 2 + strlen(extra
) + 6 + 1);
1211 strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t
, p
, fn
- p
), ".#"), extra
), fn
), "XXXXXX");
1213 *ret
= path_kill_slashes(t
);
1217 int tempfn_random(const char *p
, const char *extra
, char **ret
) {
1231 * /foo/bar/.#<extra>waldobaa2a261115984a9
1235 if (!filename_is_valid(fn
))
1241 t
= new(char, strlen(p
) + 2 + strlen(extra
) + 16 + 1);
1245 x
= stpcpy(stpcpy(stpcpy(mempcpy(t
, p
, fn
- p
), ".#"), extra
), fn
);
1248 for (i
= 0; i
< 16; i
++) {
1249 *(x
++) = hexchar(u
& 0xF);
1255 *ret
= path_kill_slashes(t
);
1259 int tempfn_random_child(const char *p
, const char *extra
, char **ret
) {
1270 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
1282 t
= new(char, strlen(p
) + 3 + strlen(extra
) + 16 + 1);
1286 x
= stpcpy(stpcpy(stpcpy(t
, p
), "/.#"), extra
);
1289 for (i
= 0; i
< 16; i
++) {
1290 *(x
++) = hexchar(u
& 0xF);
1296 *ret
= path_kill_slashes(t
);
1300 int write_timestamp_file_atomic(const char *fn
, usec_t n
) {
1301 char ln
[DECIMAL_STR_MAX(n
)+2];
1303 /* Creates a "timestamp" file, that contains nothing but a
1304 * usec_t timestamp, formatted in ASCII. */
1306 if (n
<= 0 || n
>= USEC_INFINITY
)
1309 xsprintf(ln
, USEC_FMT
"\n", n
);
1311 return write_string_file(fn
, ln
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
);
1314 int read_timestamp_file(const char *fn
, usec_t
*ret
) {
1315 _cleanup_free_
char *ln
= NULL
;
1319 r
= read_one_line_file(fn
, &ln
);
1323 r
= safe_atou64(ln
, &t
);
1327 if (t
<= 0 || t
>= (uint64_t) USEC_INFINITY
)
1334 int fputs_with_space(FILE *f
, const char *s
, const char *separator
, bool *space
) {
1339 /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
1340 * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
1341 * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
1342 * element, but not before the first one. */
1352 r
= fputs(separator
, f
);
1363 int open_tmpfile_unlinkable(const char *directory
, int flags
) {
1368 r
= tmp_dir(&directory
);
1373 /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
1375 /* Try O_TMPFILE first, if it is supported */
1376 fd
= open(directory
, flags
|O_TMPFILE
|O_EXCL
, S_IRUSR
|S_IWUSR
);
1380 /* Fall back to unguessable name + unlinking */
1381 p
= strjoina(directory
, "/systemd-tmp-XXXXXX");
1383 fd
= mkostemp_safe(p
);
1392 int open_tmpfile_linkable(const char *target
, int flags
, char **ret_path
) {
1393 _cleanup_free_
char *tmp
= NULL
;
1399 /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
1400 assert((flags
& O_EXCL
) == 0);
1402 /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
1403 * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
1404 * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
1407 _cleanup_free_
char *dn
= NULL
;
1409 dn
= dirname_malloc(target
);
1413 fd
= open(dn
, O_TMPFILE
|flags
, 0640);
1419 log_debug_errno(errno
, "Failed to use O_TMPFILE on %s: %m", dn
);
1422 r
= tempfn_random(target
, NULL
, &tmp
);
1426 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_NOFOLLOW
|O_NOCTTY
|flags
, 0640);
1436 int open_serialization_fd(const char *ident
) {
1439 fd
= memfd_create(ident
, MFD_CLOEXEC
);
1443 path
= getpid_cached() == 1 ? "/run/systemd" : "/tmp";
1444 fd
= open_tmpfile_unlinkable(path
, O_RDWR
|O_CLOEXEC
);
1448 log_debug("Serializing %s to %s.", ident
, path
);
1450 log_debug("Serializing %s to memfd.", ident
);
1455 int link_tmpfile(int fd
, const char *path
, const char *target
) {
1460 /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
1461 * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
1462 * on the directory, and renameat2() is used instead.
1464 * Note that in both cases we will not replace existing files. This is because linkat() does not support this
1465 * operation currently (renameat2() does), and there is no nice way to emulate this. */
1468 if (rename_noreplace(AT_FDCWD
, path
, AT_FDCWD
, target
) < 0)
1471 char proc_fd_path
[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd
) + 1];
1473 xsprintf(proc_fd_path
, "/proc/self/fd/%i", fd
);
1475 if (linkat(AT_FDCWD
, proc_fd_path
, AT_FDCWD
, target
, AT_SYMLINK_FOLLOW
) < 0)
1482 int read_nul_string(FILE *f
, char **ret
) {
1483 _cleanup_free_
char *x
= NULL
;
1484 size_t allocated
= 0, n
= 0;
1489 /* Reads a NUL-terminated string from the specified file. */
1494 if (!GREEDY_REALLOC(x
, allocated
, n
+2))
1498 if (c
== 0) /* Terminate at NUL byte */
1503 break; /* Terminate at EOF */
1523 int mkdtemp_malloc(const char *template, char **ret
) {
1529 p
= strdup(template);