2020-12-23 22:21:36 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2020-07-05 10:16:54 +02:00
|
|
|
|
all: add "nm-std-aux/nm-default-std.h" as replacement for "nm-default.h"
autotools projects commonly should include "config.h" as first header.
Also, commonly we need more headers, like glib.h or our nm_auto macros.
Hence, almost all our sources should as first include "nm-default.h".
However, as we build different parts, "nm-default.h" gets controlled
by the NETWORKMANAGER_COMPILATION define which autotools/meson needs
to specify in the build options.
That is confusing.
One advantage of that was, that theoretically the same sources can
be built twice, with different behavior. However, we should avoid doing
that altogether and build static libraries (once) that we link multiple
times.
Another advantage was that if NETWORKMANAGER_COMPILATION is for example
set to build a DAEMON source, there is a check that we don't include
private headers from libnm-core. However, that should be better solved
by not having public, internal and private headers in the same
directory.
Instead, introduce different "nm-default-*.h" headers that don't require
special defines and behave in a consistent way. This way, we require
fewer CFLAGS and it's immediately clear by looking at the source alone
which headers are included. Also, you will be easier see when a wrong
nm-default-*.h header gets included.
Introduce the first replacement. The others will follow.
2021-02-04 07:34:17 +01:00
|
|
|
#include "nm-default-std.h"
|
2020-07-05 10:16:54 +02:00
|
|
|
|
|
|
|
|
#include "nm-std-utils.h"
|
|
|
|
|
|
2021-01-11 15:10:38 +01:00
|
|
|
#include <assert.h>
|
2025-09-29 09:52:51 +02:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <grp.h>
|
2021-01-11 15:10:38 +01:00
|
|
|
#include <limits.h>
|
2023-05-25 15:04:13 +02:00
|
|
|
#include <net/if.h>
|
2025-09-29 09:52:51 +02:00
|
|
|
#include <pwd.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <sys/types.h>
|
2020-07-05 10:19:52 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2023-05-25 15:04:13 +02:00
|
|
|
NM_STATIC_ASSERT(NM_IFNAMSIZ == IFNAMSIZ);
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-07-05 10:19:52 +02:00
|
|
|
size_t
|
|
|
|
|
nm_utils_get_next_realloc_size(bool true_realloc, size_t requested)
|
|
|
|
|
{
|
|
|
|
|
size_t n, x;
|
|
|
|
|
|
|
|
|
|
/* https://doc.qt.io/qt-5/containers.html#growth-strategies */
|
|
|
|
|
|
|
|
|
|
if (requested <= 40) {
|
|
|
|
|
/* small allocations. Increase in small steps of 8 bytes.
|
|
|
|
|
*
|
|
|
|
|
* We get thus sizes of 8, 16, 32, 40. */
|
|
|
|
|
if (requested <= 8)
|
|
|
|
|
return 8;
|
|
|
|
|
if (requested <= 16)
|
|
|
|
|
return 16;
|
|
|
|
|
if (requested <= 32)
|
|
|
|
|
return 32;
|
|
|
|
|
|
|
|
|
|
/* The return values for < 104 are essentially hard-coded, and the choice here is
|
|
|
|
|
* made without very strong reasons.
|
|
|
|
|
*
|
|
|
|
|
* We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here.
|
|
|
|
|
* However, the next step then is already 104 (128 - 24). It's a larger gap than in
|
|
|
|
|
* the steps before.
|
|
|
|
|
*
|
|
|
|
|
* It's not clear whether some of the steps should be adjusted (or how exactly). */
|
|
|
|
|
return 40;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (requested <= 0x2000u - 24u || NM_UNLIKELY(!true_realloc)) {
|
|
|
|
|
/* mid sized allocations. Return next power of two, minus 24 bytes extra space
|
|
|
|
|
* at the beginning.
|
|
|
|
|
* That means, we double the size as we grow.
|
|
|
|
|
*
|
|
|
|
|
* With !true_realloc, it means that the caller does not intend to call
|
|
|
|
|
* realloc() but instead clone the buffer. This is for example the case, when we
|
|
|
|
|
* want to nm_explicit_bzero() the old buffer. In that case we really want to grow
|
|
|
|
|
* the buffer exponentially every time and not increment in page sizes of 4K (below).
|
|
|
|
|
*
|
|
|
|
|
* We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */
|
|
|
|
|
|
|
|
|
|
if (NM_UNLIKELY(requested > SIZE_MAX / 2u - 24u))
|
2021-01-11 15:10:38 +01:00
|
|
|
goto out_huge;
|
2020-07-05 10:19:52 +02:00
|
|
|
|
|
|
|
|
x = requested + 24u;
|
|
|
|
|
n = 128u;
|
|
|
|
|
while (n < x) {
|
|
|
|
|
n <<= 1;
|
|
|
|
|
nm_assert(n > 128u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_assert(n > 24u && n - 24u >= requested);
|
|
|
|
|
return n - 24u;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-12 09:49:57 +01:00
|
|
|
if (NM_UNLIKELY(requested > SIZE_MAX - 0x1000u - 24u)) {
|
2021-01-11 15:10:38 +01:00
|
|
|
/* overflow happened. */
|
|
|
|
|
goto out_huge;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-12 09:49:57 +01:00
|
|
|
/* For large allocations (with !true_realloc) we allocate memory in chunks of
|
|
|
|
|
* 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus
|
|
|
|
|
* realloc() is efficient by just reordering pages. */
|
|
|
|
|
n = ((requested + (0x0FFFu + 24u)) & ~((size_t) 0x0FFFu)) - 24u;
|
|
|
|
|
nm_assert(n >= requested);
|
2020-07-05 10:19:52 +02:00
|
|
|
return n;
|
2021-01-11 15:10:38 +01:00
|
|
|
|
|
|
|
|
out_huge:
|
|
|
|
|
if (sizeof(size_t) > 4u) {
|
|
|
|
|
/* on s390x (64 bit), gcc with LTO can complain that the size argument to
|
|
|
|
|
* malloc must not be larger than 9223372036854775807.
|
|
|
|
|
*
|
|
|
|
|
* Work around that by returning SSIZE_MAX. It should be plenty still! */
|
|
|
|
|
assert(requested <= (size_t) SSIZE_MAX);
|
|
|
|
|
return (size_t) SSIZE_MAX;
|
|
|
|
|
}
|
|
|
|
|
return SIZE_MAX;
|
2020-07-05 10:19:52 +02:00
|
|
|
}
|
2023-08-18 14:56:42 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2025-09-29 09:52:51 +02:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
nm_utils_set_effective_user(const char *user, char *errbuf, size_t errbuf_len)
|
|
|
|
|
{
|
|
|
|
|
struct passwd *pwentry;
|
|
|
|
|
int errsv;
|
|
|
|
|
char error[1024];
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
pwentry = getpwnam(user);
|
|
|
|
|
if (!pwentry) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
if (errsv == 0) {
|
|
|
|
|
snprintf(errbuf, errbuf_len, "user not found");
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"error getting user entry: %d (%s)\n",
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (setgid(pwentry->pw_gid) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"failed to change group to %u: %d (%s)\n",
|
|
|
|
|
pwentry->pw_gid,
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (initgroups(user, pwentry->pw_gid) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"failed to reset supplementary group list to %u: %d (%s)\n",
|
|
|
|
|
pwentry->pw_gid,
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (setuid(pwentry->pw_uid) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"failed to change user to %u: %d (%s)\n",
|
|
|
|
|
pwentry->pw_uid,
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
nm_utils_read_file_to_stdout(const char *filename, char *errbuf, size_t errbuf_len)
|
|
|
|
|
{
|
|
|
|
|
nm_auto_close int fd = -1;
|
|
|
|
|
char buffer[4096];
|
|
|
|
|
char error[1024];
|
|
|
|
|
ssize_t bytes_read;
|
|
|
|
|
int errsv;
|
|
|
|
|
|
|
|
|
|
fd = open(filename, O_RDONLY);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"error opening the file: %d (%s)",
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
|
|
|
|
|
if (fwrite(buffer, 1, bytes_read, stdout) != (size_t) bytes_read) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"error writing to stdout: %d (%s)",
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bytes_read < 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
snprintf(errbuf,
|
|
|
|
|
errbuf_len,
|
|
|
|
|
"error reading the file: %d (%s)",
|
|
|
|
|
errsv,
|
|
|
|
|
strerror_r(errsv, error, sizeof(error)));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2023-08-18 14:56:42 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* _nm_strerror_r:
|
|
|
|
|
* @errsv: the errno passed to strerror_r()
|
|
|
|
|
* @buf: the string buffer, must be non-null
|
|
|
|
|
* @buf_size: the size of the buffer, must be positive.
|
|
|
|
|
*
|
|
|
|
|
* A wrapper around strerror_r(). Does little else, aside clearing up the
|
|
|
|
|
* confusion about the different versions of the function.
|
|
|
|
|
*
|
|
|
|
|
* errno is preserved.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the error string. This is either a static strong or @buf. It
|
|
|
|
|
* is not guaranteed to be @buf.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
_nm_strerror_r(int errsv, char *buf, size_t buf_size)
|
|
|
|
|
{
|
|
|
|
|
NM_AUTO_PROTECT_ERRNO(errsv2);
|
|
|
|
|
char *buf2;
|
|
|
|
|
|
|
|
|
|
nm_assert(buf);
|
|
|
|
|
nm_assert(buf_size > 0);
|
|
|
|
|
|
|
|
|
|
#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE)
|
|
|
|
|
/* XSI-compliant */
|
|
|
|
|
if (strerror_r(errsv, buf, buf_size) != 0) {
|
|
|
|
|
snprintf(buf, buf_size, "Unspecified errno %d", errsv);
|
|
|
|
|
}
|
|
|
|
|
buf2 = buf;
|
|
|
|
|
#else
|
|
|
|
|
/* GNU-specific */
|
|
|
|
|
buf2 = strerror_r(errsv, buf, buf_size);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
nm_assert(buf2);
|
|
|
|
|
return buf2;
|
|
|
|
|
}
|