2020-11-09 05:23:58 +01:00
|
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
2019-11-25 15:15:38 +01:00
|
|
|
|
#include <limits.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
|
#include <stdio.h>
|
2013-04-03 19:04:03 +02:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
|
#include "alloc-util.h"
|
2013-09-19 23:22:59 +02:00
|
|
|
|
#include "ctype.h"
|
2018-11-30 22:08:41 +01:00
|
|
|
|
#include "env-file.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "env-util.h"
|
|
|
|
|
#include "fd-util.h"
|
|
|
|
|
#include "fileio.h"
|
2018-05-16 11:35:41 +02:00
|
|
|
|
#include "fs-util.h"
|
2016-03-02 23:50:21 +01:00
|
|
|
|
#include "io-util.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
|
#include "parse-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "process-util.h"
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
#include "random-util.h"
|
2020-07-17 12:26:01 +02:00
|
|
|
|
#include "rm-rf.h"
|
|
|
|
|
#include "socket-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
|
#include "string-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "strv.h"
|
2018-09-13 14:31:13 +02:00
|
|
|
|
#include "tests.h"
|
2018-11-30 21:05:27 +01:00
|
|
|
|
#include "tmpfile-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
|
#include "util.h"
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
|
|
|
|
static void test_parse_env_file(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char
|
|
|
|
|
t[] = "/tmp/test-fileio-in-XXXXXX",
|
2013-07-30 01:49:55 +02:00
|
|
|
|
p[] = "/tmp/test-fileio-out-XXXXXX";
|
2013-04-03 19:04:03 +02:00
|
|
|
|
FILE *f;
|
2013-04-17 15:25:02 +02:00
|
|
|
|
_cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
|
2019-01-14 22:35:10 +01:00
|
|
|
|
*six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL,
|
|
|
|
|
*eleven = NULL, *twelve = NULL, *thirteen = NULL;
|
2013-04-03 19:56:39 +02:00
|
|
|
|
_cleanup_strv_free_ char **a = NULL, **b = NULL;
|
2013-04-03 19:04:03 +02:00
|
|
|
|
char **i;
|
2013-04-03 19:56:39 +02:00
|
|
|
|
unsigned k;
|
2018-10-23 10:50:09 +02:00
|
|
|
|
int r;
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
assert_se(fmkostemp_safe(t, "w", &f) == 0);
|
2013-04-03 19:04:03 +02:00
|
|
|
|
fputs("one=BAR \n"
|
|
|
|
|
"# comment\n"
|
|
|
|
|
" # comment \n"
|
2013-04-18 10:15:25 +02:00
|
|
|
|
" ; comment \n"
|
2013-04-03 19:04:03 +02:00
|
|
|
|
" two = bar \n"
|
|
|
|
|
"invalid line\n"
|
2013-04-18 10:15:25 +02:00
|
|
|
|
"invalid line #comment\n"
|
2013-04-03 19:04:03 +02:00
|
|
|
|
"three = \"333\n"
|
|
|
|
|
"xxxx\"\n"
|
|
|
|
|
"four = \'44\\\"44\'\n"
|
2019-01-14 22:35:10 +01:00
|
|
|
|
"five = \"55\\\"55\" \"FIVE\" cinco \n"
|
2013-04-03 19:04:03 +02:00
|
|
|
|
"six = seis sechs\\\n"
|
|
|
|
|
" sis\n"
|
2013-04-18 10:15:25 +02:00
|
|
|
|
"seven=\"sevenval\" #nocomment\n"
|
|
|
|
|
"eight=eightval #nocomment\n"
|
2013-04-17 15:25:02 +02:00
|
|
|
|
"export nine=nineval\n"
|
env-util,fileio: immediately replace variables in load_env_file_push()
strv_env_replace was calling env_match(), which in effect allowed multiple
values for the same key to be inserted into the environment block. That's
pointless, because APIs to access variables only return a single value (the
latest entry), so it's better to keep the block clean, i.e. with just a single
entry for each key.
Add a new helper function that simply tests if the part before '=' is equal in
two strings and use that in strv_env_replace.
In load_env_file_push, use strv_env_replace to immediately replace the previous
assignment with a matching name.
Afaict, none of the callers are materially affected by this change, but it
seems like some pointless work was being done, if the same value was set
multiple times. We'd go through parsing and assigning the value for each
entry. With this change, we handle just the last one.
2017-02-11 05:08:53 +01:00
|
|
|
|
"ten=ignored\n"
|
|
|
|
|
"ten=ignored\n"
|
2019-01-14 22:35:10 +01:00
|
|
|
|
"ten=\n"
|
|
|
|
|
"eleven=\\value\n"
|
|
|
|
|
"twelve=\"\\value\"\n"
|
|
|
|
|
"thirteen='\\value'", f);
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
|
|
|
|
fflush(f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
|
r = load_env_file(NULL, t, &a);
|
2013-04-17 15:25:02 +02:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
|
log_info("Got: <%s>", *i);
|
|
|
|
|
|
2013-12-12 23:08:47 +01:00
|
|
|
|
assert_se(streq_ptr(a[0], "one=BAR"));
|
|
|
|
|
assert_se(streq_ptr(a[1], "two=bar"));
|
|
|
|
|
assert_se(streq_ptr(a[2], "three=333\nxxxx"));
|
2019-01-14 22:35:10 +01:00
|
|
|
|
assert_se(streq_ptr(a[3], "four=44\\\"44"));
|
|
|
|
|
assert_se(streq_ptr(a[4], "five=55\"55FIVEcinco"));
|
2013-12-12 23:08:47 +01:00
|
|
|
|
assert_se(streq_ptr(a[5], "six=seis sechs sis"));
|
|
|
|
|
assert_se(streq_ptr(a[6], "seven=sevenval#nocomment"));
|
|
|
|
|
assert_se(streq_ptr(a[7], "eight=eightval #nocomment"));
|
|
|
|
|
assert_se(streq_ptr(a[8], "export nine=nineval"));
|
|
|
|
|
assert_se(streq_ptr(a[9], "ten="));
|
2019-01-14 22:35:10 +01:00
|
|
|
|
assert_se(streq_ptr(a[10], "eleven=value"));
|
|
|
|
|
assert_se(streq_ptr(a[11], "twelve=\\value"));
|
|
|
|
|
assert_se(streq_ptr(a[12], "thirteen=\\value"));
|
|
|
|
|
assert_se(a[13] == NULL);
|
2013-04-17 15:25:02 +02:00
|
|
|
|
|
2014-12-23 19:04:56 +01:00
|
|
|
|
strv_env_clean(a);
|
2013-04-17 15:25:02 +02:00
|
|
|
|
|
|
|
|
|
k = 0;
|
|
|
|
|
STRV_FOREACH(i, b) {
|
|
|
|
|
log_info("Got2: <%s>", *i);
|
|
|
|
|
assert_se(streq(*i, a[k++]));
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-03 19:04:03 +02:00
|
|
|
|
r = parse_env_file(
|
2018-11-12 14:04:47 +01:00
|
|
|
|
NULL, t,
|
2013-04-03 19:04:03 +02:00
|
|
|
|
"one", &one,
|
|
|
|
|
"two", &two,
|
|
|
|
|
"three", &three,
|
|
|
|
|
"four", &four,
|
|
|
|
|
"five", &five,
|
|
|
|
|
"six", &six,
|
|
|
|
|
"seven", &seven,
|
2013-04-17 11:02:56 +02:00
|
|
|
|
"eight", &eight,
|
2013-04-17 15:25:02 +02:00
|
|
|
|
"export nine", &nine,
|
2019-01-14 22:35:10 +01:00
|
|
|
|
"ten", &ten,
|
|
|
|
|
"eleven", &eleven,
|
|
|
|
|
"twelve", &twelve,
|
|
|
|
|
"thirteen", &thirteen);
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
log_info("one=[%s]", strna(one));
|
|
|
|
|
log_info("two=[%s]", strna(two));
|
|
|
|
|
log_info("three=[%s]", strna(three));
|
|
|
|
|
log_info("four=[%s]", strna(four));
|
|
|
|
|
log_info("five=[%s]", strna(five));
|
|
|
|
|
log_info("six=[%s]", strna(six));
|
|
|
|
|
log_info("seven=[%s]", strna(seven));
|
2013-04-17 11:02:56 +02:00
|
|
|
|
log_info("eight=[%s]", strna(eight));
|
2013-04-17 15:25:02 +02:00
|
|
|
|
log_info("export nine=[%s]", strna(nine));
|
|
|
|
|
log_info("ten=[%s]", strna(nine));
|
2019-01-14 22:35:10 +01:00
|
|
|
|
log_info("eleven=[%s]", strna(eleven));
|
|
|
|
|
log_info("twelve=[%s]", strna(twelve));
|
|
|
|
|
log_info("thirteen=[%s]", strna(thirteen));
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
|
|
|
|
assert_se(streq(one, "BAR"));
|
|
|
|
|
assert_se(streq(two, "bar"));
|
|
|
|
|
assert_se(streq(three, "333\nxxxx"));
|
2019-01-14 22:35:10 +01:00
|
|
|
|
assert_se(streq(four, "44\\\"44"));
|
|
|
|
|
assert_se(streq(five, "55\"55FIVEcinco"));
|
2013-04-03 19:04:03 +02:00
|
|
|
|
assert_se(streq(six, "seis sechs sis"));
|
2013-04-18 10:15:25 +02:00
|
|
|
|
assert_se(streq(seven, "sevenval#nocomment"));
|
|
|
|
|
assert_se(streq(eight, "eightval #nocomment"));
|
2013-04-17 15:25:02 +02:00
|
|
|
|
assert_se(streq(nine, "nineval"));
|
|
|
|
|
assert_se(ten == NULL);
|
2019-01-14 22:35:10 +01:00
|
|
|
|
assert_se(streq(eleven, "value"));
|
|
|
|
|
assert_se(streq(twelve, "\\value"));
|
|
|
|
|
assert_se(streq(thirteen, "\\value"));
|
2013-04-03 19:04:03 +02:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
{
|
|
|
|
|
/* prepare a temporary file to write the environment to */
|
|
|
|
|
_cleanup_close_ int fd = mkostemp_safe(p);
|
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-30 01:49:55 +02:00
|
|
|
|
r = write_env_file(p, a);
|
2013-04-18 10:15:25 +02:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
|
r = load_env_file(NULL, p, &b);
|
2013-04-18 10:15:25 +02:00
|
|
|
|
assert_se(r >= 0);
|
2013-04-03 19:04:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-11 17:43:05 +02:00
|
|
|
|
static void test_one_shell_var(const char *file, const char *variable, const char *value) {
|
|
|
|
|
_cleanup_free_ char *cmd = NULL, *from_shell = NULL;
|
2020-09-14 22:32:52 +02:00
|
|
|
|
_cleanup_pclose_ FILE *f = NULL;
|
2020-09-11 17:43:05 +02:00
|
|
|
|
size_t sz;
|
|
|
|
|
|
|
|
|
|
assert_se(cmd = strjoin(". ", file, " && /bin/echo -n \"$", variable, "\""));
|
|
|
|
|
assert_se(f = popen(cmd, "re"));
|
|
|
|
|
assert_se(read_full_stream(f, &from_shell, &sz) >= 0);
|
|
|
|
|
assert_se(sz == strlen(value));
|
|
|
|
|
assert_se(streq(from_shell, value));
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-12 03:50:16 +02:00
|
|
|
|
static void test_parse_multiline_env_file(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char
|
|
|
|
|
t[] = "/tmp/test-fileio-in-XXXXXX",
|
2013-09-12 03:50:16 +02:00
|
|
|
|
p[] = "/tmp/test-fileio-out-XXXXXX";
|
|
|
|
|
FILE *f;
|
|
|
|
|
_cleanup_strv_free_ char **a = NULL, **b = NULL;
|
|
|
|
|
char **i;
|
2018-10-23 10:50:09 +02:00
|
|
|
|
int r;
|
2013-09-12 03:50:16 +02:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
assert_se(fmkostemp_safe(t, "w", &f) == 0);
|
2013-09-12 03:50:16 +02:00
|
|
|
|
fputs("one=BAR\\\n"
|
2020-09-11 17:42:46 +02:00
|
|
|
|
"\\ \\ \\ \\ VAR\\\n"
|
|
|
|
|
"\\\tGAR\n"
|
2013-09-12 03:50:16 +02:00
|
|
|
|
"#comment\n"
|
|
|
|
|
"two=\"bar\\\n"
|
|
|
|
|
" var\\\n"
|
|
|
|
|
"\tgar\"\n"
|
|
|
|
|
"#comment\n"
|
|
|
|
|
"tri=\"bar \\\n"
|
|
|
|
|
" var \\\n"
|
|
|
|
|
"\tgar \"\n", f);
|
|
|
|
|
|
2020-09-11 17:42:46 +02:00
|
|
|
|
assert_se(fflush_and_check(f) >= 0);
|
2013-09-12 03:50:16 +02:00
|
|
|
|
fclose(f);
|
|
|
|
|
|
2020-09-11 17:43:05 +02:00
|
|
|
|
test_one_shell_var(t, "one", "BAR VAR\tGAR");
|
|
|
|
|
test_one_shell_var(t, "two", "bar var\tgar");
|
|
|
|
|
test_one_shell_var(t, "tri", "bar var \tgar ");
|
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
|
r = load_env_file(NULL, t, &a);
|
2013-09-12 03:50:16 +02:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
|
log_info("Got: <%s>", *i);
|
|
|
|
|
|
2013-12-12 23:08:47 +01:00
|
|
|
|
assert_se(streq_ptr(a[0], "one=BAR VAR\tGAR"));
|
|
|
|
|
assert_se(streq_ptr(a[1], "two=bar var\tgar"));
|
|
|
|
|
assert_se(streq_ptr(a[2], "tri=bar var \tgar "));
|
2013-09-12 03:50:16 +02:00
|
|
|
|
assert_se(a[3] == NULL);
|
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
{
|
|
|
|
|
_cleanup_close_ int fd = mkostemp_safe(p);
|
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-12 03:50:16 +02:00
|
|
|
|
r = write_env_file(p, a);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
|
r = load_env_file(NULL, p, &b);
|
2013-09-12 03:50:16 +02:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-04 18:00:00 +02:00
|
|
|
|
static void test_merge_env_file(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
|
2017-09-24 08:59:49 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2016-08-04 18:00:00 +02:00
|
|
|
|
_cleanup_strv_free_ char **a = NULL;
|
|
|
|
|
char **i;
|
2018-10-23 10:50:09 +02:00
|
|
|
|
int r;
|
2016-08-04 18:00:00 +02:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
assert_se(fmkostemp_safe(t, "w", &f) == 0);
|
2016-08-04 18:00:00 +02:00
|
|
|
|
log_info("/* %s (%s) */", __func__, t);
|
|
|
|
|
|
|
|
|
|
r = write_string_stream(f,
|
|
|
|
|
"one=1 \n"
|
|
|
|
|
"twelve=${one}2\n"
|
|
|
|
|
"twentyone=2${one}\n"
|
|
|
|
|
"one=2\n"
|
2017-02-11 20:05:10 +01:00
|
|
|
|
"twentytwo=2${one}\n"
|
|
|
|
|
"xxx_minus_three=$xxx - 3\n"
|
|
|
|
|
"xxx=0x$one$one$one\n"
|
2016-08-09 16:20:22 +02:00
|
|
|
|
"yyy=${one:-fallback}\n"
|
|
|
|
|
"zzz=${one:+replacement}\n"
|
|
|
|
|
"zzzz=${foobar:-${nothing}}\n"
|
|
|
|
|
"zzzzz=${nothing:+${nothing}}\n"
|
2017-09-22 20:55:34 +02:00
|
|
|
|
, WRITE_STRING_FILE_AVOID_NEWLINE);
|
2016-08-04 18:00:00 +02:00
|
|
|
|
assert(r >= 0);
|
|
|
|
|
|
|
|
|
|
r = merge_env_file(&a, NULL, t);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
strv_sort(a);
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
|
log_info("Got: <%s>", *i);
|
|
|
|
|
|
|
|
|
|
assert_se(streq(a[0], "one=2"));
|
|
|
|
|
assert_se(streq(a[1], "twelve=12"));
|
|
|
|
|
assert_se(streq(a[2], "twentyone=21"));
|
|
|
|
|
assert_se(streq(a[3], "twentytwo=22"));
|
2017-02-11 20:05:10 +01:00
|
|
|
|
assert_se(streq(a[4], "xxx=0x222"));
|
|
|
|
|
assert_se(streq(a[5], "xxx_minus_three= - 3"));
|
2016-08-09 16:20:22 +02:00
|
|
|
|
assert_se(streq(a[6], "yyy=2"));
|
|
|
|
|
assert_se(streq(a[7], "zzz=replacement"));
|
|
|
|
|
assert_se(streq(a[8], "zzzz="));
|
|
|
|
|
assert_se(streq(a[9], "zzzzz="));
|
|
|
|
|
assert_se(a[10] == NULL);
|
2016-08-04 18:00:00 +02:00
|
|
|
|
|
|
|
|
|
r = merge_env_file(&a, NULL, t);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
strv_sort(a);
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
|
log_info("Got2: <%s>", *i);
|
|
|
|
|
|
|
|
|
|
assert_se(streq(a[0], "one=2"));
|
|
|
|
|
assert_se(streq(a[1], "twelve=12"));
|
|
|
|
|
assert_se(streq(a[2], "twentyone=21"));
|
|
|
|
|
assert_se(streq(a[3], "twentytwo=22"));
|
2017-02-11 20:05:10 +01:00
|
|
|
|
assert_se(streq(a[4], "xxx=0x222"));
|
|
|
|
|
assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
|
2016-08-09 16:20:22 +02:00
|
|
|
|
assert_se(streq(a[6], "yyy=2"));
|
|
|
|
|
assert_se(streq(a[7], "zzz=replacement"));
|
|
|
|
|
assert_se(streq(a[8], "zzzz="));
|
|
|
|
|
assert_se(streq(a[9], "zzzzz="));
|
|
|
|
|
assert_se(a[10] == NULL);
|
2016-08-04 18:00:00 +02:00
|
|
|
|
}
|
2013-09-12 03:50:16 +02:00
|
|
|
|
|
2017-02-18 04:56:28 +01:00
|
|
|
|
static void test_merge_env_file_invalid(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
|
2017-09-24 08:59:49 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2017-02-18 04:56:28 +01:00
|
|
|
|
_cleanup_strv_free_ char **a = NULL;
|
|
|
|
|
char **i;
|
2018-10-23 10:50:09 +02:00
|
|
|
|
int r;
|
2017-02-18 04:56:28 +01:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
assert_se(fmkostemp_safe(t, "w", &f) == 0);
|
2017-02-18 04:56:28 +01:00
|
|
|
|
log_info("/* %s (%s) */", __func__, t);
|
|
|
|
|
|
|
|
|
|
r = write_string_stream(f,
|
|
|
|
|
"unset one \n"
|
|
|
|
|
"unset one= \n"
|
|
|
|
|
"unset one=1 \n"
|
|
|
|
|
"one \n"
|
|
|
|
|
"one = \n"
|
|
|
|
|
"one two =\n"
|
|
|
|
|
"\x20two=\n"
|
|
|
|
|
"#comment=comment\n"
|
|
|
|
|
";comment2=comment2\n"
|
|
|
|
|
"#\n"
|
|
|
|
|
"\n\n" /* empty line */
|
2017-09-22 20:55:34 +02:00
|
|
|
|
, WRITE_STRING_FILE_AVOID_NEWLINE);
|
2017-02-18 04:56:28 +01:00
|
|
|
|
assert(r >= 0);
|
|
|
|
|
|
|
|
|
|
r = merge_env_file(&a, NULL, t);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
|
log_info("Got: <%s>", *i);
|
|
|
|
|
|
|
|
|
|
assert_se(strv_isempty(a));
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-19 10:02:50 +02:00
|
|
|
|
static void test_executable_is_script(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2013-07-19 10:02:50 +02:00
|
|
|
|
char *command;
|
2018-10-23 10:50:09 +02:00
|
|
|
|
int r;
|
2013-07-19 10:02:50 +02:00
|
|
|
|
|
2018-10-23 10:50:09 +02:00
|
|
|
|
assert_se(fmkostemp_safe(t, "w", &f) == 0);
|
2013-07-19 10:02:50 +02:00
|
|
|
|
fputs("#! /bin/script -a -b \ngoo goo", f);
|
|
|
|
|
fflush(f);
|
|
|
|
|
|
|
|
|
|
r = executable_is_script(t, &command);
|
|
|
|
|
assert_se(r > 0);
|
|
|
|
|
assert_se(streq(command, "/bin/script"));
|
|
|
|
|
free(command);
|
|
|
|
|
|
|
|
|
|
r = executable_is_script("/bin/sh", &command);
|
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
|
|
|
|
|
r = executable_is_script("/usr/bin/yum", &command);
|
|
|
|
|
assert_se(r > 0 || r == -ENOENT);
|
|
|
|
|
if (r > 0) {
|
|
|
|
|
assert_se(startswith(command, "/"));
|
|
|
|
|
free(command);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-14 01:41:52 +02:00
|
|
|
|
static void test_status_field(void) {
|
2013-09-19 23:22:59 +02:00
|
|
|
|
_cleanup_free_ char *t = NULL, *p = NULL, *s = NULL, *z = NULL;
|
|
|
|
|
unsigned long long total = 0, buffers = 0;
|
2013-09-15 14:40:16 +02:00
|
|
|
|
int r;
|
2013-09-14 01:41:52 +02:00
|
|
|
|
|
2015-09-30 14:57:55 +02:00
|
|
|
|
assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
|
2013-09-14 01:41:52 +02:00
|
|
|
|
puts(t);
|
|
|
|
|
assert_se(streq(t, "1"));
|
|
|
|
|
|
2015-09-30 14:57:55 +02:00
|
|
|
|
r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
if (r != -ENOENT) {
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(r == 0);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
puts(p);
|
|
|
|
|
assert_se(safe_atollu(p, &total) == 0);
|
|
|
|
|
}
|
2013-09-14 01:41:52 +02:00
|
|
|
|
|
2015-09-30 14:57:55 +02:00
|
|
|
|
r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
if (r != -ENOENT) {
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(r == 0);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
puts(s);
|
|
|
|
|
assert_se(safe_atollu(s, &buffers) == 0);
|
|
|
|
|
}
|
2013-09-14 01:41:52 +02:00
|
|
|
|
|
2014-09-30 23:37:10 +02:00
|
|
|
|
if (p)
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(buffers < total);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
|
|
|
|
|
/* Seccomp should be a good test for field full of zeros. */
|
2015-09-30 14:57:55 +02:00
|
|
|
|
r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
if (r != -ENOENT) {
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(r == 0);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
puts(z);
|
|
|
|
|
assert_se(safe_atollu(z, &buffers) == 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_capeff(void) {
|
|
|
|
|
int pid, p;
|
|
|
|
|
|
|
|
|
|
for (pid = 0; pid < 2; pid++) {
|
|
|
|
|
_cleanup_free_ char *capeff = NULL;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
r = get_process_capeff(0, &capeff);
|
|
|
|
|
log_info("capeff: '%s' (r=%d)", capeff, r);
|
|
|
|
|
|
2017-10-04 16:01:32 +02:00
|
|
|
|
if (IN_SET(r, -ENOENT, -EPERM))
|
2013-09-19 23:22:59 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
assert_se(*capeff);
|
2016-05-17 15:44:32 +02:00
|
|
|
|
p = capeff[strspn(capeff, HEXDIGITS)];
|
2014-10-04 23:51:45 +02:00
|
|
|
|
assert_se(!p || isspace(p));
|
2013-09-19 23:22:59 +02:00
|
|
|
|
}
|
2013-09-14 01:41:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-16 14:19:07 +02:00
|
|
|
|
static void test_write_string_stream(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_stream-XXXXXX";
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2014-08-16 14:19:07 +02:00
|
|
|
|
int fd;
|
|
|
|
|
char buf[64];
|
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(fn);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
|
|
|
|
|
f = fdopen(fd, "r");
|
|
|
|
|
assert_se(f);
|
2017-09-22 20:55:34 +02:00
|
|
|
|
assert_se(write_string_stream(f, "boohoo", 0) < 0);
|
2018-03-05 22:23:33 +01:00
|
|
|
|
f = safe_fclose(f);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
|
2018-03-05 22:23:33 +01:00
|
|
|
|
f = fopen(fn, "r+");
|
2014-08-16 14:19:07 +02:00
|
|
|
|
assert_se(f);
|
|
|
|
|
|
2017-09-22 20:55:34 +02:00
|
|
|
|
assert_se(write_string_stream(f, "boohoo", 0) == 0);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
rewind(f);
|
|
|
|
|
|
|
|
|
|
assert_se(fgets(buf, sizeof(buf), f));
|
|
|
|
|
assert_se(streq(buf, "boohoo\n"));
|
2018-03-05 22:23:33 +01:00
|
|
|
|
f = safe_fclose(f);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
|
2018-03-05 22:23:33 +01:00
|
|
|
|
f = fopen(fn, "w+");
|
2015-07-06 23:31:44 +02:00
|
|
|
|
assert_se(f);
|
|
|
|
|
|
2017-09-22 20:55:34 +02:00
|
|
|
|
assert_se(write_string_stream(f, "boohoo", WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
|
2015-07-06 23:31:44 +02:00
|
|
|
|
rewind(f);
|
|
|
|
|
|
|
|
|
|
assert_se(fgets(buf, sizeof(buf), f));
|
|
|
|
|
printf(">%s<", buf);
|
|
|
|
|
assert_se(streq(buf, "boohoo"));
|
2014-08-16 14:19:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_write_string_file(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_file-XXXXXX";
|
2014-09-18 12:09:10 +02:00
|
|
|
|
char buf[64] = {};
|
|
|
|
|
_cleanup_close_ int fd;
|
2014-08-16 14:19:07 +02:00
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(fn);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
|
2015-07-07 01:19:25 +02:00
|
|
|
|
assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
|
2014-09-16 21:59:50 +02:00
|
|
|
|
assert_se(read(fd, buf, sizeof(buf)) == 7);
|
2014-08-16 14:19:07 +02:00
|
|
|
|
assert_se(streq(buf, "boohoo\n"));
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 18:55:31 +02:00
|
|
|
|
static void test_write_string_file_no_create(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_file_no_create-XXXXXX";
|
2014-09-02 18:55:31 +02:00
|
|
|
|
_cleanup_close_ int fd;
|
2019-07-11 07:54:04 +02:00
|
|
|
|
char buf[64] = {};
|
2014-09-02 18:55:31 +02:00
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(fn);
|
2014-09-02 18:55:31 +02:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
|
2015-07-07 01:19:25 +02:00
|
|
|
|
assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
|
|
|
|
|
assert_se(write_string_file(fn, "boohoo", 0) == 0);
|
2014-09-02 18:55:31 +02:00
|
|
|
|
|
2019-07-11 07:54:04 +02:00
|
|
|
|
assert_se(read(fd, buf, sizeof buf) == (ssize_t) strlen("boohoo\n"));
|
2014-09-02 18:55:31 +02:00
|
|
|
|
assert_se(streq(buf, "boohoo\n"));
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-13 00:54:56 +01:00
|
|
|
|
static void test_write_string_file_verify(void) {
|
|
|
|
|
_cleanup_free_ char *buf = NULL, *buf2 = NULL;
|
|
|
|
|
int r;
|
|
|
|
|
|
2020-04-23 12:12:23 +02:00
|
|
|
|
r = read_one_line_file("/proc/version", &buf);
|
|
|
|
|
if (ERRNO_IS_PRIVILEGE(r))
|
|
|
|
|
return;
|
|
|
|
|
assert_se(r >= 0);
|
2018-05-14 09:12:12 +02:00
|
|
|
|
assert_se(buf2 = strjoin(buf, "\n"));
|
2015-11-13 00:54:56 +01:00
|
|
|
|
|
2019-12-13 12:21:11 +01:00
|
|
|
|
r = write_string_file("/proc/version", buf, 0);
|
2017-10-04 16:01:32 +02:00
|
|
|
|
assert_se(IN_SET(r, -EACCES, -EIO));
|
2019-12-13 12:21:11 +01:00
|
|
|
|
r = write_string_file("/proc/version", buf2, 0);
|
2017-10-04 16:01:32 +02:00
|
|
|
|
assert_se(IN_SET(r, -EACCES, -EIO));
|
2015-11-13 00:54:56 +01:00
|
|
|
|
|
2019-12-13 12:21:11 +01:00
|
|
|
|
assert_se(write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
|
|
|
|
|
assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
|
2015-11-13 00:54:56 +01:00
|
|
|
|
|
2019-12-13 12:21:11 +01:00
|
|
|
|
r = write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
|
2017-10-04 16:01:32 +02:00
|
|
|
|
assert_se(IN_SET(r, -EACCES, -EIO));
|
2019-12-13 12:21:11 +01:00
|
|
|
|
assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
|
2015-11-13 00:54:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 18:55:31 +02:00
|
|
|
|
static void test_load_env_file_pairs(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX";
|
|
|
|
|
int fd, r;
|
2014-09-02 18:55:31 +02:00
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_strv_free_ char **l = NULL;
|
|
|
|
|
char **k, **v;
|
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(fn);
|
2014-09-02 18:55:31 +02:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
|
|
|
|
|
r = write_string_file(fn,
|
|
|
|
|
"NAME=\"Arch Linux\"\n"
|
|
|
|
|
"ID=arch\n"
|
|
|
|
|
"PRETTY_NAME=\"Arch Linux\"\n"
|
|
|
|
|
"ANSI_COLOR=\"0;36\"\n"
|
|
|
|
|
"HOME_URL=\"https://www.archlinux.org/\"\n"
|
|
|
|
|
"SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
|
2015-07-07 01:19:25 +02:00
|
|
|
|
"BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
|
|
|
|
|
WRITE_STRING_FILE_CREATE);
|
2014-09-02 18:55:31 +02:00
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
|
|
|
|
|
f = fdopen(fd, "r");
|
|
|
|
|
assert_se(f);
|
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
|
r = load_env_file_pairs(f, fn, &l);
|
2014-09-02 18:55:31 +02:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
assert_se(strv_length(l) == 14);
|
|
|
|
|
STRV_FOREACH_PAIR(k, v, l) {
|
|
|
|
|
assert_se(STR_IN_SET(*k, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL"));
|
|
|
|
|
printf("%s=%s\n", *k, *v);
|
|
|
|
|
if (streq(*k, "NAME")) assert_se(streq(*v, "Arch Linux"));
|
|
|
|
|
if (streq(*k, "ID")) assert_se(streq(*v, "arch"));
|
|
|
|
|
if (streq(*k, "PRETTY_NAME")) assert_se(streq(*v, "Arch Linux"));
|
|
|
|
|
if (streq(*k, "ANSI_COLOR")) assert_se(streq(*v, "0;36"));
|
|
|
|
|
if (streq(*k, "HOME_URL")) assert_se(streq(*v, "https://www.archlinux.org/"));
|
|
|
|
|
if (streq(*k, "SUPPORT_URL")) assert_se(streq(*v, "https://bbs.archlinux.org/"));
|
|
|
|
|
if (streq(*k, "BUG_REPORT_URL")) assert_se(streq(*v, "https://bugs.archlinux.org/"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 23:50:21 +01:00
|
|
|
|
static void test_search_and_fopen(void) {
|
|
|
|
|
const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL};
|
2018-05-16 11:35:41 +02:00
|
|
|
|
|
2016-03-02 23:50:21 +01:00
|
|
|
|
char name[] = "/tmp/test-search_and_fopen.XXXXXX";
|
2018-05-16 11:35:41 +02:00
|
|
|
|
int fd, r;
|
2016-03-02 23:50:21 +01:00
|
|
|
|
FILE *f;
|
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(name);
|
2016-03-02 23:50:21 +01:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen(name, "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen(basename(name), "r", "/", dirs, &f);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
|
|
|
|
|
r = unlink(name);
|
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_search_and_fopen_nulstr(void) {
|
|
|
|
|
const char dirs[] = "/tmp/foo/bar\0/tmp\0";
|
2018-05-16 11:35:41 +02:00
|
|
|
|
|
|
|
|
|
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-search_and_fopen.XXXXXX";
|
|
|
|
|
int fd, r;
|
2016-03-02 23:50:21 +01:00
|
|
|
|
FILE *f;
|
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(name);
|
2016-03-02 23:50:21 +01:00
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen_nulstr(name, "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
|
|
|
|
|
r = unlink(name);
|
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
|
|
|
|
|
r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
|
|
|
|
|
assert_se(r < 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_writing_tmpfile(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX";
|
2016-03-02 23:50:21 +01:00
|
|
|
|
_cleanup_free_ char *contents = NULL;
|
|
|
|
|
size_t size;
|
2018-03-05 23:05:46 +01:00
|
|
|
|
_cleanup_close_ int fd = -1;
|
2016-03-02 23:50:21 +01:00
|
|
|
|
struct iovec iov[3];
|
2018-05-16 11:35:41 +02:00
|
|
|
|
int r;
|
2016-03-02 23:50:21 +01:00
|
|
|
|
|
2017-09-21 13:52:34 +02:00
|
|
|
|
iov[0] = IOVEC_MAKE_STRING("abc\n");
|
|
|
|
|
iov[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL "\n");
|
|
|
|
|
iov[2] = IOVEC_MAKE_STRING("");
|
2016-03-02 23:50:21 +01:00
|
|
|
|
|
2016-09-13 08:20:38 +02:00
|
|
|
|
fd = mkostemp_safe(name);
|
2016-03-02 23:50:21 +01:00
|
|
|
|
printf("tmpfile: %s", name);
|
|
|
|
|
|
|
|
|
|
r = writev(fd, iov, 3);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
|
|
|
|
|
r = read_full_file(name, &contents, &size);
|
|
|
|
|
assert_se(r == 0);
|
|
|
|
|
printf("contents: %s", contents);
|
|
|
|
|
assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_tempfn(void) {
|
|
|
|
|
char *ret = NULL, *p;
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
|
|
|
|
|
assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
|
|
|
|
|
free(ret);
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0);
|
|
|
|
|
assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX"));
|
|
|
|
|
free(ret);
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0);
|
|
|
|
|
assert_se(p = startswith(ret, "/foo/bar/.#waldo"));
|
|
|
|
|
assert_se(strlen(p) == 16);
|
|
|
|
|
assert_se(in_charset(p, "0123456789abcdef"));
|
|
|
|
|
free(ret);
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret) >= 0);
|
|
|
|
|
assert_se(p = startswith(ret, "/foo/bar/.#[wuff]waldo"));
|
|
|
|
|
assert_se(strlen(p) == 16);
|
|
|
|
|
assert_se(in_charset(p, "0123456789abcdef"));
|
|
|
|
|
free(ret);
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_random_child("/foo/bar/waldo", NULL, &ret) >= 0);
|
|
|
|
|
assert_se(p = startswith(ret, "/foo/bar/waldo/.#"));
|
|
|
|
|
assert_se(strlen(p) == 16);
|
|
|
|
|
assert_se(in_charset(p, "0123456789abcdef"));
|
|
|
|
|
free(ret);
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret) >= 0);
|
|
|
|
|
assert_se(p = startswith(ret, "/foo/bar/waldo/.#[kikiriki]"));
|
|
|
|
|
assert_se(strlen(p) == 16);
|
|
|
|
|
assert_se(in_charset(p, "0123456789abcdef"));
|
|
|
|
|
free(ret);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 13:27:59 +01:00
|
|
|
|
static const char chars[] =
|
2018-12-20 11:21:36 +01:00
|
|
|
|
"Aąę„”\n루\377";
|
2018-12-18 13:27:59 +01:00
|
|
|
|
|
2020-05-25 18:20:52 +02:00
|
|
|
|
DISABLE_WARNING_TYPE_LIMITS;
|
2019-03-20 13:43:35 +01:00
|
|
|
|
|
2018-12-18 13:27:59 +01:00
|
|
|
|
static void test_fgetc(void) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
char c;
|
|
|
|
|
|
2019-04-04 12:24:38 +02:00
|
|
|
|
f = fmemopen_unlocked((void*) chars, sizeof(chars), "re");
|
2018-12-18 13:27:59 +01:00
|
|
|
|
assert_se(f);
|
|
|
|
|
|
2019-11-25 15:15:38 +01:00
|
|
|
|
for (size_t i = 0; i < sizeof(chars); i++) {
|
2018-12-18 13:27:59 +01:00
|
|
|
|
assert_se(safe_fgetc(f, &c) == 1);
|
|
|
|
|
assert_se(c == chars[i]);
|
|
|
|
|
|
2019-11-25 15:15:38 +01:00
|
|
|
|
if (ungetc(c, f) == EOF) {
|
|
|
|
|
/* EOF is -1, and hence we can't push value 255 in this way – if char is signed */
|
|
|
|
|
assert_se(c == (char) EOF);
|
|
|
|
|
assert_se(CHAR_MIN == -128); /* verify that char is signed on this platform */
|
|
|
|
|
} else {
|
|
|
|
|
assert_se(safe_fgetc(f, &c) == 1);
|
|
|
|
|
assert_se(c == chars[i]);
|
|
|
|
|
}
|
2018-12-18 13:27:59 +01:00
|
|
|
|
|
2018-12-20 11:21:36 +01:00
|
|
|
|
/* But it works when we push it properly cast */
|
2018-12-18 13:27:59 +01:00
|
|
|
|
assert_se(ungetc((unsigned char) c, f) != EOF);
|
|
|
|
|
assert_se(safe_fgetc(f, &c) == 1);
|
|
|
|
|
assert_se(c == chars[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert_se(safe_fgetc(f, &c) == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 18:20:52 +02:00
|
|
|
|
REENABLE_WARNING;
|
2019-03-20 13:43:35 +01:00
|
|
|
|
|
2017-09-24 09:10:48 +02:00
|
|
|
|
static const char buffer[] =
|
|
|
|
|
"Some test data\n"
|
2018-12-18 13:27:59 +01:00
|
|
|
|
"루Non-ascii chars: ąę„”\n"
|
2018-12-12 13:41:25 +01:00
|
|
|
|
"terminators\r\n"
|
|
|
|
|
"and even more\n\r"
|
|
|
|
|
"now the same with a NUL\n\0"
|
|
|
|
|
"and more\r\0"
|
|
|
|
|
"and even more\r\n\0"
|
|
|
|
|
"and yet even more\n\r\0"
|
2017-09-24 09:10:48 +02:00
|
|
|
|
"With newlines, and a NUL byte\0"
|
|
|
|
|
"\n"
|
|
|
|
|
"an empty line\n"
|
|
|
|
|
"an ignored line\n"
|
|
|
|
|
"and a very long line that is supposed to be truncated, because it is so long\n";
|
|
|
|
|
|
|
|
|
|
static void test_read_line_one_file(FILE *f) {
|
2017-09-22 17:55:53 +02:00
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "Some test data"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
2018-12-18 13:27:59 +01:00
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) > 0 && streq(line, "루Non-ascii chars: ąę„”"));
|
2018-12-12 13:41:25 +01:00
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 13 && streq(line, "terminators"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "and even more"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 25 && streq(line, "now the same with a NUL"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 10 && streq(line, "and more"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 16 && streq(line, "and even more"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, &line) == 20 && streq(line, "and yet even more"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
2017-09-22 17:55:53 +02:00
|
|
|
|
assert_se(read_line(f, 1024, &line) == 30 && streq(line, "With newlines, and a NUL byte"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, 1024, &line) == 1 && streq(line, ""));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, 1024, &line) == 14 && streq(line, "an empty line"));
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, NULL) == 16);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, 16, &line) == -ENOBUFS);
|
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
/* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
|
2019-04-27 02:22:40 +02:00
|
|
|
|
* character after the previous limit. Let's make use of that to continue our test. */
|
2018-12-12 13:41:25 +01:00
|
|
|
|
assert_se(read_line(f, 1024, &line) == 62 && streq(line, "line that is supposed to be truncated, because it is so long"));
|
2017-09-22 17:55:53 +02:00
|
|
|
|
line = mfree(line);
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, 1024, &line) == 0 && streq(line, ""));
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-24 09:10:48 +02:00
|
|
|
|
static void test_read_line(void) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
|
2019-04-04 12:24:38 +02:00
|
|
|
|
f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re");
|
2017-09-24 09:10:48 +02:00
|
|
|
|
assert_se(f);
|
|
|
|
|
|
|
|
|
|
test_read_line_one_file(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_read_line2(void) {
|
2018-05-16 11:35:41 +02:00
|
|
|
|
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-fileio.XXXXXX";
|
2017-09-24 09:10:48 +02:00
|
|
|
|
int fd;
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
|
|
|
|
|
fd = mkostemp_safe(name);
|
|
|
|
|
assert_se(fd >= 0);
|
|
|
|
|
assert_se((size_t) write(fd, buffer, sizeof(buffer)) == sizeof(buffer));
|
|
|
|
|
|
|
|
|
|
assert_se(lseek(fd, 0, SEEK_SET) == 0);
|
|
|
|
|
assert_se(f = fdopen(fd, "r"));
|
|
|
|
|
|
|
|
|
|
test_read_line_one_file(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void test_read_line3(void) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_free_ char *line = NULL;
|
|
|
|
|
int r;
|
|
|
|
|
|
2019-12-13 12:21:11 +01:00
|
|
|
|
f = fopen("/proc/uptime", "re");
|
2017-09-24 09:10:48 +02:00
|
|
|
|
if (!f && IN_SET(errno, ENOENT, EPERM))
|
|
|
|
|
return;
|
|
|
|
|
assert_se(f);
|
|
|
|
|
|
|
|
|
|
r = read_line(f, LINE_MAX, &line);
|
2019-03-27 09:18:50 +01:00
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
if (r == 0)
|
|
|
|
|
assert_se(line && isempty(line));
|
|
|
|
|
else
|
|
|
|
|
assert_se((size_t) r == strlen(line) + 1);
|
2017-09-24 09:10:48 +02:00
|
|
|
|
assert_se(read_line(f, LINE_MAX, NULL) == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 12:57:32 +01:00
|
|
|
|
static void test_read_line4(void) {
|
|
|
|
|
static const struct {
|
|
|
|
|
size_t length;
|
|
|
|
|
const char *string;
|
|
|
|
|
} eof_endings[] = {
|
|
|
|
|
/* Each of these will be followed by EOF and should generate the one same single string */
|
|
|
|
|
{ 3, "foo" },
|
|
|
|
|
{ 4, "foo\n" },
|
|
|
|
|
{ 4, "foo\r" },
|
|
|
|
|
{ 4, "foo\0" },
|
|
|
|
|
{ 5, "foo\n\0" },
|
|
|
|
|
{ 5, "foo\r\0" },
|
|
|
|
|
{ 5, "foo\r\n" },
|
|
|
|
|
{ 5, "foo\n\r" },
|
|
|
|
|
{ 6, "foo\r\n\0" },
|
|
|
|
|
{ 6, "foo\n\r\0" },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
|
|
2019-04-04 12:24:38 +02:00
|
|
|
|
assert_se(f = fmemopen_unlocked((void*) eof_endings[i].string, eof_endings[i].length, "r"));
|
2018-12-14 12:57:32 +01:00
|
|
|
|
|
|
|
|
|
r = read_line(f, (size_t) -1, &s);
|
|
|
|
|
assert_se((size_t) r == eof_endings[i].length);
|
|
|
|
|
assert_se(streq_ptr(s, "foo"));
|
|
|
|
|
|
|
|
|
|
assert_se(read_line(f, (size_t) -1, NULL) == 0); /* Ensure we hit EOF */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 11:52:51 +01:00
|
|
|
|
static void test_read_nul_string(void) {
|
|
|
|
|
static const char test[] = "string nr. 1\0"
|
|
|
|
|
"string nr. 2\n\0"
|
2018-12-20 10:21:16 +01:00
|
|
|
|
"\377empty string follows\0"
|
2018-12-17 11:52:51 +01:00
|
|
|
|
"\0"
|
|
|
|
|
"final string\n is empty\0"
|
|
|
|
|
"\0";
|
|
|
|
|
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
|
|
2019-04-04 12:24:38 +02:00
|
|
|
|
assert_se(f = fmemopen_unlocked((void*) test, sizeof(test)-1, "r"));
|
2018-12-17 11:52:51 +01:00
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 13 && streq_ptr(s, "string nr. 1"));
|
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 14 && streq_ptr(s, "string nr. 2\n"));
|
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
2018-12-20 10:21:16 +01:00
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 22 && streq_ptr(s, "\377empty string follows"));
|
2018-12-17 11:52:51 +01:00
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
|
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 23 && streq_ptr(s, "final string\n is empty"));
|
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
|
|
|
|
|
s = mfree(s);
|
|
|
|
|
|
|
|
|
|
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, ""));
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-17 12:26:01 +02:00
|
|
|
|
static void test_read_full_file_socket(void) {
|
|
|
|
|
_cleanup_(rm_rf_physical_and_freep) char *z = NULL;
|
|
|
|
|
_cleanup_close_ int listener = -1;
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
_cleanup_free_ char *data = NULL, *clientname = NULL;
|
2020-07-17 12:26:01 +02:00
|
|
|
|
union sockaddr_union sa;
|
|
|
|
|
const char *j;
|
|
|
|
|
size_t size;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
log_info("/* %s */", __func__);
|
|
|
|
|
|
|
|
|
|
listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
|
|
|
|
assert_se(listener >= 0);
|
|
|
|
|
|
|
|
|
|
assert_se(mkdtemp_malloc(NULL, &z) >= 0);
|
|
|
|
|
j = strjoina(z, "/socket");
|
|
|
|
|
|
|
|
|
|
assert_se(sockaddr_un_set_path(&sa.un, j) >= 0);
|
|
|
|
|
|
|
|
|
|
assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0);
|
|
|
|
|
assert_se(listen(listener, 1) >= 0);
|
|
|
|
|
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
/* Bind the *client* socket to some randomized name, to verify that this works correctly. */
|
|
|
|
|
assert_se(asprintf(&clientname, "@%" PRIx64 "/test-bindname", random_u64()) >= 0);
|
|
|
|
|
|
2020-07-17 12:26:01 +02:00
|
|
|
|
r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid);
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
|
if (r == 0) {
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
union sockaddr_union peer = {};
|
|
|
|
|
socklen_t peerlen = sizeof(peer);
|
2020-07-17 12:26:01 +02:00
|
|
|
|
_cleanup_close_ int rfd = -1;
|
|
|
|
|
/* child */
|
|
|
|
|
|
|
|
|
|
rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC);
|
|
|
|
|
assert_se(rfd >= 0);
|
|
|
|
|
|
fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name
This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of
read_full_file_full() a bit: when used a sender socket name may be
specified. If specified as NULL behaviour is as before: the client
socket name is picked by the kernel. But if specified as non-NULL the
client can pick a socket name to use when connecting. This is useful to
communicate a minimal amount of metainformation from client to server,
outside of the transport payload.
Specifically, these beefs up the service credential logic to pass an
abstract AF_UNIX socket name as client socket name when connecting via
READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name
and the eventual credential name. This allows servers implementing the
trivial credential socket logic to distinguish clients: via a simple
getpeername() it can be determined which unit is requesting a
credential, and which credential specifically.
Example: with this patch in place, in a unit file "waldo.service" a
configuration line like the following:
LoadCredential=foo:/run/quux/creds.sock
will result in a connection to the AF_UNIX socket /run/quux/creds.sock,
originating from an abstract namespace AF_UNIX socket:
@$RANDOM/unit/waldo.service/foo
(The $RANDOM is replaced by some randomized string. This is included in
the socket name order to avoid namespace squatting issues: the abstract
socket namespace is open to unprivileged users after all, and care needs
to be taken not to use guessable names)
The services listening on the /run/quux/creds.sock socket may thus
easily retrieve the name of the unit the credential is requested for
plus the credential name, via a simpler getpeername(), discarding the
random preifx and the /unit/ string.
This logic uses "/" as separator between the fields, since both unit
names and credential names appear in the file system, and thus are
designed to use "/" as outer separators. Given that it's a good safe
choice to use as separators here, too avoid any conflicts.
This is a minimal patch only: the new logic is used only for the unit
file credential logic. For other places where we use
READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this
scheme too, but this should be done carefully in later patches, since
the socket names become API that way, and we should determine the right
amount of info to pass over.
2020-11-02 12:07:51 +01:00
|
|
|
|
assert_se(getpeername(rfd, &peer.sa, &peerlen) >= 0);
|
|
|
|
|
|
|
|
|
|
assert_se(peer.un.sun_family == AF_UNIX);
|
|
|
|
|
assert_se(peerlen > offsetof(struct sockaddr_un, sun_path));
|
|
|
|
|
assert_se(peer.un.sun_path[0] == 0);
|
|
|
|
|
assert_se(streq(peer.un.sun_path + 1, clientname + 1));
|
|
|
|
|
|
2020-07-17 12:26:01 +02:00
|
|
|
|
#define TEST_STR "This is a test\nreally."
|
|
|
|
|
|
|
|
|
|
assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR));
|
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-04 20:25:06 +01:00
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO);
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0);
|
2020-07-17 12:26:01 +02:00
|
|
|
|
assert_se(size == strlen(TEST_STR));
|
|
|
|
|
assert_se(streq(data, TEST_STR));
|
|
|
|
|
|
|
|
|
|
assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0);
|
|
|
|
|
#undef TEST_STR
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-04 20:25:06 +01:00
|
|
|
|
static void test_read_full_file_offset_size(void) {
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
|
_cleanup_(unlink_and_freep) char *fn = NULL;
|
|
|
|
|
_cleanup_free_ char *rbuf = NULL;
|
|
|
|
|
size_t rbuf_size;
|
|
|
|
|
uint8_t buf[4711];
|
|
|
|
|
|
|
|
|
|
random_bytes(buf, sizeof(buf));
|
|
|
|
|
|
|
|
|
|
assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0);
|
|
|
|
|
assert_se(f = fopen(fn, "we"));
|
|
|
|
|
assert_se(fwrite(buf, 1, sizeof(buf), f) == sizeof(buf));
|
|
|
|
|
assert_se(fflush_and_check(f) >= 0);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == sizeof(buf));
|
|
|
|
|
assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == 128);
|
|
|
|
|
assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, 1234, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == sizeof(buf) - 1234);
|
|
|
|
|
assert_se(memcmp(buf + 1234, rbuf, rbuf_size) == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, 2345, 777, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == 777);
|
|
|
|
|
assert_se(memcmp(buf + 2345, rbuf, rbuf_size) == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, 4700, 20, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == 11);
|
|
|
|
|
assert_se(memcmp(buf + 4700, rbuf, rbuf_size) == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
|
|
|
|
|
assert_se(read_full_file_full(AT_FDCWD, fn, 10000, 99, 0, NULL, &rbuf, &rbuf_size) >= 0);
|
|
|
|
|
assert_se(rbuf_size == 0);
|
|
|
|
|
rbuf = mfree(rbuf);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-03 19:04:03 +02:00
|
|
|
|
int main(int argc, char *argv[]) {
|
2018-09-13 14:31:13 +02:00
|
|
|
|
test_setup_logging(LOG_DEBUG);
|
2013-09-19 23:22:59 +02:00
|
|
|
|
|
2013-04-03 19:04:03 +02:00
|
|
|
|
test_parse_env_file();
|
2013-09-12 03:50:16 +02:00
|
|
|
|
test_parse_multiline_env_file();
|
2016-08-04 18:00:00 +02:00
|
|
|
|
test_merge_env_file();
|
2017-02-18 04:56:28 +01:00
|
|
|
|
test_merge_env_file_invalid();
|
2013-07-19 10:02:50 +02:00
|
|
|
|
test_executable_is_script();
|
2013-09-14 01:41:52 +02:00
|
|
|
|
test_status_field();
|
2013-09-19 23:22:59 +02:00
|
|
|
|
test_capeff();
|
2014-08-16 14:19:07 +02:00
|
|
|
|
test_write_string_stream();
|
|
|
|
|
test_write_string_file();
|
2014-09-02 18:55:31 +02:00
|
|
|
|
test_write_string_file_no_create();
|
2015-11-13 00:54:56 +01:00
|
|
|
|
test_write_string_file_verify();
|
2014-09-02 18:55:31 +02:00
|
|
|
|
test_load_env_file_pairs();
|
2016-03-02 23:50:21 +01:00
|
|
|
|
test_search_and_fopen();
|
|
|
|
|
test_search_and_fopen_nulstr();
|
|
|
|
|
test_writing_tmpfile();
|
|
|
|
|
test_tempfn();
|
2018-12-18 13:27:59 +01:00
|
|
|
|
test_fgetc();
|
2017-09-22 17:55:53 +02:00
|
|
|
|
test_read_line();
|
2017-09-24 09:10:48 +02:00
|
|
|
|
test_read_line2();
|
|
|
|
|
test_read_line3();
|
2018-12-14 12:57:32 +01:00
|
|
|
|
test_read_line4();
|
2018-12-17 11:52:51 +01:00
|
|
|
|
test_read_nul_string();
|
2020-07-17 12:26:01 +02:00
|
|
|
|
test_read_full_file_socket();
|
2020-11-04 20:25:06 +01:00
|
|
|
|
test_read_full_file_offset_size();
|
2013-09-19 23:22:59 +02:00
|
|
|
|
|
2013-04-03 19:04:03 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|