fileio.c revision 550a40eceb7d1917152fc9317bf2696708d52bc2
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder/***
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder This file is part of systemd.
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder Copyright 2010 Lennart Poettering
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder systemd is free software; you can redistribute it and/or modify it
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder under the terms of the GNU Lesser General Public License as published by
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder the Free Software Foundation; either version 2.1 of the License, or
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder (at your option) any later version.
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder systemd is distributed in the hope that it will be useful, but
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder WITHOUT ANY WARRANTY; without even the implied warranty of
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder Lesser General Public License for more details.
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder You should have received a copy of the GNU Lesser General Public License
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder along with systemd; If not, see <http://www.gnu.org/licenses/>.
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder***/
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include <unistd.h>
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include <sys/sendfile.h>
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include "fileio.h"
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder#include "util.h"
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include "strv.h"
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include "utf8.h"
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder#include "ctype.h"
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroederint write_string_to_file(FILE *f, const char *line) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder errno = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder fputs(line, f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!endswith(line, "\n"))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder fputc('\n', f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder fflush(f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (ferror(f))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return errno ? -errno : -EIO;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder}
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroederint write_string_file(const char *fn, const char *line) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder _cleanup_fclose_ FILE *f = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(fn);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(line);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder f = fopen(fn, "we");
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!f)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -errno;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return write_string_to_file(f, line);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder}
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroederint write_string_file_atomic(const char *fn, const char *line) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder _cleanup_fclose_ FILE *f = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder _cleanup_free_ char *p = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder int r;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(fn);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(line);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = fopen_temporary(fn, &f, &p);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (r < 0)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return r;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder fchmod_umask(fileno(f), 0644);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder errno = 0;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder fputs(line, f);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!endswith(line, "\n"))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder fputc('\n', f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder fflush(f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (ferror(f))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = errno ? -errno : -EIO;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder else {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (rename(p, fn) < 0)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = -errno;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder else
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (r < 0)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder unlink(p);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return r;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder}
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maederint read_one_line_file(const char *fn, char **line) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder _cleanup_fclose_ FILE *f = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder char t[LINE_MAX], *c;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(fn);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder assert(line);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder f = fopen(fn, "re");
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!f)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!fgets(t, sizeof(t), f)) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (ferror(f))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return errno ? -errno : -EIO;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder t[0] = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder c = strdup(t);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!c)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -ENOMEM;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder truncate_nl(c);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder *line = c;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return 0;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder}
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maederssize_t sendfile_full(int out_fd, const char *fn) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder _cleanup_fclose_ FILE *f;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder struct stat st;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder int r;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder ssize_t s;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder size_t n, l;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder _cleanup_free_ char *buf = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(out_fd > 0);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder assert(fn);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder f = fopen(fn, "r");
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!f)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder r = fstat(fileno(f), &st);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (r < 0)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -errno;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder s = sendfile(out_fd, fileno(f), NULL, st.st_size);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (s < 0)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (errno == EINVAL || errno == ENOSYS) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* continue below */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder } else
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder else
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return s;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* sendfile() failed, fall back to read/write */
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* Safety check */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (st.st_size > 4*1024*1024)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -E2BIG;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder n = st.st_size > 0 ? st.st_size : LINE_MAX;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder l = 0;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder while (true) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder char *t;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder size_t k;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder t = realloc(buf, n);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!t)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -ENOMEM;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder buf = t;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder k = fread(buf + l, 1, n - l, f);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (k <= 0) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (ferror(f))
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder break;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder }
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder l += k;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder n *= 2;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* Safety check */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (n > 4*1024*1024)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -E2BIG;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = write(out_fd, buf, l);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (r < 0)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -errno;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return (ssize_t) l;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder}
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maederint read_full_file(const char *fn, char **contents, size_t *size) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder _cleanup_fclose_ FILE *f = NULL;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder size_t n, l;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder _cleanup_free_ char *buf = NULL;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder struct stat st;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder assert(fn);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(contents);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder f = fopen(fn, "re");
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!f)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (fstat(fileno(f), &st) < 0)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder /* Safety check */
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (st.st_size > 4*1024*1024)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -E2BIG;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder n = st.st_size > 0 ? st.st_size : LINE_MAX;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder l = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder for (;;) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder char *t;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder size_t k;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder t = realloc(buf, n+1);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!t)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return -ENOMEM;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder buf = t;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder k = fread(buf + l, 1, n - l, f);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (k <= 0) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (ferror(f))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -errno;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder break;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder l += k;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder n *= 2;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* Safety check */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (n > 4*1024*1024)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return -E2BIG;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder buf[l] = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder *contents = buf;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder buf = NULL; /* do not free */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (size)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder *size = l;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder return 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder}
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maederstatic int parse_env_file_internal(
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder const char *fname,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder const char *newline,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder int (*push) (const char *filename, unsigned line,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder const char *key, char *value, void *userdata),
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder void *userdata) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder _cleanup_free_ char *contents = NULL, *key = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder 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;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder char *p, *value = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder int r;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder unsigned line = 1;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder enum {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder PRE_KEY,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder KEY,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder PRE_VALUE,
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder VALUE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder VALUE_ESCAPE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder SINGLE_QUOTE_VALUE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder SINGLE_QUOTE_VALUE_ESCAPE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder DOUBLE_QUOTE_VALUE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder DOUBLE_QUOTE_VALUE_ESCAPE,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder COMMENT,
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder COMMENT_ESCAPE
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder } state = PRE_KEY;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(fname);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder assert(newline);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = read_full_file(fname, &contents, NULL);
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (r < 0)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder return r;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder for (p = contents; *p; p++) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder char c = *p;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder switch (state) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder case PRE_KEY:
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (strchr(COMMENTS, c))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = COMMENT;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder else if (!strchr(WHITESPACE, c)) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = KEY;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder last_key_whitespace = (size_t) -1;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder r = -ENOMEM;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder goto fail;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder key[n_key++] = c;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder break;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder case KEY:
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (strchr(newline, c)) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder state = PRE_KEY;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder line ++;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder n_key = 0;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder } else if (c == '=') {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = PRE_VALUE;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder last_value_whitespace = (size_t) -1;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder } else {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!strchr(WHITESPACE, c))
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder last_key_whitespace = (size_t) -1;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder else if (last_key_whitespace == (size_t) -1)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder last_key_whitespace = n_key;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = -ENOMEM;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder goto fail;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder key[n_key++] = c;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder break;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder case PRE_VALUE:
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (strchr(newline, c)) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = PRE_KEY;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder line ++;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder key[n_key] = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (value)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder value[n_value] = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder /* strip trailing whitespace from key */
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (last_key_whitespace != (size_t) -1)
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder key[last_key_whitespace] = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = push(fname, line, key, value, userdata);
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (r < 0)
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder goto fail;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder n_key = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder value = NULL;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder value_alloc = n_value = 0;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder } else if (c == '\'')
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = SINGLE_QUOTE_VALUE;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder else if (c == '\"')
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = DOUBLE_QUOTE_VALUE;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder else if (c == '\\')
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder state = VALUE_ESCAPE;
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder else if (!strchr(WHITESPACE, c)) {
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder state = VALUE;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder r = -ENOMEM;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder goto fail;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder value[n_value++] = c;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder }
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder break;
81f49ee02aaa3bc870401f8883bf52742eb3ea7aJonathan von Schroeder
db9680b2bbd9d091b198eaa4e324762921965fb3Christian Maeder case VALUE:
if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
key[n_key] = 0;
if (value)
value[n_value] = 0;
/* Chomp off trailing whitespace from value */
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata);
if (r < 0)
goto fail;
n_key = 0;
value = NULL;
value_alloc = n_value = 0;
} else if (c == '\\') {
state = VALUE_ESCAPE;
last_value_whitespace = (size_t) -1;
} else {
if (!strchr(WHITESPACE, c))
last_value_whitespace = (size_t) -1;
else if (last_value_whitespace == (size_t) -1)
last_value_whitespace = n_value;
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case VALUE_ESCAPE:
state = VALUE;
if (!strchr(newline, c)) {
/* Escaped newlines we eat up entirely */
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE:
if (c == '\'')
state = PRE_VALUE;
else if (c == '\\')
state = SINGLE_QUOTE_VALUE_ESCAPE;
else {
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE_ESCAPE:
state = SINGLE_QUOTE_VALUE;
if (!strchr(newline, c)) {
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE:
if (c == '\"')
state = PRE_VALUE;
else if (c == '\\')
state = DOUBLE_QUOTE_VALUE_ESCAPE;
else {
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE_ESCAPE:
state = DOUBLE_QUOTE_VALUE;
if (!strchr(newline, c)) {
if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
r = -ENOMEM;
goto fail;
}
value[n_value++] = c;
}
break;
case COMMENT:
if (c == '\\')
state = COMMENT_ESCAPE;
else if (strchr(newline, c)) {
state = PRE_KEY;
line ++;
}
break;
case COMMENT_ESCAPE:
state = COMMENT;
break;
}
}
if (state == PRE_VALUE ||
state == VALUE ||
state == VALUE_ESCAPE ||
state == SINGLE_QUOTE_VALUE ||
state == SINGLE_QUOTE_VALUE_ESCAPE ||
state == DOUBLE_QUOTE_VALUE ||
state == DOUBLE_QUOTE_VALUE_ESCAPE) {
key[n_key] = 0;
if (value)
value[n_value] = 0;
if (state == VALUE)
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata);
if (r < 0)
goto fail;
}
return 0;
fail:
free(value);
return r;
}
static int parse_env_file_push(const char *filename, unsigned line,
const char *key, char *value, void *userdata) {
const char *k;
va_list aq, *ap = userdata;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.",
filename, line, p);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *p = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
filename, line, key, p);
return -EINVAL;
}
va_copy(aq, *ap);
while ((k = va_arg(aq, const char *))) {
char **v;
v = va_arg(aq, char **);
if (streq(key, k)) {
va_end(aq);
free(*v);
*v = value;
return 1;
}
}
va_end(aq);
free(value);
return 0;
}
int parse_env_file(
const char *fname,
const char *newline, ...) {
va_list ap;
int r;
if (!newline)
newline = NEWLINE;
va_start(ap, newline);
r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
va_end(ap);
return r;
}
static int load_env_file_push(const char *filename, unsigned line,
const char *key, char *value, void *userdata) {
char ***m = userdata;
char *p;
int r;
if (!utf8_is_valid(key)) {
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.",
filename, line, key);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
/* FIXME: filter UTF-8 */
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
filename, line, key, value);
return -EINVAL;
}
p = strjoin(key, "=", strempty(value), NULL);
if (!p)
return -ENOMEM;
r = strv_push(m, p);
if (r < 0) {
free(p);
return r;
}
free(value);
return 0;
}
int load_env_file(const char *fname, const char *newline, char ***rl) {
char **m = NULL;
int r;
if (!newline)
newline = NEWLINE;
r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static void write_env_var(FILE *f, const char *v) {
const char *p;
p = strchr(v, '=');
if (!p) {
/* Fallback */
fputs(v, f);
fputc('\n', f);
return;
}
p++;
fwrite(v, 1, p-v, f);
if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
fputc('\"', f);
for (; *p; p++) {
if (strchr("\'\"\\`$", *p))
fputc('\\', f);
fputc(*p, f);
}
fputc('\"', f);
} else
fputs(p, f);
fputc('\n', f);
}
int write_env_file(const char *fname, char **l) {
char **i;
_cleanup_free_ char *p = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
fchmod_umask(fileno(f), 0644);
errno = 0;
STRV_FOREACH(i, l)
write_env_var(f, *i);
fflush(f);
if (ferror(f))
r = errno ? -errno : -EIO;
else {
if (rename(p, fname) < 0)
r = -errno;
else
r = 0;
}
if (r < 0)
unlink(p);
return r;
}
int executable_is_script(const char *path, char **interpreter) {
int r;
char _cleanup_free_ *line = NULL;
int len;
char *ans;
assert(path);
r = read_one_line_file(path, &line);
if (r < 0)
return r;
if (!startswith(line, "#!"))
return 0;
ans = strstrip(line + 2);
len = strcspn(ans, " \t");
if (len == 0)
return 0;
ans = strndup(ans, len);
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
/**
* Retrieve one field from a file like /proc/self/status. pattern
* should start with '\n' and end with a ':'. Whitespace and zeros
* after the ':' will be skipped. field must be freed afterwards.
*/
int get_status_field(const char *filename, const char *pattern, char **field) {
_cleanup_free_ char *status = NULL;
char *t;
size_t len;
int r;
assert(filename);
assert(pattern);
assert(field);
r = read_full_file(filename, &status, NULL);
if (r < 0)
return r;
t = strstr(status, pattern);
if (!t)
return -ENOENT;
t += strlen(pattern);
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 --;
}
len = strcspn(t, WHITESPACE);
*field = strndup(t, len);
if (!*field)
return -ENOMEM;
return 0;
}