/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "alloc-util.h"
#include "ctype.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
#include "utf8.h"
assert(f);
fputc('\n', f);
return fflush_and_check(f);
}
_cleanup_free_ char *p = NULL;
int r;
r = fopen_temporary(fn, &f, &p);
if (r < 0)
return r;
if (r >= 0) {
r = -errno;
}
if (r < 0)
(void) unlink(p);
return r;
}
int q, r;
if (flags & WRITE_STRING_FILE_ATOMIC) {
if (r < 0)
goto fail;
return r;
}
if (flags & WRITE_STRING_FILE_CREATE) {
if (!f) {
r = -errno;
goto fail;
}
} else {
int fd;
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
if (fd < 0) {
r = -errno;
goto fail;
}
if (!f) {
r = -errno;
safe_close(fd);
goto fail;
}
}
if (r < 0)
goto fail;
return 0;
fail:
if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
return r;
f = safe_fclose(f);
/* OK, the operation failed, but let's see if the right
* contents in place already. If so, eat up the error. */
if (q <= 0)
return r;
return 0;
}
char t[LINE_MAX], *c;
if (!f)
return -errno;
if (!fgets(t, sizeof(t), f)) {
if (ferror(f))
t[0] = 0;
}
c = strdup(t);
if (!c)
return -ENOMEM;
truncate_nl(c);
*line = c;
return 0;
}
size_t l, k;
accept_extra_nl = false;
if (!buf)
return -ENOMEM;
if (!f)
return -errno;
/* We try to read one byte more than we need, so that we know whether we hit eof */
errno = 0;
if (ferror(f))
if (k != l && k != l + accept_extra_nl)
return 0;
return 0;
if (k > l && buf[l] != '\n')
return 0;
return 1;
}
size_t n, l;
assert(f);
return -errno;
n = LINE_MAX;
/* Safety check */
return -E2BIG;
/* Start with the right file size, but be prepared for
* files from /proc which generally report a file size
* of 0 */
}
l = 0;
for (;;) {
char *t;
size_t k;
if (!t)
return -ENOMEM;
buf = t;
if (k <= 0) {
if (ferror(f))
return -errno;
break;
}
l += k;
n *= 2;
/* Safety check */
if (n > 4*1024*1024)
return -E2BIG;
}
buf[l] = 0;
if (size)
*size = l;
return 0;
}
if (!f)
return -errno;
}
static int parse_env_file_internal(
FILE *f,
const char *fname,
const char *newline,
void *userdata,
int *n_pushed) {
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;
int r;
enum {
KEY,
if (f)
else
if (r < 0)
return r;
for (p = contents; *p; p++) {
char c = *p;
switch (state) {
case PRE_KEY:
else if (!strchr(WHITESPACE, c)) {
r = -ENOMEM;
goto fail;
}
}
break;
case KEY:
line ++;
n_key = 0;
} else if (c == '=') {
} else {
if (!strchr(WHITESPACE, c))
r = -ENOMEM;
goto fail;
}
}
break;
case PRE_VALUE:
line ++;
if (value)
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
n_key = 0;
value_alloc = n_value = 0;
} else if (c == '\'')
else if (c == '\"')
else if (c == '\\')
else if (!strchr(WHITESPACE, c)) {
r = -ENOMEM;
goto fail;
}
}
break;
case VALUE:
line ++;
if (value)
/* Chomp off trailing whitespace from value */
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
n_key = 0;
value_alloc = n_value = 0;
} else if (c == '\\') {
} else {
if (!strchr(WHITESPACE, c))
r = -ENOMEM;
goto fail;
}
}
break;
case VALUE_ESCAPE:
/* Escaped newlines we eat up entirely */
r = -ENOMEM;
goto fail;
}
}
break;
case SINGLE_QUOTE_VALUE:
if (c == '\'')
else if (c == '\\')
else {
r = -ENOMEM;
goto fail;
}
}
break;
r = -ENOMEM;
goto fail;
}
}
break;
case DOUBLE_QUOTE_VALUE:
if (c == '\"')
else if (c == '\\')
else {
r = -ENOMEM;
goto fail;
}
}
break;
r = -ENOMEM;
goto fail;
}
}
break;
case COMMENT:
if (c == '\\')
line ++;
}
break;
case COMMENT_ESCAPE:
break;
}
}
state == VALUE_ESCAPE ||
state == SINGLE_QUOTE_VALUE ||
state == DOUBLE_QUOTE_VALUE ||
if (value)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
}
return 0;
fail:
return r;
}
static int parse_env_file_push(
void *userdata,
int *n_pushed) {
const char *k;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p;
p = utf8_escape_invalid(key);
return -EINVAL;
}
_cleanup_free_ char *p;
p = utf8_escape_invalid(value);
return -EINVAL;
}
char **v;
free(*v);
*v = value;
if (n_pushed)
(*n_pushed)++;
return 1;
}
}
return 0;
}
int parse_env_file(
const char *fname,
const char *newline, ...) {
int r, n_pushed = 0;
if (!newline)
return r < 0 ? r : n_pushed;
}
static int load_env_file_push(
void *userdata,
int *n_pushed) {
char ***m = userdata;
char *p;
int r;
if (!utf8_is_valid(key)) {
return -EINVAL;
}
return -EINVAL;
}
if (!p)
return -ENOMEM;
r = strv_consume(m, p);
if (r < 0)
return r;
if (n_pushed)
(*n_pushed)++;
return 0;
}
char **m = NULL;
int r;
if (!newline)
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static int load_env_file_push_pairs(
void *userdata,
int *n_pushed) {
char ***m = userdata;
int r;
if (!utf8_is_valid(key)) {
return -EINVAL;
}
return -EINVAL;
}
r = strv_extend(m, key);
if (r < 0)
return -ENOMEM;
if (!value) {
r = strv_extend(m, "");
if (r < 0)
return -ENOMEM;
} else {
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;
return 0;
}
char **m = NULL;
int r;
if (!newline)
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
const char *p;
p = strchr(v, '=');
if (!p) {
/* Fallback */
fputs(v, f);
fputc('\n', f);
return;
}
p++;
fwrite(v, 1, p-v, f);
fputc('\"', f);
for (; *p; p++) {
if (strchr(SHELL_NEED_ESCAPE, *p))
fputc('\\', f);
fputc(*p, f);
}
fputc('\"', f);
} else
fputs(p, f);
fputc('\n', f);
}
_cleanup_free_ char *p = NULL;
char **i;
int r;
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
STRV_FOREACH(i, l)
write_env_var(f, *i);
r = fflush_and_check(f);
if (r >= 0) {
return 0;
r = -errno;
}
unlink(p);
return r;
}
int r;
int len;
char *ans;
if (r < 0)
return r;
return 0;
if (len == 0)
return 0;
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
/**
* should not include whitespace or the delimiter (':'). pattern matches only
* the beginning of a line. Whitespace before ':' is skipped. Whitespace and
* zeros after the ':' will be skipped. field must be freed afterwards.
* terminator specifies the terminating characters of the field value (not
* included in the value).
*/
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
char *t, *f;
int r;
if (r < 0)
return r;
t = status;
do {
bool pattern_ok;
do {
if (!t)
return -ENOENT;
/* Check that pattern occurs in beginning of line. */
} while (!pattern_ok);
t += strspn(t, " \t");
if (!*t)
return -ENOENT;
} while (*t != ':');
t++;
if (*t) {
t += strspn(t, " \t");
/* Also skip zeros, because when this is used for
* capabilities, we don't want the zeros. This way the
* same capability set always maps to the same string,
* irrespective of the total capability set size. For
* other numbers it shouldn't matter. */
t += strspn(t, "0");
/* Back off one char if there's nothing but whitespace
and zeros */
if (!*t || isspace(*t))
t --;
}
if (!f)
return -ENOMEM;
*field = f;
return 0;
}
int nfd;
DIR *d;
if (nfd < 0)
return NULL;
if (!d) {
return NULL;
}
return d;
}
static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
char **i;
return -ENOMEM;
_cleanup_free_ char *p = NULL;
FILE *f;
if (root)
else
if (!p)
return -ENOMEM;
if (f) {
*_f = f;
return 0;
}
return -errno;
}
return -ENOENT;
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
if (path_is_absolute(path)) {
FILE *f;
if (f) {
*_f = f;
return 0;
}
return -errno;
}
if (!copy)
return -ENOMEM;
}
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
_cleanup_strv_free_ char **s = NULL;
if (path_is_absolute(path)) {
FILE *f;
if (f) {
*_f = f;
return 0;
}
return -errno;
}
s = strv_split_nulstr(search);
if (!s)
return -ENOMEM;
}
FILE *f;
char *t;
int r, fd;
if (r < 0)
return r;
if (fd < 0) {
free(t);
return -errno;
}
if (!f) {
unlink_noerrno(t);
free(t);
safe_close(fd);
return -errno;
}
*_f = f;
*_temp_path = t;
return 0;
}
assert(f);
errno = 0;
fflush(f);
if (ferror(f))
return 0;
}
/* This is much like like mkostemp() but is subject to umask(). */
int fd;
u = umask(077);
if (fd < 0)
return -errno;
return fd;
}
char *p;
int fd;
#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
if (fd >= 0)
return fd;
#endif
/* Fall back to unguessable name + unlinking */
if (fd < 0)
return fd;
unlink(p);
return fd;
}
const char *fn;
char *t;
assert(p);
/*
* Turns this:
*
* Into this:
*/
if (!filename_is_valid(fn))
return -EINVAL;
extra = "";
if (!t)
return -ENOMEM;
*ret = path_kill_slashes(t);
return 0;
}
const char *fn;
char *t, *x;
uint64_t u;
unsigned i;
assert(p);
/*
* Turns this:
*
* Into this:
*/
if (!filename_is_valid(fn))
return -EINVAL;
if (!extra)
extra = "";
if (!t)
return -ENOMEM;
u = random_u64();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
*x = 0;
*ret = path_kill_slashes(t);
return 0;
}
char *t, *x;
uint64_t u;
unsigned i;
assert(p);
/* Turns this:
* Into this:
*/
if (!extra)
extra = "";
if (!t)
return -ENOMEM;
u = random_u64();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
*x = 0;
*ret = path_kill_slashes(t);
return 0;
}
/* Creates a "timestamp" file, that contains nothing but a
* usec_t timestamp, formatted in ASCII. */
if (n <= 0 || n >= USEC_INFINITY)
return -ERANGE;
}
uint64_t t;
int r;
if (r < 0)
return r;
r = safe_atou64(ln, &t);
if (r < 0)
return r;
if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
return -ERANGE;
return 0;
}
int r;
assert(s);
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
* when specified shall initially point to a boolean variable initialized to false. It is set to true after the
* first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
* element, but not before the first one. */
if (!f)
f = stdout;
if (space) {
if (!separator)
separator = " ";
if (*space) {
if (r < 0)
return r;
}
*space = true;
}
return fputs(s, f);
}