systemd: 258.3 -> 259 (#472008)

This commit is contained in:
Arian van Putten
2026-01-30 01:15:42 +00:00
committed by GitHub
49 changed files with 81 additions and 3083 deletions
@@ -14,10 +14,10 @@ Original-Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index 57ab68deb6..8e281b8f2c 100644
index 25e229bf3e..b9af1c3b13 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -72,6 +72,8 @@ bool fstab_is_extrinsic(const char *mount, const char *opts) {
@@ -76,6 +76,8 @@ bool fstab_is_extrinsic(const char *mount, const char *opts) {
/* Don't bother with the OS data itself */
if (PATH_IN_SET(mount,
"/",
@@ -27,7 +27,7 @@ index 57ab68deb6..8e281b8f2c 100644
"/etc"))
return true;
diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c
index 45c6db9245..d7283b7fa6 100644
index 46f208824a..3a625ad2a6 100644
--- a/src/shutdown/umount.c
+++ b/src/shutdown/umount.c
@@ -178,8 +178,10 @@ int mount_points_list_get(FILE *f, MountPoint **head) {
@@ -10,10 +10,10 @@ container, so checking early whether it exists will fail.
1 file changed, 2 insertions(+)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index ab8746c442..480a9c55c6 100644
index e4fccaa3a7..05e6be8874 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -6165,6 +6165,7 @@ static int run(int argc, char *argv[]) {
@@ -6208,6 +6208,7 @@ static int run(int argc, char *argv[]) {
goto finish;
}
} else {
@@ -21,7 +21,7 @@ index ab8746c442..480a9c55c6 100644
_cleanup_free_ char *p = NULL;
if (arg_pivot_root_new)
@@ -6184,6 +6185,7 @@ static int run(int argc, char *argv[]) {
@@ -6227,6 +6228,7 @@ static int run(int argc, char *argv[]) {
log_error_errno(r, "Unable to determine if %s looks like it has an OS tree (i.e. whether /usr/ exists): %m", arg_directory);
goto finish;
}
@@ -16,7 +16,7 @@ Original-Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
2 files changed, 7 insertions(+), 21 deletions(-)
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index 58f2463104..0c263430a7 100644
index 862d57dcb6..8ba0ca4486 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -46,10 +46,10 @@ systemdsystemconfdir=${systemd_system_conf_dir}
@@ -46,10 +46,10 @@ index 58f2463104..0c263430a7 100644
systemd_sleep_dir=${prefix}/lib/systemd/system-sleep
diff --git a/src/libsystemd/sd-path/path-lookup.c b/src/libsystemd/sd-path/path-lookup.c
index 3ddbda0e9f..3eddc80c90 100644
index 389d7e2ad9..0819689ffa 100644
--- a/src/libsystemd/sd-path/path-lookup.c
+++ b/src/libsystemd/sd-path/path-lookup.c
@@ -64,11 +64,7 @@ int runtime_directory(RuntimeScope scope, const char *suffix, char **ret) {
@@ -86,11 +86,7 @@ int runtime_directory(RuntimeScope scope, const char *fallback_suffix, char **re
}
static const char* const user_data_unit_paths[] = {
@@ -61,7 +61,7 @@ index 3ddbda0e9f..3eddc80c90 100644
NULL
};
@@ -476,16 +472,13 @@ int lookup_paths_init(
@@ -498,16 +494,13 @@ int lookup_paths_init(
persistent_config,
SYSTEM_CONFIG_UNIT_DIR,
"/etc/systemd/system",
@@ -79,7 +79,7 @@ index 3ddbda0e9f..3eddc80c90 100644
STRV_IFNOTNULL(generator_late));
break;
@@ -503,13 +496,10 @@ int lookup_paths_init(
@@ -525,13 +518,10 @@ int lookup_paths_init(
add = strv_new(persistent_config,
USER_CONFIG_UNIT_DIR,
"/etc/systemd/user",
@@ -95,7 +95,7 @@ index 3ddbda0e9f..3eddc80c90 100644
break;
case RUNTIME_SCOPE_USER:
@@ -648,7 +638,6 @@ void lookup_paths_log(LookupPaths *lp) {
@@ -670,7 +660,6 @@ void lookup_paths_log(LookupPaths *lp) {
static const char* const system_generator_paths[] = {
"/run/systemd/system-generators",
"/etc/systemd/system-generators",
@@ -103,7 +103,7 @@ index 3ddbda0e9f..3eddc80c90 100644
SYSTEM_GENERATOR_DIR,
NULL,
};
@@ -656,7 +645,6 @@ static const char* const system_generator_paths[] = {
@@ -678,7 +667,6 @@ static const char* const system_generator_paths[] = {
static const char* const user_generator_paths[] = {
"/run/systemd/user-generators",
"/etc/systemd/user-generators",
@@ -111,7 +111,7 @@ index 3ddbda0e9f..3eddc80c90 100644
USER_GENERATOR_DIR,
NULL,
};
@@ -664,7 +652,6 @@ static const char* const user_generator_paths[] = {
@@ -686,7 +674,6 @@ static const char* const user_generator_paths[] = {
static const char* const system_env_generator_paths[] = {
"/run/systemd/system-environment-generators",
"/etc/systemd/system-environment-generators",
@@ -119,7 +119,7 @@ index 3ddbda0e9f..3eddc80c90 100644
SYSTEM_ENV_GENERATOR_DIR,
NULL,
};
@@ -672,7 +659,6 @@ static const char* const system_env_generator_paths[] = {
@@ -694,7 +681,6 @@ static const char* const system_env_generator_paths[] = {
static const char* const user_env_generator_paths[] = {
"/run/systemd/user-environment-generators",
"/etc/systemd/user-environment-generators",
@@ -13,10 +13,10 @@ in containers.
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/manager.c b/src/core/manager.c
index d85896577f..2a4782a55e 100644
index 7938c989af..c1ae20dfc0 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1575,7 +1575,8 @@ static unsigned manager_dispatch_stop_when_bound_queue(Manager *m) {
@@ -1578,7 +1578,8 @@ static unsigned manager_dispatch_stop_when_bound_queue(Manager *m) {
if (!unit_is_bound_by_inactive(u, &culprit))
continue;
@@ -11,7 +11,7 @@ Subject: [PATCH] hostnamed, localed, timedated: disable methods that change
3 files changed, 25 insertions(+)
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 06fb3f2878..b656e511b7 100644
index fbe1b2fa7a..30e7c75279 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -1388,6 +1388,9 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
@@ -35,7 +35,7 @@ index 06fb3f2878..b656e511b7 100644
context_read_machine_info(c);
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 11d9130bbc..dd4d9e59ea 100644
index 041ba29cd8..02fbe3e278 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -221,6 +221,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
@@ -69,7 +69,7 @@ index 11d9130bbc..dd4d9e59ea 100644
r = x11_context_verify_and_warn(&in, LOG_ERR, error);
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 662cc29ac5..57f3413dc6 100644
index d1ef772b56..9b1fe439f1 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -678,6 +678,10 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
@@ -35,10 +35,10 @@ index 3a13e04a27..4fd58068a1 100644
<literal>Etc/UTC</literal>. The resulting link should lead to the
corresponding binary
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 55931a2546..2e00bd7539 100644
index 8c776960e1..2e0563bd00 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1410,7 +1410,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
@@ -1443,7 +1443,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
assert(ret);
@@ -47,7 +47,7 @@ index 55931a2546..2e00bd7539 100644
if (!f)
return -errno;
@@ -1451,7 +1451,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
@@ -1488,7 +1488,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
assert(ret);
@@ -56,7 +56,7 @@ index 55931a2546..2e00bd7539 100644
if (!f)
return -errno;
@@ -1562,7 +1562,7 @@ int verify_timezone(const char *name, int log_level) {
@@ -1603,7 +1603,7 @@ int verify_timezone(const char *name, int log_level) {
if (p - name >= PATH_MAX)
return -ENAMETOOLONG;
@@ -65,7 +65,7 @@ index 55931a2546..2e00bd7539 100644
fd = open(t, O_RDONLY|O_CLOEXEC);
if (fd < 0)
@@ -1614,7 +1614,7 @@ int get_timezone(char **ret) {
@@ -1675,7 +1675,7 @@ int get_timezone(char **ret) {
if (r < 0)
return r; /* Return EINVAL if not a symlink */
@@ -75,19 +75,19 @@ index 55931a2546..2e00bd7539 100644
return -EINVAL;
if (!timezone_is_valid(e, LOG_DEBUG))
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 5ed6d3a9d2..f922d91a9d 100644
index 55bd273f21..d00aa16592 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -618,7 +618,7 @@ static int prompt_timezone(int rfd) {
@@ -571,7 +571,7 @@ static int prompt_timezone(int rfd, sd_varlink **mute_console_link) {
static int process_timezone(int rfd) {
static int process_timezone(int rfd, sd_varlink **mute_console_link) {
_cleanup_close_ int pfd = -EBADF;
- _cleanup_free_ char *f = NULL, *relpath = NULL;
+ _cleanup_free_ char *f = NULL;
const char *e;
int r;
@@ -664,12 +664,9 @@ static int process_timezone(int rfd) {
@@ -617,12 +617,9 @@ static int process_timezone(int rfd, sd_varlink **mute_console_link) {
if (isempty(arg_timezone))
return 0;
@@ -103,10 +103,10 @@ index 5ed6d3a9d2..f922d91a9d 100644
return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 480a9c55c6..02c9ae6608 100644
index 05e6be8874..6dc00ff416 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1821,8 +1821,8 @@ int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid
@@ -1808,8 +1808,8 @@ int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid
static const char *timezone_from_path(const char *path) {
return PATH_STARTSWITH_SET(
path,
@@ -118,7 +118,7 @@ index 480a9c55c6..02c9ae6608 100644
static bool etc_writable(void) {
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 57f3413dc6..6a456fe601 100644
index 9b1fe439f1..916886aeff 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -266,7 +266,7 @@ static int context_read_data(Context *c) {
@@ -10,15 +10,15 @@ NixOS has an option to link the xkb data files to /etc/X11, but not to
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/locale/localectl.c b/src/locale/localectl.c
index c595f81b40..90033f6566 100644
index 63703007ad..50def81940 100644
--- a/src/locale/localectl.c
+++ b/src/locale/localectl.c
@@ -302,7 +302,7 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) {
} state = NONE, look_for;
int r;
@@ -295,7 +295,7 @@ static const char* xkb_directory(void) {
static const char *cached = NULL;
- f = fopen("/usr/share/X11/xkb/rules/base.lst", "re");
+ f = fopen("/etc/X11/xkb/rules/base.lst", "re");
if (!f)
return log_error_errno(errno, "Failed to open keyboard mapping list: %m");
if (!cached)
- cached = secure_getenv("SYSTEMD_XKB_DIRECTORY") ?: "/usr/share/X11/xkb";
+ cached = secure_getenv("SYSTEMD_XKB_DIRECTORY") ?: "/etc/X11/xkb";
return cached;
}
@@ -12,7 +12,7 @@ files that I might have missed.
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/basic/constants.h b/src/basic/constants.h
index 7a09fbf878..ee3378f109 100644
index a26cff4062..3800c37e1b 100644
--- a/src/basic/constants.h
+++ b/src/basic/constants.h
@@ -40,13 +40,15 @@
@@ -10,10 +10,10 @@ This is needed for NixOS to use such scripts as systemd directory is immutable.
1 file changed, 1 insertion(+)
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index 3317068e47..021a3c0699 100644
index 25882970ef..599dd0a63f 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -358,6 +358,7 @@ static void notify_supervisor(void) {
@@ -329,6 +329,7 @@ static void notify_supervisor(void) {
int main(int argc, char *argv[]) {
static const char* const dirs[] = {
SYSTEM_SHUTDOWN_PATH,
@@ -9,7 +9,7 @@ This is needed for NixOS to use such scripts as systemd directory is immutable.
1 file changed, 1 insertion(+)
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
index 3390ebe0c0..affc823bc4 100644
index 4fa6f16fcd..012cf16f90 100644
--- a/src/sleep/sleep.c
+++ b/src/sleep/sleep.c
@@ -248,6 +248,7 @@ static int execute(
@@ -10,7 +10,7 @@ systemd itself uses extensively.
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index a3a82574dd..b836dba7f1 100644
index 5b2baef4e1..7f366e4544 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -9,11 +9,11 @@
@@ -16,10 +16,10 @@ executables that are being called from managers.
1 file changed, 8 insertions(+)
diff --git a/src/core/manager.c b/src/core/manager.c
index 2a4782a55e..8dfdc227a1 100644
index c1ae20dfc0..c120e555a2 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -3977,9 +3977,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
@@ -3994,9 +3994,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
* adjust generated units to that. Let's pass down some bits of information that are easy for us to
* determine (but a bit harder for generator scripts to determine), as environment variables. */
@@ -27,10 +27,10 @@ filename_is_valid with path_is_valid.
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 57e7a91f93..faf45e4807 100644
index e089cfbc5e..35e135945b 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -724,7 +724,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
@@ -741,7 +741,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
fn = strjoina("libtss2-tcti-", driver, ".so.0");
/* Better safe than sorry, let's refuse strings that cannot possibly be valid driver early, before going to disk. */
@@ -38,4 +38,4 @@ index 57e7a91f93..faf45e4807 100644
+ if (!path_is_valid(fn))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name '%s' not valid, refusing.", driver);
context->tcti_dl = dlopen(fn, RTLD_NOW|RTLD_NODELETE);
const char *dle = NULL;
@@ -30,11 +30,11 @@ are written into `$XDG_CONFIG_HOME/systemd/user`.
1 file changed, 3 insertions(+)
diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
index 53bc57186a..0ed12ce931 100644
index a28180922a..22c9c8fdbd 100644
--- a/src/systemctl/systemctl-edit.c
+++ b/src/systemctl/systemctl-edit.c
@@ -330,6 +330,9 @@ int verb_edit(int argc, char *argv[], void *userdata) {
sd_bus *bus;
@@ -336,6 +336,9 @@ int verb_edit(int argc, char *argv[], void *userdata) {
sd_bus *bus = NULL;
int r;
+ if (!arg_runtime && arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
@@ -8,10 +8,10 @@ Subject: [PATCH] meson.build: do not create systemdstatedir
1 file changed, 1 deletion(-)
diff --git a/meson.build b/meson.build
index 238b935372..b0b67c9b99 100644
index 4746146a98..c13b72af56 100644
--- a/meson.build
+++ b/meson.build
@@ -2791,7 +2791,6 @@ install_data('LICENSE.GPL2',
@@ -2859,7 +2859,6 @@ install_data('LICENSE.GPL2',
install_subdir('LICENSES',
install_dir : docdir)
@@ -8,10 +8,10 @@ Subject: [PATCH] meson: Don't link ssh dropins
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
index b0b67c9b99..4721b7482b 100644
index c13b72af56..32f6e791e7 100644
--- a/meson.build
+++ b/meson.build
@@ -219,13 +219,13 @@ sshconfdir = get_option('sshconfdir')
@@ -214,13 +214,13 @@ sshconfdir = get_option('sshconfdir')
if sshconfdir == ''
sshconfdir = sysconfdir / 'ssh/ssh_config.d'
endif
@@ -8,7 +8,7 @@ Subject: [PATCH] install: unit_file_exists_full: follow symlinks
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/install.c b/src/shared/install.c
index 8195436564..dfbc35f357 100644
index a22c6df2f7..7e900c8bcd 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -3227,7 +3227,7 @@ int unit_file_exists_full(RuntimeScope scope, const LookupPaths *lp, const char
@@ -13,7 +13,7 @@ directly.
1 file changed, 11 insertions(+)
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 40305c046b..c3ed57c54e 100644
index 96d0dd5c2b..ee7cc4bd17 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -25,6 +25,11 @@
@@ -28,7 +28,7 @@ index 40305c046b..c3ed57c54e 100644
static int advance_tstamp(int fd, usec_t epoch) {
assert(fd >= 0);
@@ -203,6 +208,12 @@ static int run(int argc, char *argv[]) {
@@ -204,6 +209,12 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to parse fallback server strings: %m");
+21 -71
View File
@@ -25,7 +25,6 @@
python3Packages,
# Mandatory dependencies
libcap,
util-linux,
kbd,
kmod,
@@ -203,17 +202,17 @@ let
# command:
# $ curl -s https://api.github.com/repos/systemd/systemd/releases/latest | \
# jq '.created_at|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime'
releaseTimestamp = "1734643670";
releaseTimestamp = "1766012573";
in
stdenv.mkDerivation (finalAttrs: {
inherit pname;
version = "258.3";
version = "259";
src = fetchFromGitHub {
owner = "systemd";
repo = "systemd";
rev = "v${finalAttrs.version}";
hash = "sha256-wpg/0z7xrB8ysPaa/zNp1mz+yYRCGyXz0ODZcKapovM=";
hash = "sha256-lJUX1sWRouhEEPZoA9UjjOy5IUZYGGV8pltAU0E4Dsg=";
};
# On major changes, or when otherwise required, you *must* :
@@ -252,39 +251,6 @@ stdenv.mkDerivation (finalAttrs: {
]
++ lib.optionals (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isGnu) [
./0020-timesyncd-disable-NSCD-when-DNSSEC-validation-is-dis.patch
]
++ lib.optionals stdenv.hostPlatform.isMusl [
# Patchset to build with musl by an upstream systemd contributor:
# https://github.com/systemd/systemd/pull/37788
# This is vendored here because of the lack of permanent patch urls for the unmerged PR
./musl/0001-musl-meson-allow-to-choose-libc-implementation.patch
./musl/0002-musl-meson-do-not-use-libcrypt-libxcrypt.patch
./musl/0003-musl-meson-explicitly-link-with-libintl-when-necessa.patch
./musl/0004-musl-meson-explicitly-set-_LARGEFILE64_SOURCE.patch
./musl/0005-musl-meson-make-musl-not-define-wchar_t-in-their-hea.patch
./musl/0006-musl-meson-check-existence-of-renameat2.patch
./musl/0007-musl-meson-gracefully-disable-gshadow-idn-nss-and-ut.patch
./musl/0008-musl-introduce-dummy-gshadow-header-file-for-userdb.patch
./musl/0009-musl-add-fallback-parse_printf_format-implementation.patch
./musl/0010-musl-introduce-GNU-specific-version-of-strerror_r.patch
./musl/0011-musl-make-strptime-accept-z.patch
./musl/0012-musl-make-strtoll-accept-strings-start-with-dot.patch
./musl/0013-musl-introduce-strerrorname_np.patch
./musl/0014-musl-introduce-dummy-functions-for-mallinfo-malloc_i.patch
./musl/0015-musl-introduce-dummy-function-for-gnu_get_libc_versi.patch
./musl/0016-musl-define-__THROW-when-not-defined.patch
./musl/0017-musl-replace-sys-prctl.h-with-our-own-implementation.patch
./musl/0018-musl-replace-netinet-if_ether.h-with-our-own-impleme.patch
./musl/0019-musl-add-missing-FTW_CONTINUE-macro.patch
./musl/0020-musl-add-several-missing-statx-macros.patch
./musl/0021-musl-avoid-conflict-between-fcntl.h-and-our-forward..patch
./musl/0022-musl-redefine-HOST_NAME_MAX-as-64.patch
./musl/0023-musl-avoid-multiple-evaluations-in-CPU_ISSET_S-macro.patch
./musl/0024-musl-core-there-is-one-less-usable-signal-when-built.patch
./musl/0025-musl-build-path-fix-reading-DT_RUNPATH-or-DT_RPATH.patch
./musl/0026-musl-format-util-use-llu-for-formatting-rlim_t.patch
./musl/0027-musl-time-util-skip-tm.tm_wday-check.patch
./musl/0028-musl-glob-util-filter-out-.-and-.-even-if-GLOB_ALTDI.patch
];
postPatch = ''
@@ -366,7 +332,6 @@ stdenv.mkDerivation (finalAttrs: {
buildInputs = [
libxcrypt
(if withPam then libcap else libcap.override { usePam = false; })
libuuid
linuxHeaders
]
@@ -481,6 +446,10 @@ stdenv.mkDerivation (finalAttrs: {
(lib.mesonOption "mount-path" "${lib.getOutput "mount" util-linux}/bin/mount")
(lib.mesonOption "umount-path" "${lib.getOutput "mount" util-linux}/bin/umount")
# Swap
(lib.mesonOption "swapon-path" "${lib.getOutput "swap" util-linux}/sbin/swapon")
(lib.mesonOption "swapoff-path" "${lib.getOutput "swap" util-linux}/sbin/swapoff")
# SSH
(lib.mesonOption "sshconfdir" "")
(lib.mesonOption "sshdconfdir" "no")
@@ -620,19 +589,6 @@ stdenv.mkDerivation (finalAttrs: {
"man/systemd-makefs@.service.xml"
];
}
{
search = "/sbin/swapon";
replacement = "${lib.getOutput "swap" util-linux}/sbin/swapon";
where = [
"src/core/swap.c"
"src/basic/unit-def.h"
];
}
{
search = "/sbin/swapoff";
replacement = "${lib.getOutput "swap" util-linux}/sbin/swapoff";
where = [ "src/core/swap.c" ];
}
{
search = "/bin/echo";
replacement = "${coreutils}/bin/echo";
@@ -766,29 +722,23 @@ stdenv.mkDerivation (finalAttrs: {
--replace "SYSTEMD_CGROUP_AGENTS_PATH" "_SYSTEMD_CGROUP_AGENT_PATH"
'';
env.NIX_CFLAGS_COMPILE = toString (
[
# Can't say ${polkit.bin}/bin/pkttyagent here because that would
# lead to a cyclic dependency.
"-UPOLKIT_AGENT_BINARY_PATH"
"-DPOLKIT_AGENT_BINARY_PATH=\"/run/current-system/sw/bin/pkttyagent\""
env.NIX_CFLAGS_COMPILE = toString [
# Can't say ${polkit.bin}/bin/pkttyagent here because that would
# lead to a cyclic dependency.
"-UPOLKIT_AGENT_BINARY_PATH"
"-DPOLKIT_AGENT_BINARY_PATH=\"/run/current-system/sw/bin/pkttyagent\""
# Set the release_agent on /sys/fs/cgroup/systemd to the
# currently running systemd (/run/current-system/systemd) so
# that we don't use an obsolete/garbage-collected release agent.
"-USYSTEMD_CGROUP_AGENTS_PATH"
"-DSYSTEMD_CGROUP_AGENTS_PATH=\"/run/current-system/systemd/lib/systemd/systemd-cgroups-agent\""
# Set the release_agent on /sys/fs/cgroup/systemd to the
# currently running systemd (/run/current-system/systemd) so
# that we don't use an obsolete/garbage-collected release agent.
"-USYSTEMD_CGROUP_AGENTS_PATH"
"-DSYSTEMD_CGROUP_AGENTS_PATH=\"/run/current-system/systemd/lib/systemd/systemd-cgroups-agent\""
"-USYSTEMD_BINARY_PATH"
"-DSYSTEMD_BINARY_PATH=\"/run/current-system/systemd/lib/systemd/systemd\""
"-USYSTEMD_BINARY_PATH"
"-DSYSTEMD_BINARY_PATH=\"/run/current-system/systemd/lib/systemd/systemd\""
];
]
++ lib.optionals stdenv.hostPlatform.isMusl [
"-D__UAPI_DEF_ETHHDR=0"
]
);
doCheck = true;
doCheck = false;
# trigger the test -n "$DESTDIR" || mutate in upstreams build system
preInstall = ''
@@ -1,96 +0,0 @@
From 0e2b9909fed24a682c8566f9df8bbac4e9347186 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 7 Jul 2025 14:11:19 +0900
Subject: [PATCH 01/30] musl: meson: allow to choose libc implementation
This also introduces skelton directories for storing musl specific code.
---
meson.build | 17 +++++++++++++++--
meson_options.txt | 2 ++
src/libc/meson.build | 2 ++
src/libc/musl/meson.build | 5 +++++
4 files changed, 24 insertions(+), 2 deletions(-)
create mode 100644 src/libc/musl/meson.build
diff --git a/meson.build b/meson.build
index 238b935372..bea62f0eb6 100644
--- a/meson.build
+++ b/meson.build
@@ -72,7 +72,10 @@ conf.set10('SD_BOOT', false)
# Create a title-less summary section early, so it ends up first in the output.
# More items are added later after they have been detected.
-summary({'build mode' : get_option('mode')})
+summary({
+ 'libc' : get_option('libc'),
+ 'build mode' : get_option('mode'),
+})
#####################################################################
@@ -2042,7 +2045,14 @@ dbus_programs = []
boot_stubs = []
# This is similar to system_includes below, but for passing custom_target().
-system_include_args = [
+system_include_args = []
+if get_option('libc') == 'musl'
+ system_include_args += [
+ '-isystem', meson.project_build_root() / 'src/include/musl',
+ '-isystem', meson.project_source_root() / 'src/include/musl',
+ ]
+endif
+system_include_args += [
'-isystem', meson.project_build_root() / 'src/include/override',
'-isystem', meson.project_source_root() / 'src/include/override',
'-isystem', meson.project_build_root() / 'src/include/uapi',
@@ -2060,6 +2070,9 @@ system_includes = [
is_system : true,
),
]
+if get_option('libc') == 'musl'
+ system_includes += include_directories('src/include/musl', is_system : true)
+endif
basic_includes = [
include_directories(
diff --git a/meson_options.txt b/meson_options.txt
index d8dec33ec4..ad203ba301 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -397,6 +397,8 @@ option('ima', type : 'boolean',
option('ipe', type : 'boolean',
description : 'IPE support')
+option('libc', type : 'combo', choices : ['glibc', 'musl'],
+ description : 'libc implementation to be used')
option('acl', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'libacl support')
option('audit', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
diff --git a/src/libc/meson.build b/src/libc/meson.build
index eeee98c9d6..306512ffd7 100644
--- a/src/libc/meson.build
+++ b/src/libc/meson.build
@@ -16,6 +16,8 @@ libc_wrapper_sources = files(
'xattr.c',
)
+subdir('musl')
+
sources += libc_wrapper_sources
libc_wrapper_static = static_library(
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
new file mode 100644
index 0000000000..a876230c67
--- /dev/null
+++ b/src/libc/musl/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+if get_option('libc') != 'musl'
+ subdir_done()
+endif
--
2.51.0
@@ -1,38 +0,0 @@
From 03010a0716f509b448c84a35bc2723a6f08e0c09 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 23 Jul 2025 10:24:14 +0900
Subject: [PATCH 02/30] musl: meson: do not use libcrypt/libxcrypt
Otherwise, when both glibc and musl are installed, libxcrypt or glibc's
libcrypt may be picked even when we are building systemd with musl.
Let's not use libcrypt/libxcrypt in that case.
---
meson.build | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/meson.build b/meson.build
index bea62f0eb6..06be36409f 100644
--- a/meson.build
+++ b/meson.build
@@ -1013,10 +1013,14 @@ else
libatomic = []
endif
-libcrypt = dependency('libcrypt', 'libxcrypt', required : false)
-if not libcrypt.found()
- # fallback to use find_library() if libcrypt is provided by glibc, e.g. for LibreELEC.
- libcrypt = cc.find_library('crypt')
+if get_option('libc') == 'musl'
+ libcrypt = []
+else
+ libcrypt = dependency('libcrypt', 'libxcrypt', required : false)
+ if not libcrypt.found()
+ # fallback to use find_library() if libcrypt is provided by glibc, e.g. for LibreELEC.
+ libcrypt = cc.find_library('crypt')
+ endif
endif
foreach func : [
--
2.51.0
@@ -1,56 +0,0 @@
From bf4cb5aeeef23c5f12e6d2258bb8f3d12059f59f Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sat, 6 Sep 2025 16:25:41 +0900
Subject: [PATCH 03/30] musl: meson: explicitly link with libintl when
necessary
On some musl based distributions provides dgettext() by libintl.so.
Hence, we need to add dependency in that case.
---
meson.build | 17 +++++++++++++++++
src/home/meson.build | 1 +
2 files changed, 18 insertions(+)
diff --git a/meson.build b/meson.build
index 06be36409f..054752c339 100644
--- a/meson.build
+++ b/meson.build
@@ -1002,6 +1002,23 @@ libm = cc.find_library('m')
libdl = cc.find_library('dl')
libcap = dependency('libcap')
+# On some distributions that uses musl (e.g. Alpine), libintl.h may be provided by gettext rather than musl.
+# In that case, we need to explicitly link with libintl.so.
+if get_option('libc') == 'musl'
+ if cc.has_function('dgettext', prefix : '''#include <libintl.h>''', args : '-D_GNU_SOURCE')
+ libintl = []
+ else
+ libintl = cc.find_library('intl')
+ if not cc.has_function('dgettext', prefix : '''#include <libintl.h>''', args : '-D_GNU_SOURCE',
+ dependencies : libintl)
+ error('dgettext() not found.')
+ endif
+ endif
+else
+ # When building with glibc, we assume that libintl.h is provided by glibc.
+ libintl = []
+endif
+
# On some architectures, libatomic is required. But on some installations,
# it is found, but actual linking fails. So let's try to use it opportunistically.
# If it is installed, but not needed, it will be dropped because of --as-needed.
diff --git a/src/home/meson.build b/src/home/meson.build
index 1937e6f56c..3305334707 100644
--- a/src/home/meson.build
+++ b/src/home/meson.build
@@ -115,6 +115,7 @@ modules += [
'sources' : pam_systemd_home_sources,
'dependencies' : [
libcrypt,
+ libintl,
libpam_misc,
libpam,
threads,
--
2.51.0
@@ -1,32 +0,0 @@
From 59f993d1acb069f3f2b7d6c71b91696388692702 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 10 Jun 2025 00:29:46 +0900
Subject: [PATCH 04/30] musl: meson: explicitly set _LARGEFILE64_SOURCE
glibc sets it when _GNU_SOURCE is defined, however, musl does not.
Let's explicitly define it to make getdents64() and struct dirent64
available even when building with musl.
---
meson.build | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/meson.build b/meson.build
index 054752c339..37fb1c2765 100644
--- a/meson.build
+++ b/meson.build
@@ -560,6 +560,12 @@ conf.set10('HAVE_WARNING_ZERO_AS_NULL_POINTER_CONSTANT', have)
conf.set('_GNU_SOURCE', 1)
conf.set('__SANE_USERSPACE_TYPES__', true)
+if get_option('libc') == 'musl'
+ # glibc always defines _LARGEFILE64_SOURCE when _GNU_SOURCE is set, but musl does not do that,
+ # and it is necessary for making getdents64() and struct dirent64 exist.
+ conf.set('_LARGEFILE64_SOURCE', 1)
+endif
+
conf.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix : '#include <sys/types.h>'))
conf.set('SIZEOF_INO_T', cc.sizeof('ino_t', prefix : '#include <sys/types.h>'))
conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h>'))
--
2.51.0
@@ -1,31 +0,0 @@
From a8cc582e42dd9fd6b2304003f3e5ab7637c21b73 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 9 Jun 2025 13:37:38 +0900
Subject: [PATCH 05/30] musl: meson: make musl not define wchar_t in their
header
Otherwise, musl defines wchar_t as int, which conflicts with the
assumption by sd-boot, i.e. wchar_t is 2 bytes.
---
src/boot/meson.build | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/boot/meson.build b/src/boot/meson.build
index ba0b309acf..bea96bb2e3 100644
--- a/src/boot/meson.build
+++ b/src/boot/meson.build
@@ -179,6 +179,11 @@ if get_option('mode') == 'developer' and get_option('debug')
efi_c_args += '-DEFI_DEBUG'
endif
+if get_option('libc') == 'musl'
+ # To make musl not define wchar_t as int, rather than short.
+ efi_c_args += '-D__DEFINED_wchar_t'
+endif
+
efi_c_ld_args = [
'-nostdlib',
'-static-pie',
--
2.51.0
@@ -1,91 +0,0 @@
From b9904ed6818232d9ad9bb496747b79a6c9606819 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 9 Jun 2025 13:00:37 +0900
Subject: [PATCH 06/30] musl: meson: check existence of renameat2()
musl-1.2.5 does not provide renameat2(). Note, it is added by
https://github.com/kraj/musl/commit/05ce67fea99ca09cd4b6625cff7aec9cc222dd5a,
hence hopefully it will be provided by musl-1.2.6 or newer.
---
meson.build | 5 +++++
src/include/musl/stdio.h | 13 +++++++++++++
src/libc/musl/meson.build | 4 ++++
src/libc/musl/stdio.c | 11 +++++++++++
4 files changed, 33 insertions(+)
create mode 100644 src/include/musl/stdio.h
create mode 100644 src/libc/musl/stdio.c
diff --git a/meson.build b/meson.build
index 37fb1c2765..71e422f545 100644
--- a/meson.build
+++ b/meson.build
@@ -581,6 +581,7 @@ assert(long_max > 100000)
conf.set_quoted('LONG_MAX_STR', '@0@'.format(long_max))
foreach ident : [
+ ['renameat2', '''#include <stdio.h>''', get_option('libc') == 'musl'], # since musl-1.2.6
['set_mempolicy', '''#include <sys/syscall.h>'''], # declared at numaif.h provided by libnuma, which we do not use
['get_mempolicy', '''#include <sys/syscall.h>'''], # declared at numaif.h provided by libnuma, which we do not use
['strerrorname_np', '''#include <string.h>'''], # since glibc-2.32
@@ -614,6 +615,10 @@ foreach ident : [
['pivot_root', '''#include <unistd.h>'''], # no known header declares pivot_root
]
+ if ident.length() >= 3 and not ident[2]
+ continue
+ endif
+
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
conf.set10('HAVE_' + ident[0].to_upper(), have)
endforeach
diff --git a/src/include/musl/stdio.h b/src/include/musl/stdio.h
new file mode 100644
index 0000000000..d677201f45
--- /dev/null
+++ b/src/include/musl/stdio.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <stdio.h>
+
+#if !HAVE_RENAMEAT2
+# define RENAME_NOREPLACE (1 << 0)
+# define RENAME_EXCHANGE (1 << 1)
+# define RENAME_WHITEOUT (1 << 2)
+
+int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags);
+# define renameat2 missing_renameat2
+#endif
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index a876230c67..8d06d919ef 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -3,3 +3,7 @@
if get_option('libc') != 'musl'
subdir_done()
endif
+
+libc_wrapper_sources += files(
+ 'stdio.c',
+)
diff --git a/src/libc/musl/stdio.c b/src/libc/musl/stdio.c
new file mode 100644
index 0000000000..102a22cd5c
--- /dev/null
+++ b/src/libc/musl/stdio.c
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if !HAVE_RENAMEAT2
+int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags) {
+ return syscall(__NR_renameat2, __oldfd, __old, __newfd, __new, __flags);
+}
+#endif
--
2.51.0
@@ -1,91 +0,0 @@
From 1a5f752e2a80e206474f04fe5c1990fdb08742bd Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 22 Jun 2025 02:16:25 +0900
Subject: [PATCH 07/30] musl: meson: gracefully disable gshadow, idn, nss, and
utmp support
- musl does not support gshadow, and does not provide gshadow.h,
- musl does not provide NI_IDN flag,
- musl does not support nss, and does not provide nss.h which is necessary
for each nss modules,
- musl does not support utmp, and all utmp related functions do nothing,
see https://github.com/kraj/musl/blob/v1.2.5/src/legacy/utmpx.c
---
meson.build | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/meson.build b/meson.build
index 71e422f545..5f235d5113 100644
--- a/meson.build
+++ b/meson.build
@@ -1638,11 +1638,9 @@ foreach term : ['analyze',
'efi',
'environment-d',
'firstboot',
- 'gshadow',
'hibernate',
'hostnamed',
'hwdb',
- 'idn',
'ima',
'ipe',
'initrd',
@@ -1654,8 +1652,6 @@ foreach term : ['analyze',
'mountfsd',
'networkd',
'nsresourced',
- 'nss-myhostname',
- 'nss-systemd',
'oomd',
'portabled',
'pstore',
@@ -1671,7 +1667,6 @@ foreach term : ['analyze',
'tmpfiles',
'tpm',
'userdb',
- 'utmp',
'vconsole',
'xdg-autostart']
have = get_option(term)
@@ -1681,14 +1676,34 @@ endforeach
enable_sysusers = conf.get('ENABLE_SYSUSERS') == 1
+foreach term : ['gshadow',
+ 'idn',
+ 'nss-myhostname',
+ 'nss-systemd',
+ 'utmp']
+
+ have = get_option(term)
+ if have and get_option('libc') == 'musl'
+ warning('@0@ support is requested but it is not supported when building with musl, disabling it.'.format(term))
+ have = false
+ endif
+ name = 'ENABLE_' + term.underscorify().to_upper()
+ conf.set10(name, have)
+endforeach
+
foreach tuple : [['nss-mymachines', 'machined'],
['nss-resolve', 'resolve']]
want = get_option(tuple[0])
- if want.allowed()
- have = get_option(tuple[1])
- if want.enabled() and not have
- error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1]))
+ if want.enabled()
+ if get_option('libc') == 'musl'
+ error('@0@ is requested but it is not supported when building with musl.'.format(tuple[0]))
endif
+ if not get_option(tuple[1])
+ error('@0@ is requested but @1@ is disabled.'.format(tuple[0], tuple[1]))
+ endif
+ have = true
+ elif want.allowed()
+ have = get_option(tuple[1]) and get_option('libc') != 'musl'
else
have = false
endif
--
2.51.0
@@ -1,44 +0,0 @@
From 7e023763fb7f56b7c1ea6fa139d79ec2c3e0164f Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 21 Sep 2025 15:24:06 +0900
Subject: [PATCH 08/30] musl: introduce dummy gshadow header file for userdb
Even 'gshadow' meson option is disabled, src/shared/userdb.c and
src/shared/user-record-nss.c include gshadow.h unconditionally.
Let's introduce dummy header to make them compiled gracefully.
---
src/include/musl/gshadow.h | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 src/include/musl/gshadow.h
diff --git a/src/include/musl/gshadow.h b/src/include/musl/gshadow.h
new file mode 100644
index 0000000000..b57c74ca83
--- /dev/null
+++ b/src/include/musl/gshadow.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <errno.h>
+#include <stddef.h>
+
+struct sgrp {
+ char *sg_namp;
+ char *sg_passwd;
+ char **sg_adm;
+ char **sg_mem;
+};
+
+static inline int getsgnam_r(
+ const char *__name,
+ struct sgrp *__result_buf,
+ char *__buffer,
+ size_t __buflen,
+ struct sgrp **__result) {
+
+ return EOPNOTSUPP; /* this function returns positive errno in case of error. */
+}
--
2.51.0
@@ -1,447 +0,0 @@
From 444acef17881cbc27057bd85b7dc07b332c03d67 Mon Sep 17 00:00:00 2001
From: Emil Renner Berthing <systemd@esmil.dk>
Date: Sat, 22 May 2021 20:26:24 +0200
Subject: [PATCH 09/30] musl: add fallback parse_printf_format() implementation
musl does not provide parse_printf_format(). Let's introduce a fallback
method.
Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
---
src/include/musl/printf.h | 35 +++++
src/libc/musl/meson.build | 1 +
src/libc/musl/printf.c | 276 ++++++++++++++++++++++++++++++++++++++
src/test/meson.build | 6 +
src/test/test-printf.c | 67 +++++++++
5 files changed, 385 insertions(+)
create mode 100644 src/include/musl/printf.h
create mode 100644 src/libc/musl/printf.c
create mode 100644 src/test/test-printf.c
diff --git a/src/include/musl/printf.h b/src/include/musl/printf.h
new file mode 100644
index 0000000000..4242392c89
--- /dev/null
+++ b/src/include/musl/printf.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Copyright 2014 Emil Renner Berthing <systemd@esmil.dk> */
+#pragma once
+
+#include <features.h>
+#include <stddef.h>
+
+#ifdef __GLIBC__
+#include_next <printf.h>
+#else
+
+enum { /* C type: */
+ PA_INT, /* int */
+ PA_CHAR, /* int, cast to char */
+ PA_WCHAR, /* wide char */
+ PA_STRING, /* const char *, a '\0'-terminated string */
+ PA_WSTRING, /* const wchar_t *, wide character string */
+ PA_POINTER, /* void * */
+ PA_FLOAT, /* float */
+ PA_DOUBLE, /* double */
+ PA_LAST,
+};
+
+/* Flag bits that can be set in a type returned by `parse_printf_format'. */
+# define PA_FLAG_MASK 0xff00
+# define PA_FLAG_LONG_LONG (1 << 8)
+# define PA_FLAG_LONG_DOUBLE PA_FLAG_LONG_LONG
+# define PA_FLAG_LONG (1 << 9)
+# define PA_FLAG_SHORT (1 << 10)
+# define PA_FLAG_PTR (1 << 11)
+
+# define parse_printf_format missing_parse_printf_format
+#endif
+
+size_t missing_parse_printf_format(const char *fmt, size_t n, int *types);
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index 8d06d919ef..3e205e1eb1 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -5,5 +5,6 @@ if get_option('libc') != 'musl'
endif
libc_wrapper_sources += files(
+ 'printf.c',
'stdio.c',
)
diff --git a/src/libc/musl/printf.c b/src/libc/musl/printf.c
new file mode 100644
index 0000000000..33c4acd27f
--- /dev/null
+++ b/src/libc/musl/printf.c
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Copyright 2014 Emil Renner Berthing <systemd@esmil.dk> */
+
+#include <limits.h>
+#include <../musl/printf.h> /* This file is also compiled when built with glibc. */
+#include <stdint.h>
+#include <string.h>
+
+static const char* consume_nonarg(const char *fmt) {
+ do {
+ if (*fmt == '\0')
+ return fmt;
+ } while (*fmt++ != '%');
+ return fmt;
+}
+
+static const char* consume_num(const char *fmt) {
+ for (;*fmt >= '0' && *fmt <= '9'; fmt++)
+ /* do nothing */;
+ return fmt;
+}
+
+static const char* consume_argn(const char *fmt, size_t *arg) {
+ const char *p = fmt;
+ size_t val = 0;
+
+ if (*p < '1' || *p > '9')
+ return fmt;
+ do {
+ val = 10*val + (*p++ - '0');
+ } while (*p >= '0' && *p <= '9');
+
+ if (*p != '$')
+ return fmt;
+ *arg = val;
+ return p+1;
+}
+
+static const char* consume_flags(const char *fmt) {
+ for (;;)
+ switch (*fmt) {
+ case '#':
+ case '0':
+ case '-':
+ case ' ':
+ case '+':
+ case '\'':
+ case 'I':
+ fmt++;
+ continue;
+ default:
+ return fmt;
+ }
+}
+
+enum state {
+ BARE,
+ LPRE,
+ LLPRE,
+ HPRE,
+ HHPRE,
+ BIGLPRE,
+ ZTPRE,
+ JPRE,
+ STOP,
+};
+
+enum type {
+ NONE,
+ PTR,
+ STR,
+ WSTR,
+ INT,
+ LONG,
+ LLONG,
+ SHORT,
+ IMAX,
+ SIZET,
+ CHAR,
+ WCHAR,
+ PDIFF,
+ DBL,
+ LDBL,
+ NPTR,
+ MAXTYPE,
+};
+
+static const short pa_types[MAXTYPE] = {
+ [NONE] = PA_INT,
+ [PTR] = PA_POINTER,
+ [STR] = PA_STRING,
+ [WSTR] = PA_WSTRING,
+ [INT] = PA_INT,
+ [SHORT] = PA_INT | PA_FLAG_SHORT,
+ [LONG] = PA_INT | PA_FLAG_LONG,
+ [CHAR] = PA_CHAR,
+ [WCHAR] = PA_WCHAR,
+ [DBL] = PA_DOUBLE,
+ [LDBL] = PA_DOUBLE | PA_FLAG_LONG_DOUBLE,
+ [NPTR] = PA_FLAG_PTR,
+};
+
+static int state_to_pa_type(unsigned state) {
+ switch (state) {
+ case LLONG:
+#if LONG_MAX != LLONG_MAX
+ return PA_INT | PA_FLAG_LONG_LONG;
+#else
+ return PA_INT | PA_FLAG_LONG;
+#endif
+
+ case IMAX:
+#if LONG_MAX != LLONG_MAX
+ if (sizeof(intmax_t) > sizeof(long))
+ return PA_INT | PA_FLAG_LONG_LONG;
+#endif
+ if (sizeof(intmax_t) > sizeof(int))
+ return PA_INT | PA_FLAG_LONG;
+ return PA_INT;
+
+ case SIZET:
+#if LONG_MAX != LLONG_MAX
+ if (sizeof(size_t) > sizeof(long))
+ return PA_INT | PA_FLAG_LONG_LONG;
+#endif
+ if (sizeof(size_t) > sizeof(int))
+ return PA_INT | PA_FLAG_LONG;
+ return PA_INT;
+ default:
+ return pa_types[state];
+ }
+}
+
+#define S(x) [(x)-'A']
+#define E(x) (STOP + (x))
+
+static const unsigned char states[]['z'-'A'+1] = {
+ { /* 0: bare types */
+ S('d') = E(INT), S('i') = E(INT),
+ S('o') = E(INT), S('u') = E(INT), S('x') = E(INT), S('X') = E(INT),
+ S('e') = E(DBL), S('f') = E(DBL), S('g') = E(DBL), S('a') = E(DBL),
+ S('E') = E(DBL), S('F') = E(DBL), S('G') = E(DBL), S('A') = E(DBL),
+ S('c') = E(CHAR), S('C') = E(WCHAR),
+ S('s') = E(STR), S('S') = E(WSTR), S('p') = E(PTR),
+ S('n') = E(NPTR),
+ S('m') = E(NONE),
+ S('l') = LPRE, S('q') = LLPRE, S('h') = HPRE, S('L') = BIGLPRE,
+ S('z') = ZTPRE, S('Z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
+ },
+ { /* 1: l-prefixed */
+ S('d') = E(LONG), S('i') = E(LONG),
+ S('o') = E(LONG), S('u') = E(LONG), S('x') = E(LONG), S('X') = E(LONG),
+ S('e') = E(DBL), S('f') = E(DBL), S('g') = E(DBL), S('a') = E(DBL),
+ S('E') = E(DBL), S('F') = E(DBL), S('G') = E(DBL), S('A') = E(DBL),
+ S('c') = E(CHAR), S('s') = E(STR),
+ S('n') = E(NPTR),
+ S('l') = LLPRE,
+ },
+ { /* 2: ll-prefixed */
+ S('d') = E(LLONG), S('i') = E(LLONG),
+ S('o') = E(LLONG), S('u') = E(LLONG), S('x') = E(LLONG), S('X') = E(LLONG),
+ S('n') = E(NPTR),
+ },
+ { /* 3: h-prefixed */
+ S('d') = E(SHORT), S('i') = E(SHORT),
+ S('o') = E(SHORT), S('u') = E(SHORT), S('x') = E(SHORT), S('X') = E(SHORT),
+ S('n') = E(NPTR),
+ S('h') = HHPRE,
+ },
+ { /* 4: hh-prefixed */
+ S('d') = E(CHAR), S('i') = E(CHAR),
+ S('o') = E(CHAR), S('u') = E(CHAR), S('x') = E(CHAR), S('X') = E(CHAR),
+ S('n') = E(NPTR),
+ },
+ { /* 5: L-prefixed */
+ S('e') = E(LDBL), S('f') = E(LDBL), S('g') = E(LDBL), S('a') = E(LDBL),
+ S('E') = E(LDBL), S('F') = E(LDBL), S('G') = E(LDBL), S('A') = E(LDBL),
+ },
+ { /* 6: z- or t-prefixed (assumed to be same size) */
+ S('d') = E(SIZET), S('i') = E(SIZET),
+ S('o') = E(SIZET), S('u') = E(SIZET), S('x') = E(SIZET), S('X') = E(SIZET),
+ S('n') = E(NPTR),
+ },
+ { /* 7: j-prefixed */
+ S('d') = E(IMAX), S('i') = E(IMAX),
+ S('o') = E(IMAX), S('u') = E(IMAX), S('x') = E(IMAX), S('X') = E(IMAX),
+ S('n') = E(NPTR),
+ },
+};
+
+size_t missing_parse_printf_format(const char *fmt, size_t n, int *types) {
+ size_t i = 0;
+ size_t last = 0;
+
+ memset(types, 0, n);
+
+ for (;;) {
+ size_t arg;
+
+ fmt = consume_nonarg(fmt);
+ if (*fmt == '\0')
+ break;
+ if (*fmt == '%') {
+ fmt++;
+ continue;
+ }
+ arg = 0;
+ fmt = consume_argn(fmt, &arg);
+ /* flags */
+ fmt = consume_flags(fmt);
+ /* width */
+ if (*fmt == '*') {
+ size_t warg = 0;
+ fmt = consume_argn(fmt+1, &warg);
+ if (warg == 0)
+ warg = ++i;
+ if (warg > last)
+ last = warg;
+ if (warg <= n && types[warg-1] == NONE)
+ types[warg-1] = INT;
+ } else
+ fmt = consume_num(fmt);
+ /* precision */
+ if (*fmt == '.') {
+ fmt++;
+ if (*fmt == '*') {
+ size_t parg = 0;
+ fmt = consume_argn(fmt+1, &parg);
+ if (parg == 0)
+ parg = ++i;
+ if (parg > last)
+ last = parg;
+ if (parg <= n && types[parg-1] == NONE)
+ types[parg-1] = INT;
+ } else {
+ if (*fmt == '-')
+ fmt++;
+ fmt = consume_num(fmt);
+ }
+ }
+ /* length modifier and conversion specifier */
+ unsigned state = BARE;
+ for (;;) {
+ unsigned char c = *fmt;
+
+ if (c == '\0')
+ break;
+
+ fmt++;
+
+ if (c < 'A' || c > 'z')
+ break;
+
+ state = states[state]S(c);
+ if (state == 0 || state >= STOP)
+ break;
+ }
+
+ if (state <= STOP) /* %m or invalid format */
+ continue;
+
+ if (arg == 0)
+ arg = ++i;
+ if (arg > last)
+ last = arg;
+ if (arg <= n)
+ types[arg-1] = state - STOP;
+ }
+
+ if (last > n)
+ last = n;
+ for (i = 0; i < last; i++)
+ types[i] = state_to_pa_type(types[i]);
+
+ return last;
+}
diff --git a/src/test/meson.build b/src/test/meson.build
index da04b82d47..1dd61effc0 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -403,6 +403,12 @@ executables += [
'dependencies' : libacl,
'type' : 'manual',
},
+ test_template + {
+ 'sources' : files(
+ 'test-printf.c',
+ '../libc/musl/printf.c'
+ ),
+ },
test_template + {
'sources' : files('test-process-util.c'),
'dependencies' : threads,
diff --git a/src/test/test-printf.c b/src/test/test-printf.c
new file mode 100644
index 0000000000..28497e902d
--- /dev/null
+++ b/src/test/test-printf.c
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <../musl/printf.h>
+
+#include "memory-util.h"
+#include "strv.h"
+#include "tests.h"
+
+static void test_parse_printf_format_one(const char *fmt) {
+ int arg_types_x[128] = {}, arg_types_y[128] = {};
+ size_t x, y;
+
+ log_debug("/* %s(%s) */", __func__, fmt);
+
+ x = parse_printf_format(fmt, ELEMENTSOF(arg_types_x), arg_types_x);
+ y = missing_parse_printf_format(fmt, ELEMENTSOF(arg_types_y), arg_types_y);
+
+ for (size_t i = 0; i < x; i++)
+ log_debug("x[%zu]=%i", i, arg_types_x[i]);
+ for (size_t i = 0; i < y; i++)
+ log_debug("y[%zu]=%i", i, arg_types_y[i]);
+
+ ASSERT_EQ(memcmp_nn(arg_types_x, x * sizeof(int), arg_types_y, y * sizeof(int)), 0);
+}
+
+TEST(parse_printf_format) {
+ FOREACH_STRING(s, "d", "i", "o", "u", "x", "X", "n")
+ FOREACH_STRING(p, "", "hh", "h", "l", "ll", "j", "z", "Z", "t") {
+ _cleanup_free_ char *fmt = NULL;
+
+ ASSERT_NOT_NULL(fmt = strjoin("%", p, s));
+ test_parse_printf_format_one(fmt);
+ }
+
+ FOREACH_STRING(s, "e", "E", "f", "F", "g", "G", "a", "A")
+ FOREACH_STRING(p, "", "L") {
+ _cleanup_free_ char *fmt = NULL;
+
+ ASSERT_NOT_NULL(fmt = strjoin("%", p, s));
+ test_parse_printf_format_one(fmt);
+ }
+
+ FOREACH_STRING(s, "c", "s")
+ FOREACH_STRING(p, "", "l") {
+ _cleanup_free_ char *fmt = NULL;
+
+ ASSERT_NOT_NULL(fmt = strjoin("%", p, s));
+ test_parse_printf_format_one(fmt);
+ }
+
+ FOREACH_STRING(s, "C", "S", "p", "m", "%") {
+ _cleanup_free_ char *fmt = NULL;
+
+ ASSERT_NOT_NULL(fmt = strjoin("%", s));
+ test_parse_printf_format_one(fmt);
+ }
+
+ test_parse_printf_format_one("asfhghejmlahpgakdmsalc");
+ test_parse_printf_format_one("%d%i%o%u%x%X");
+ test_parse_printf_format_one("%e%E%f%F%g%G%a%A");
+ test_parse_printf_format_one("%c%s%C%S%p%n%m%%");
+ test_parse_printf_format_one("%03d%-05d%+i%hhu%hu%hx%lx");
+ test_parse_printf_format_one("%llx%x%LE%ji%zi%zu%zi%zu%Zi%Zu%tu");
+ test_parse_printf_format_one("%l");
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
--
2.51.0
@@ -1,74 +0,0 @@
From 8fd471ff028cd3d5df5ee944fdbcefc7e77abc3d Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 23 Jan 2023 23:39:46 -0800
Subject: [PATCH 10/30] musl: introduce GNU specific version of strerror_r()
musl provides XSI compliant strerror_r(), and it is slightly different
from the one by glibc.
Let's introduce a tiny wrapper to convert XSI strerror_r() to GNU one.
---
src/include/musl/string.h | 7 +++++++
src/libc/musl/meson.build | 1 +
src/libc/musl/string.c | 26 ++++++++++++++++++++++++++
3 files changed, 34 insertions(+)
create mode 100644 src/include/musl/string.h
create mode 100644 src/libc/musl/string.c
diff --git a/src/include/musl/string.h b/src/include/musl/string.h
new file mode 100644
index 0000000000..cc3da63012
--- /dev/null
+++ b/src/include/musl/string.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <string.h>
+
+char* strerror_r_gnu(int errnum, char *buf, size_t buflen);
+#define strerror_r strerror_r_gnu
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index 3e205e1eb1..a64f292081 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -7,4 +7,5 @@ endif
libc_wrapper_sources += files(
'printf.c',
'stdio.c',
+ 'string.c',
)
diff --git a/src/libc/musl/string.c b/src/libc/musl/string.c
new file mode 100644
index 0000000000..c2a9f4a169
--- /dev/null
+++ b/src/libc/musl/string.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* The header errno.h overrides strerror_r with strerror_r_gnu, hence we need to undef it here. */
+#undef strerror_r
+
+char* strerror_r_gnu(int errnum, char *buf, size_t buflen) {
+ int saved_errno = errno;
+ const char *msg = strerror(errnum);
+ size_t l = strlen(msg);
+ if (l >= buflen) {
+ if (buflen > 0) {
+ if (buflen > 1)
+ memcpy(buf, msg, buflen - 1);
+ buf[buflen - 1] = '\0';
+ }
+ errno = ERANGE;
+ } else {
+ memcpy(buf, msg, l + 1);
+ errno = saved_errno;
+ }
+ return buf;
+}
--
2.51.0
@@ -1,137 +0,0 @@
From bf670a5af2a78f81ee9396b5b5da49b2df07fcfd Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 9 Sep 2025 08:31:22 +0900
Subject: [PATCH 11/30] musl: make strptime() accept "%z"
musl v1.2.5 does not support %z specifier in strptime(). Since
https://github.com/kraj/musl/commit/fced99e93daeefb0192fd16304f978d4401d1d77
%z is supported, but it only supports strict RFC-822/ISO 8601 format,
that is, 4 digits with sign (e.g. +0900 or -1400), but does not support
extended format: 2 digits or colon separated 4 digits (e.g. +09 or -14:00).
Let's add fallback logic to make it support the extended timezone spec.
---
src/include/musl/time.h | 7 ++++
src/libc/musl/meson.build | 1 +
src/libc/musl/time.c | 86 +++++++++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+)
create mode 100644 src/include/musl/time.h
create mode 100644 src/libc/musl/time.c
diff --git a/src/include/musl/time.h b/src/include/musl/time.h
new file mode 100644
index 0000000000..349f9a3577
--- /dev/null
+++ b/src/include/musl/time.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <time.h>
+
+char* strptime_fallback(const char *s, const char *format, struct tm *tm);
+#define strptime strptime_fallback
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index a64f292081..5fb590e868 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -8,4 +8,5 @@ libc_wrapper_sources += files(
'printf.c',
'stdio.c',
'string.c',
+ 'time.c',
)
diff --git a/src/libc/musl/time.c b/src/libc/musl/time.c
new file mode 100644
index 0000000000..12108166b2
--- /dev/null
+++ b/src/libc/musl/time.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+
+/* The header time.h overrides strptime with strerror_fallback, hence we need to undef it here. */
+#undef strptime
+
+char* strptime_fallback(const char *s, const char *format, struct tm *tm) {
+ /* First try native strptime() as is, and if it succeeds, return the resuit as is. */
+ char *k = strptime(s, format, tm);
+ if (k)
+ return k;
+
+ /* Check inputs for safety. */
+ if (!s || !format || !tm)
+ return NULL;
+
+ /* We only fallback if the format is exactly "%z". */
+ if (strcmp(format, "%z") != 0)
+ return NULL;
+
+ /* In the below, we parse timezone specifiction compatible with RFC-822/ISO 8601 and its extensions
+ * (e.g. +06, +0900, or -03:00). */
+
+ bool positive;
+ switch (*s) {
+ case '+':
+ positive = true;
+ break;
+ case '-':
+ positive = false;
+ break;
+ default:
+ return NULL;
+ }
+
+ s++;
+
+ if (*s < '0' || *s > '9')
+ return NULL;
+ long t = (*s - '0') * 10 * 60 * 60;
+
+ s++;
+
+ if (*s < '0' || *s > '9')
+ return NULL;
+ t += (*s - '0') * 60 * 60;
+
+ s++;
+
+ if (*s == '\0') /* 2 digits case */
+ goto finalize;
+
+ if (*s == ':') /* skip colon */
+ s++;
+
+ if (*s < '0' || *s >= '6') /* refuse minutes equal to or larger than 60 */
+ return NULL;
+ t += (*s - '0') * 10 * 60;
+
+ s++;
+
+ if (*s < '0' || *s > '9')
+ return NULL;
+ t += (*s - '0') * 60;
+
+ s++;
+
+ if (*s != '\0')
+ return NULL;
+
+finalize:
+ if (t > 24 * 60 * 60) /* refuse larger than 24 hours */
+ return NULL;
+
+ if (!positive)
+ t = -t;
+
+ *tm = (struct tm) {
+ .tm_gmtoff = t,
+ };
+
+ return (char*) s;
+}
--
2.51.0
@@ -1,69 +0,0 @@
From d97f38a2ffd0aca2a1d7b8a5bbbd1994e786c7bc Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 9 Sep 2025 09:10:44 +0900
Subject: [PATCH 12/30] musl: make strtoll() accept strings start with dot
glibc accepts strings start with '.' and returns 0, but musl refuses
them. Let's accept them, as our code assumes the function accept such
strings.
---
src/include/musl/stdlib.h | 7 +++++++
src/libc/musl/meson.build | 1 +
src/libc/musl/stdlib.c | 19 +++++++++++++++++++
3 files changed, 27 insertions(+)
create mode 100644 src/include/musl/stdlib.h
create mode 100644 src/libc/musl/stdlib.c
diff --git a/src/include/musl/stdlib.h b/src/include/musl/stdlib.h
new file mode 100644
index 0000000000..ecfd6ccb43
--- /dev/null
+++ b/src/include/musl/stdlib.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <stdlib.h>
+
+long long strtoll_fallback(const char *nptr, char **endptr, int base);
+#define strtoll strtoll_fallback
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index 5fb590e868..ea09af9fa5 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -7,6 +7,7 @@ endif
libc_wrapper_sources += files(
'printf.c',
'stdio.c',
+ 'stdlib.c',
'string.c',
'time.c',
)
diff --git a/src/libc/musl/stdlib.c b/src/libc/musl/stdlib.c
new file mode 100644
index 0000000000..d4e2714217
--- /dev/null
+++ b/src/libc/musl/stdlib.c
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdlib.h>
+
+/* The header stdlib.h overrides strtoll with strtoll_fallback, hence we need to undef it here. */
+#undef strtoll
+
+long long strtoll_fallback(const char *nptr, char **endptr, int base) {
+ /* glibc returns 0 if the first character is '.' without error, but musl returns as an error.
+ * As our code assumes the glibc behavior, let's accept string starts with '.'. */
+ if (nptr && *nptr == '.') {
+ if (endptr)
+ *endptr = (char*) nptr;
+ return 0;
+ }
+
+ /* Otherwise, use the native strtoll(). */
+ return strtoll(nptr, endptr, base);
+}
--
2.51.0
@@ -1,86 +0,0 @@
From 96b9f85d08be6f60768d1ebcdaf77e8e5dafebf6 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Fri, 5 Sep 2025 09:20:50 +0900
Subject: [PATCH 13/30] musl: introduce strerrorname_np()
musl does not provide strerrorname_np(). Thus, we need to implement it.
---
src/include/musl/string.h | 2 ++
src/libc/musl/generate-strerrorname_np.sh | 39 +++++++++++++++++++++++
src/libc/musl/meson.build | 7 ++++
3 files changed, 48 insertions(+)
create mode 100755 src/libc/musl/generate-strerrorname_np.sh
diff --git a/src/include/musl/string.h b/src/include/musl/string.h
index cc3da63012..7317c8dea0 100644
--- a/src/include/musl/string.h
+++ b/src/include/musl/string.h
@@ -5,3 +5,5 @@
char* strerror_r_gnu(int errnum, char *buf, size_t buflen);
#define strerror_r strerror_r_gnu
+
+const char* strerrorname_np(int errnum);
diff --git a/src/libc/musl/generate-strerrorname_np.sh b/src/libc/musl/generate-strerrorname_np.sh
new file mode 100755
index 0000000000..0e0fb8b187
--- /dev/null
+++ b/src/libc/musl/generate-strerrorname_np.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eu
+set -o pipefail
+
+# This is based on src/basic/generate-errno-list.sh.
+
+# ECANCELLED, EDEADLOCK, ENOTSUP, EREFUSED, and EWOULDBLOCK are defined as aliases of
+# ECANCELED, EDEADLK, EOPNOTSUPP, ECONNREFUSED, and EAGAIN, respectively. Let's drop them.
+
+CC=${1:?}
+shift
+
+cat <<'EOF'
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+static const char * const errno_table[] = {
+EOF
+
+$CC -dM -include errno.h - </dev/null | \
+ grep -Ev '^#define[[:space:]]+(ECANCELLED|EDEADLOCK|ENOTSUP|EREFUSED|EWOULDBLOCK)' | \
+ awk '/^#define[ \t]+E[^ _]+[ \t]+/ { printf " [%s] = \"%s\",\n", $2, $2; }' | \
+ sort
+
+cat <<'EOF'
+};
+
+const char* strerrorname_np(int errnum) {
+ if (errnum < 0)
+ errnum = -errnum;
+ if ((size_t) errnum >= sizeof(errno_table) / sizeof(errno_table[0]))
+ return NULL;
+ return errno_table[errnum];
+}
+EOF
diff --git a/src/libc/musl/meson.build b/src/libc/musl/meson.build
index ea09af9fa5..1cb6bf28c1 100644
--- a/src/libc/musl/meson.build
+++ b/src/libc/musl/meson.build
@@ -11,3 +11,10 @@ libc_wrapper_sources += files(
'string.c',
'time.c',
)
+
+generator= files('generate-strerrorname_np.sh')
+libc_wrapper_sources += custom_target(
+ input : generator,
+ output : 'strerrorname_np.c',
+ command : [env, 'bash', generator, cpp],
+ capture : true)
--
2.51.0
@@ -1,60 +0,0 @@
From 331e8b9158f1bb3b4ff5db2c17a16a60640e0c12 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 10 Jun 2025 00:40:59 +0900
Subject: [PATCH 14/30] musl: introduce dummy functions for mallinfo(),
malloc_info(), and malloc_trim()
These functions are not provided by musl.
---
src/include/musl/malloc.h | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 src/include/musl/malloc.h
diff --git a/src/include/musl/malloc.h b/src/include/musl/malloc.h
new file mode 100644
index 0000000000..9d15d4bf91
--- /dev/null
+++ b/src/include/musl/malloc.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <errno.h>
+#include <stdio.h>
+
+/* struct mallinfo2 will be defined and struct mallinfo is converted to struct mallinfo2 in
+ * override/malloc.h. Hence, here we define struct mallinfo. */
+
+struct mallinfo {
+ int arena; /* non-mmapped space allocated from system */
+ int ordblks; /* number of free chunks */
+ int smblks; /* number of fastbin blocks */
+ int hblks; /* number of mmapped regions */
+ int hblkhd; /* space in mmapped regions */
+ int usmblks; /* always 0, preserved for backwards compatibility */
+ int fsmblks; /* space available in freed fastbin blocks */
+ int uordblks; /* total allocated space */
+ int fordblks; /* total free space */
+ int keepcost; /* top-most, releasable (via malloc_trim) space */
+};
+
+static inline struct mallinfo mallinfo(void) {
+ return (struct mallinfo) {};
+}
+
+static inline int malloc_info(int options, FILE *stream) {
+ if (options != 0)
+ errno = EINVAL;
+ else
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+static inline int malloc_trim(size_t pad) {
+ return 0;
+}
+
+#include_next <malloc.h>
--
2.51.0
@@ -1,86 +0,0 @@
From 5067b1cfac07dbb50d7813b2a900b1b6f8e6e4bd Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 8 Jun 2025 10:07:54 +0900
Subject: [PATCH 15/30] musl: introduce dummy function for
gnu_get_libc_version()
As the header gnu/libc-version.h and gnu_get_libc_version() function
are glibc specific, and musl does not provide them.
---
src/include/musl/gnu/libc-version.h | 8 ++++++++
src/shared/condition.c | 9 +++++++--
src/test/test-condition.c | 8 +++++---
3 files changed, 20 insertions(+), 5 deletions(-)
create mode 100644 src/include/musl/gnu/libc-version.h
diff --git a/src/include/musl/gnu/libc-version.h b/src/include/musl/gnu/libc-version.h
new file mode 100644
index 0000000000..8ad0ed16b8
--- /dev/null
+++ b/src/include/musl/gnu/libc-version.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+
+static inline const char* gnu_get_libc_version(void) {
+ return NULL;
+}
diff --git a/src/shared/condition.c b/src/shared/condition.c
index b09eff1bfb..b27b24aba7 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -255,8 +255,13 @@ static int condition_test_version(Condition *c, char **env) {
if (streq(word, "systemd"))
return condition_test_version_cmp(p, STRINGIFY(PROJECT_VERSION));
- if (streq(word, "glibc"))
- return condition_test_version_cmp(p, gnu_get_libc_version());
+ if (streq(word, "glibc")) {
+ const char *v = gnu_get_libc_version();
+ if (!v)
+ return false; /* built with musl */
+
+ return condition_test_version_cmp(p, v);
+ }
/* if no predicate has been set, default to "kernel" and use the whole parameter as condition */
if (!streq(word, "kernel"))
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 11b3a42418..898c111ffe 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -669,8 +669,10 @@ TEST(condition_test_version) {
condition_free(condition);
/* Test glibc version */
+ bool expected = !!gnu_get_libc_version();
+
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc > 1", false, false)));
- ASSERT_OK_POSITIVE(condition_test(condition, environ));
+ ASSERT_OK_EQ(condition_test(condition, environ), expected);
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 2", false, false)));
@@ -678,7 +680,7 @@ TEST(condition_test_version) {
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc < 9999", false, false)));
- ASSERT_OK_POSITIVE(condition_test(condition, environ));
+ ASSERT_OK_EQ(condition_test(condition, environ), expected);
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, "glibc > 9999", false, false)));
@@ -688,7 +690,7 @@ TEST(condition_test_version) {
v = strjoina("glibc = ", gnu_get_libc_version());
ASSERT_NOT_NULL((condition = condition_new(CONDITION_VERSION, v, false, false)));
- ASSERT_OK_POSITIVE(condition_test(condition, environ));
+ ASSERT_OK_EQ(condition_test(condition, environ), expected);
condition_free(condition);
v = strjoina("glibc != ", gnu_get_libc_version());
--
2.51.0
@@ -1,30 +0,0 @@
From 65a6110f75e3bcf980aa4138ad18c06f351b78f5 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 9 Jun 2025 14:00:55 +0900
Subject: [PATCH 16/30] musl: define __THROW when not defined
__THROW is internally used by glibc headers, hence our implementation of
net/if.h and sys/mount.h also use it. However, musl does not provide
the macro. Let's define it when not defined.
---
src/include/musl/features.h | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 src/include/musl/features.h
diff --git a/src/include/musl/features.h b/src/include/musl/features.h
new file mode 100644
index 0000000000..bd6d00a9d4
--- /dev/null
+++ b/src/include/musl/features.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <features.h>
+
+#ifndef __THROW
+#define __THROW
+#endif
--
2.51.0
@@ -1,29 +0,0 @@
From 75aa46c9399664d2a1cdabefcad2899ebfabc741 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 23 Jun 2025 16:00:21 +0900
Subject: [PATCH 17/30] musl: replace sys/prctl.h with our own implementation
To avoid conflicts between musl's sys/prctl.h and linux/prctl.h.
---
src/include/musl/sys/prctl.h | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 src/include/musl/sys/prctl.h
diff --git a/src/include/musl/sys/prctl.h b/src/include/musl/sys/prctl.h
new file mode 100644
index 0000000000..2c830d2649
--- /dev/null
+++ b/src/include/musl/sys/prctl.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* This is for avoiding conflicts between musl's sys/prctl.h and linux/prctl.h. */
+
+#include <features.h>
+#include <linux/prctl.h> /* IWYU pragma: export */
+
+int prctl(int, ...);
--
2.51.0
@@ -1,72 +0,0 @@
From 6e77a74ca91e689a43651d9eeb5f8f40893eed76 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 23 Jun 2025 16:08:37 +0900
Subject: [PATCH 18/30] musl: replace netinet/if_ether.h with our own
implementation
musl's netinet/if_ether.h conflicts with linux/if_ether.h.
The reimplementation is mostly equivalent with what glibc does.
This also unset __UAPI_DEF_ETHHDR before including linux/if_ether.h,
otherwise struct ethhdr may not be defined by the header when it is
defined.
---
src/include/musl/linux/if_ether.h | 5 +++++
src/include/musl/netinet/if_ether.h | 33 +++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
create mode 100644 src/include/musl/linux/if_ether.h
create mode 100644 src/include/musl/netinet/if_ether.h
diff --git a/src/include/musl/linux/if_ether.h b/src/include/musl/linux/if_ether.h
new file mode 100644
index 0000000000..e28cd4a014
--- /dev/null
+++ b/src/include/musl/linux/if_ether.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#undef __UAPI_DEF_ETHHDR
+#include_next <linux/if_ether.h>
diff --git a/src/include/musl/netinet/if_ether.h b/src/include/musl/netinet/if_ether.h
new file mode 100644
index 0000000000..62f4ac03b3
--- /dev/null
+++ b/src/include/musl/netinet/if_ether.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* glibc's netinet/if_ether.h does the following:
+ * - include linux/if_ether.h, net/ethernet.h, and net/if_arp.h,
+ * - define struct ether_arp, and relevant macros,
+ * - define ETHER_MAP_IP_MULTICAST() macro (currently we do not use it).
+ * However, musl's netinet/if_ether.h conflicts with linux/if_ether.h.
+ * Let's use the same way that glibc uses. */
+
+#include <linux/if_ether.h> /* IWYU pragma: export */
+#include <net/ethernet.h> /* IWYU pragma: export */
+#include <net/if_arp.h> /* IWYU pragma: export */
+
+/*
+ * Ethernet Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. Structure below is adapted
+ * to resolving internet addresses. Field names used correspond to
+ * RFC 826.
+ */
+struct ether_arp {
+ struct arphdr ea_hdr; /* fixed-size header */
+ uint8_t arp_sha[ETH_ALEN]; /* sender hardware address */
+ uint8_t arp_spa[4]; /* sender protocol address */
+ uint8_t arp_tha[ETH_ALEN]; /* target hardware address */
+ uint8_t arp_tpa[4]; /* target protocol address */
+};
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op ea_hdr.ar_op
--
2.51.0
@@ -1,35 +0,0 @@
From 7d4b4e49729f7c7d5164ba2c5040ed522d68ee7a Mon Sep 17 00:00:00 2001
From: Chen Qi <Qi.Chen@windriver.com>
Date: Mon, 25 Feb 2019 15:00:06 +0800
Subject: [PATCH 19/30] musl: add missing FTW_CONTINUE macro
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is to avoid build failures like below for musl.
test-recurse-dir.c:23:24: error: FTW_CONTINUE undeclared
Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
---
src/include/musl/ftw.h | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 src/include/musl/ftw.h
diff --git a/src/include/musl/ftw.h b/src/include/musl/ftw.h
new file mode 100644
index 0000000000..fd153734d0
--- /dev/null
+++ b/src/include/musl/ftw.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <ftw.h>
+
+#ifndef FTW_CONTINUE
+#define FTW_CONTINUE 0
+#endif
--
2.51.0
@@ -1,90 +0,0 @@
From c306693ac26aef3d5fb06fa0981d911bda9b93d7 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 22 Jun 2025 00:38:58 +0900
Subject: [PATCH 20/30] musl: add several missing statx macros
glibc's sys/stat.h includes linux/stat.h, and we have copy of it from
the latest kernel, hence all new flags are always defined.
However, musl's sys/stat.h does not include linux/stat.h, and moreover,
they conflict with each other, hence we cannot include both header
simultaneously. Let's define missing macros to support musl.
---
src/include/musl/sys/stat.h | 66 +++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 src/include/musl/sys/stat.h
diff --git a/src/include/musl/sys/stat.h b/src/include/musl/sys/stat.h
new file mode 100644
index 0000000000..610dd3e699
--- /dev/null
+++ b/src/include/musl/sys/stat.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <sys/stat.h>
+
+#include <assert.h>
+#include <stddef.h>
+
+/* musl's sys/stat.h does not include linux/stat.h, and unfortunately they conflict with each other.
+ * Hence, some relatively new macros need to be explicitly defined here. */
+
+/* Before 23ab04a8630225371455d5f4538fd078665bb646, statx.stx_mnt_id is not defined. */
+#ifndef STATX_MNT_ID
+static_assert(offsetof(struct statx, __pad1) == offsetof(struct statx, stx_dev_minor) + sizeof(uint32_t), "");
+#define stx_mnt_id __pad1[0]
+#endif
+
+#ifndef STATX_MNT_ID
+#define STATX_MNT_ID 0x00001000U
+#endif
+#ifndef STATX_DIOALIGN
+#define STATX_DIOALIGN 0x00002000U
+#endif
+#ifndef STATX_MNT_ID_UNIQUE
+#define STATX_MNT_ID_UNIQUE 0x00004000U
+#endif
+#ifndef STATX_SUBVOL
+#define STATX_SUBVOL 0x00008000U
+#endif
+#ifndef STATX_WRITE_ATOMIC
+#define STATX_WRITE_ATOMIC 0x00010000U
+#endif
+#ifndef STATX_DIO_READ_ALIGN
+#define STATX_DIO_READ_ALIGN 0x00020000U
+#endif
+
+#ifndef STATX_ATTR_COMPRESSED
+#define STATX_ATTR_COMPRESSED 0x00000004
+#endif
+#ifndef STATX_ATTR_IMMUTABLE
+#define STATX_ATTR_IMMUTABLE 0x00000010
+#endif
+#ifndef STATX_ATTR_APPEND
+#define STATX_ATTR_APPEND 0x00000020
+#endif
+#ifndef STATX_ATTR_NODUMP
+#define STATX_ATTR_NODUMP 0x00000040
+#endif
+#ifndef STATX_ATTR_ENCRYPTED
+#define STATX_ATTR_ENCRYPTED 0x00000800
+#endif
+#ifndef STATX_ATTR_AUTOMOUNT
+#define STATX_ATTR_AUTOMOUNT 0x00001000
+#endif
+#ifndef STATX_ATTR_MOUNT_ROOT
+#define STATX_ATTR_MOUNT_ROOT 0x00002000
+#endif
+#ifndef STATX_ATTR_VERITY
+#define STATX_ATTR_VERITY 0x00100000
+#endif
+#ifndef STATX_ATTR_DAX
+#define STATX_ATTR_DAX 0x00200000
+#endif
+#ifndef STATX_ATTR_WRITE_ATOMIC
+#define STATX_ATTR_WRITE_ATOMIC 0x00400000
+#endif
--
2.51.0
@@ -1,36 +0,0 @@
From a8209a45e73f4c8b1f4e4e174d2d5fbc8a9ac4df Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 22 Jul 2025 03:37:37 +0900
Subject: [PATCH 21/30] musl: avoid conflict between fcntl.h and our forward.h
glibc defines AT_FDCWD as -100, but musl defines it as (-100).
In forward.h, we also define AT_FDCWD as -100, hence musl's fcntl.h
conflicts with forward.h. This is for avoiding the conflict.
---
src/include/musl/fcntl.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 src/include/musl/fcntl.h
diff --git a/src/include/musl/fcntl.h b/src/include/musl/fcntl.h
new file mode 100644
index 0000000000..e69f13087d
--- /dev/null
+++ b/src/include/musl/fcntl.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* glibc defines AT_FDCWD as -100, but musl defines it as (-100). Hence, musl's fcntl.h conflicts with
+ * forward.h. To avoid the conflict, here temporary undef AT_FDCWD before including fcntl.h. */
+#ifdef AT_FDCWD
+#undef AT_FDCWD
+#endif
+
+#include_next <fcntl.h>
+
+/* Then, undef AT_FDCWD by fcntl.h and redefine it as consistent with forward.h */
+#undef AT_FDCWD
+#define AT_FDCWD -100
--
2.51.0
@@ -1,29 +0,0 @@
From 4a06464ea72d06859c54fca4d7bc361e2f4b069c Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 7 Sep 2025 06:16:02 +0900
Subject: [PATCH 22/30] musl: redefine HOST_NAME_MAX as 64
glibc defines HOST_NAME_MAX as 64 and our code rely on that, but musl
defines the constant as 255. Let's redefine it.
---
src/include/musl/limits.h | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 src/include/musl/limits.h
diff --git a/src/include/musl/limits.h b/src/include/musl/limits.h
new file mode 100644
index 0000000000..fadf71d66f
--- /dev/null
+++ b/src/include/musl/limits.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <limits.h>
+
+/* HOST_NAME_MAX should be 64 on linux, but musl uses the one by POSIX (255). */
+#undef HOST_NAME_MAX
+#define HOST_NAME_MAX 64
--
2.51.0
@@ -1,62 +0,0 @@
From 1aecd86536aef53e92a4903794259300815a9e62 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 1 Jul 2025 12:53:14 +0900
Subject: [PATCH 23/30] musl: avoid multiple evaluations in CPU_ISSET_S() macro
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
musl's CPU_ISSET_S() macro does not avoid multiple evaluations, and it
only accepts simple variable or constant.
Fixes the following error.
```
../src/shared/cpu-set-util.c: In function cpu_set_to_mask_string:
../src/shared/cpu-set-util.c:101:41: warning: operation on i may be undefined [-Werror=sequence-point]
101 | if (CPU_ISSET_S(--i, c->allocated, c->set))
| ^
```
---
src/include/musl/sched.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 src/include/musl/sched.h
diff --git a/src/include/musl/sched.h b/src/include/musl/sched.h
new file mode 100644
index 0000000000..d6e7100ee4
--- /dev/null
+++ b/src/include/musl/sched.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <sched.h>
+
+/* This is for avoiding multiple evaluations in musl's __CPU_op_S() macro. */
+
+#undef __CPU_op_S
+#undef CPU_SET_S
+#undef CPU_CLR_S
+#undef CPU_ISSET_S
+#undef CPU_SET
+#undef CPU_CLR
+#undef CPU_ISSET
+
+#define __CPU_op_S(i, size, set, op) \
+ ({ \
+ typeof(i) _i = (i); \
+ \
+ _i / 8U >= (size) ? 0 : \
+ (((unsigned long*) (set))[_i / 8 / sizeof(long)] op (1UL << (_i % (8 * sizeof(long))))); \
+ })
+
+#define CPU_SET_S(i, size, set) __CPU_op_S(i, size, set, |=)
+#define CPU_CLR_S(i, size, set) __CPU_op_S(i, size, set, &=~)
+#define CPU_ISSET_S(i, size, set) __CPU_op_S(i, size, set, &)
+
+#define CPU_SET(i, set) CPU_SET_S(i, sizeof(cpu_set_t), set)
+#define CPU_CLR(i, set) CPU_CLR_S(i, sizeof(cpu_set_t), set)
+#define CPU_ISSET(i, set) CPU_ISSET_S(i, sizeof(cpu_set_t), set)
--
2.51.0
@@ -1,57 +0,0 @@
From c00d31373a8f5b922dd947a6d6d6098df0429762 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 7 Sep 2025 08:53:07 +0900
Subject: [PATCH 24/30] musl: core: there is one less usable signal when built
with musl
musl internally reserves one more signal, hence we can only use 29
signals.
---
src/core/manager.c | 4 ++--
src/test/test-signal-util.c | 7 ++++++-
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/core/manager.c b/src/core/manager.c
index d85896577f..86cb11905f 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -526,7 +526,7 @@ static int manager_setup_signals(Manager *m) {
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
/* We make liberal use of realtime signals here. On Linux/glibc we have 30 of them, between
- * SIGRTMIN+0 ... SIGRTMIN+30 (aka SIGRTMAX). */
+ * SIGRTMIN+0 ... SIGRTMIN+30 (aka SIGRTMAX). When musl is used SIGRTMAX is SIGRTMIN+29. */
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask,
@@ -571,7 +571,7 @@ static int manager_setup_signals(Manager *m) {
SIGRTMIN+28, /* systemd: set log target to kmsg */
SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */
- /* ... one free signal here SIGRTMIN+30 ... */
+ /* ... one free signal here SIGRTMIN+30 (glibc only) ... */
-1);
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c
index 7d069a7fb2..f26b48f3f2 100644
--- a/src/test/test-signal-util.c
+++ b/src/test/test-signal-util.c
@@ -13,8 +13,13 @@ TEST(rt_signals) {
info(SIGRTMIN);
info(SIGRTMAX);
- /* We use signals SIGRTMIN+0 to SIGRTMIN+30 unconditionally */
+ /* We use signals SIGRTMIN+0 to SIGRTMIN+29 unconditionally. SIGRTMIN+30 can be used only when
+ * built with glibc. */
+#ifdef __GLIBC__
assert_se(SIGRTMAX - SIGRTMIN >= 30);
+#else
+ assert_se(SIGRTMAX - SIGRTMIN >= 29);
+#endif
}
static void test_signal_to_string_one(int val) {
--
2.51.0
@@ -1,33 +0,0 @@
From 7cade54777e9b715ca5b1023dd8fead64d1f4b3e Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sat, 6 Sep 2025 12:06:06 +0900
Subject: [PATCH 25/30] musl: build-path: fix reading DT_RUNPATH or DT_RPATH
musl records DT_STRTAB as offset, rather than address. So, need to add
obtained bias to read runpath or rpath.
---
src/basic/build-path.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/basic/build-path.c b/src/basic/build-path.c
index 577ff72bce..3212089379 100644
--- a/src/basic/build-path.c
+++ b/src/basic/build-path.c
@@ -34,11 +34,11 @@ static int get_runpath_from_dynamic(const ElfW(Dyn) *d, ElfW(Addr) bias, const c
break;
case DT_STRTAB:
- /* On MIPS and RISC-V DT_STRTAB records an offset, not a valid address, so it has to be adjusted
- * using the bias calculated earlier. */
+ /* On MIPS, RISC-V, or with musl, DT_STRTAB records an offset, not a valid address,
+ * so it has to be adjusted using the bias calculated earlier. */
if (d->d_un.d_val != 0)
strtab = (const char *) ((uintptr_t) d->d_un.d_val
-#if defined(__mips__) || defined(__riscv)
+#if defined(__mips__) || defined(__riscv) || !defined(__GLIBC__)
+ bias
#endif
);
--
2.51.0
@@ -1,57 +0,0 @@
From 65cd003563fe1b8f1f1b3e1c1198b3d63f993820 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 9 Jun 2025 12:00:01 +0900
Subject: [PATCH 26/30] musl: format-util: use %llu for formatting rlim_t
glibc uses uint32_t or uint64_t for rlim_t, while musl uses unsigned long long.
---
meson.build | 7 +++++++
src/basic/format-util.h | 14 ++++++++------
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/meson.build b/meson.build
index 5f235d5113..66579b9182 100644
--- a/meson.build
+++ b/meson.build
@@ -572,6 +572,13 @@ conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include <sys/resource.h
conf.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include <sys/time.h>'))
conf.set('SIZEOF_TIMEX_MEMBER', cc.sizeof('typeof(((struct timex *)0)->freq)', prefix : '#include <sys/timex.h>'))
+if get_option('libc') == 'musl'
+ if conf.get('SIZEOF_RLIM_T') != 8
+ error('Unexpected size of rlim_t: @0@'.format(conf.get('SIZEOF_RLIM_T')))
+ endif
+ conf.set_quoted('RLIM_FMT', '%llu')
+endif
+
long_max = cc.compute_int(
'LONG_MAX',
prefix : '#include <limits.h>',
diff --git a/src/basic/format-util.h b/src/basic/format-util.h
index e42f788ce6..d40ca5818a 100644
--- a/src/basic/format-util.h
+++ b/src/basic/format-util.h
@@ -39,12 +39,14 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
# error Unknown timex member size
#endif
-#if SIZEOF_RLIM_T == 8
-# define RLIM_FMT "%" PRIu64
-#elif SIZEOF_RLIM_T == 4
-# define RLIM_FMT "%" PRIu32
-#else
-# error Unknown rlim_t size
+#ifndef RLIM_FMT
+# if SIZEOF_RLIM_T == 8
+# define RLIM_FMT "%" PRIu64
+# elif SIZEOF_RLIM_T == 4
+# define RLIM_FMT "%" PRIu32
+# else
+# error Unknown rlim_t size
+# endif
#endif
#if SIZEOF_DEV_T == 8
--
2.51.0
@@ -1,47 +0,0 @@
From 524396a3058a08955683038dae86cf5aaaf10847 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 8 Sep 2025 15:08:49 +0900
Subject: [PATCH 27/30] musl: time-util: skip tm.tm_wday check
musl does not set tm_wday when it is explicitly requested.
The check is not necessary at all, it is just for safety.
Let's skip it when built with musl.
---
src/basic/time-util.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 38591b0604..c890141a99 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -668,10 +668,14 @@ static int parse_timestamp_impl(
_cleanup_free_ char *t_alloc = NULL;
usec_t usec, plus = 0, minus = 0;
bool with_tz = false;
- int r, weekday = -1;
unsigned fractional = 0;
const char *k;
struct tm tm, copy;
+ int r;
+#ifndef __GLIBC__
+ _unused_
+#endif
+ int weekday = -1;
/* Allowed syntaxes:
*
@@ -921,8 +925,11 @@ from_tm:
assert(plus == 0);
assert(minus == 0);
+#ifdef __GLIBC__
+ /* musl does not set tm_wday field and set 0 unless it is explicitly requested by %w or so. */
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
+#endif
if (gmtoff < 0) {
plus = -gmtoff * USEC_PER_SEC;
--
2.51.0
@@ -1,205 +0,0 @@
From d59cd7edd78901db8fd88c86258f6830e35e91df Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 25 Feb 2019 14:56:21 +0800
Subject: [PATCH 28/30] musl: glob-util: filter out . and .. even if
GLOB_ALTDIRFUNC is not supported
musl neither support GLOB_ALTDIRFUNC nor GLOB_BRACE.
Let's make safe_glob() work even when GLOB_ALTDIRFUNC is not supported.
Currently, GLOB_BRACE is simply ignored when it is not supported.
---
src/basic/glob-util.c | 75 +++++++++++++++++++++++++++++++++++++--
src/basic/glob-util.h | 9 ++++-
src/include/musl/glob.h | 21 +++++++++++
src/test/test-glob-util.c | 10 ++++++
4 files changed, 112 insertions(+), 3 deletions(-)
create mode 100644 src/include/musl/glob.h
diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c
index 5843ef088f..84b369be87 100644
--- a/src/basic/glob-util.c
+++ b/src/basic/glob-util.c
@@ -9,9 +9,42 @@
#include "string-util.h"
#include "strv.h"
+static bool safe_glob_verify(const char *p, const char *prefix) {
+ if (isempty(p))
+ return false; /* should not happen, but for safey. */
+
+ if (prefix) {
+ /* Skip the prefix, as we allow dots in prefix.
+ * Note, glob() does not normalize paths, hence do not use path_startswith(). */
+ p = startswith(p, prefix);
+ if (!p)
+ return false; /* should not happen, but for safety. */
+ }
+
+ for (;;) {
+ p += strspn(p, "/");
+ if (*p == '\0')
+ return true;
+ if (*p == '.') {
+ p++;
+ if (IN_SET(*p, '/', '\0'))
+ return false; /* refuse dot */
+ if (*p == '.') {
+ p++;
+ if (IN_SET(*p, '/', '\0'))
+ return false; /* refuse dot dot */
+ }
+ }
+
+ p += strcspn(p, "/");
+ if (*p == '\0')
+ return true;
+ }
+}
+
DEFINE_TRIVIAL_DESTRUCTOR(closedir_wrapper, void, closedir);
-int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret) {
+int safe_glob_internal(const char *path, int flags, bool use_gnu_extension, opendir_t opendir_func, char ***ret) {
_cleanup_(globfree) glob_t g = {
.gl_closedir = closedir_wrapper,
.gl_readdir = (struct dirent* (*)(void *)) readdir_no_dot,
@@ -23,8 +56,16 @@ int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***
assert(path);
+ // TODO: expand braces if GLOB_BRACE is specified but not supported.
+
+#if GLOB_ALTDIRFUNC == 0
+ use_gnu_extension = false;
+#else
+ SET_FLAG(flags, GLOB_ALTDIRFUNC, use_gnu_extension);
+#endif
+
errno = 0;
- r = glob(path, flags | GLOB_ALTDIRFUNC, NULL, &g);
+ r = glob(path, flags, NULL, &g);
if (r == GLOB_NOMATCH)
return -ENOENT;
if (r == GLOB_NOSPACE)
@@ -32,6 +73,36 @@ int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***
if (r != 0)
return errno_or_else(EIO);
+ if (!use_gnu_extension) {
+ _cleanup_free_ char *prefix = NULL;
+ r = glob_non_glob_prefix(path, &prefix);
+ if (r < 0 && r != -ENOENT)
+ return r;
+
+ _cleanup_strv_free_ char **filtered = NULL;
+ size_t n_filtered = 0;
+ STRV_FOREACH(p, g.gl_pathv) {
+ if (!safe_glob_verify(*p, prefix))
+ continue;
+
+ if (!ret)
+ return 0; /* Found at least one entry, let's return earlier. */
+
+ /* When musl is used, each entry is not a head of allocated memory. Hence, it is
+ * necessary to copy the string. */
+ r = strv_extend_with_size(&filtered, &n_filtered, *p);
+ if (r < 0)
+ return r;
+ }
+
+ if (n_filtered == 0)
+ return -ENOENT;
+
+ assert(ret);
+ *ret = TAKE_PTR(filtered);
+ return 0;
+ }
+
if (strv_isempty(g.gl_pathv))
return -ENOENT;
diff --git a/src/basic/glob-util.h b/src/basic/glob-util.h
index ea3e869319..36bfd6716c 100644
--- a/src/basic/glob-util.h
+++ b/src/basic/glob-util.h
@@ -7,7 +7,14 @@
typedef DIR* (*opendir_t)(const char *);
-int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret);
+int safe_glob_internal(const char *path, int flags, bool use_gnu_extension, opendir_t opendir_func, char ***ret);
+static inline int safe_glob_test(const char *path, int flags, char ***ret) {
+ /* This is for testing the fallback logic for the case GLOB_ALTDIRFUNC is not supported. */
+ return safe_glob_internal(path, flags, false, NULL, ret);
+}
+static inline int safe_glob_full(const char *path, int flags, opendir_t opendir_func, char ***ret) {
+ return safe_glob_internal(path, flags, true, opendir_func, ret);
+}
static inline int safe_glob(const char *path, int flags, char ***ret) {
return safe_glob_full(path, flags, NULL, ret);
}
diff --git a/src/include/musl/glob.h b/src/include/musl/glob.h
new file mode 100644
index 0000000000..58e6c50678
--- /dev/null
+++ b/src/include/musl/glob.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include_next <glob.h>
+
+/* Here, we set 0 to GLOB_ALTDIRFUNC and GLOB_BRACE, rather than the values used by glibc,
+ * to indicate that glob() does not support these flags. */
+
+#ifndef GLOB_ALTDIRFUNC
+#define GLOB_ALTDIRFUNC 0
+#define gl_flags __dummy1
+#define gl_closedir __dummy2[0]
+#define gl_readdir __dummy2[1]
+#define gl_opendir __dummy2[2]
+#define gl_lstat __dummy2[3]
+#define gl_stat __dummy2[4]
+#endif
+
+#ifndef GLOB_BRACE
+#define GLOB_BRACE 0
+#endif
diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c
index a9880f15c8..730961d172 100644
--- a/src/test/test-glob-util.c
+++ b/src/test/test-glob-util.c
@@ -61,20 +61,30 @@ TEST(safe_glob) {
fn = strjoina(template, "/*");
ASSERT_ERROR(safe_glob(fn, /* flags = */ 0, &v), ENOENT);
+ ASSERT_ERROR(safe_glob_test(fn, /* flags = */ 0, &v), ENOENT);
fn2 = strjoina(template, "/.*");
ASSERT_ERROR(safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &v), ENOENT);
+ ASSERT_ERROR(safe_glob_test(fn2, GLOB_NOSORT|GLOB_BRACE, &v), ENOENT);
fname = strjoina(template, "/.foobar");
ASSERT_OK(touch(fname));
ASSERT_ERROR(safe_glob(fn, /* flags = */ 0, &v), ENOENT);
+ ASSERT_ERROR(safe_glob_test(fn, /* flags = */ 0, &v), ENOENT);
ASSERT_OK(safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &v));
ASSERT_EQ(strv_length(v), 1u);
ASSERT_STREQ(v[0], fname);
ASSERT_NULL(v[1]);
+ v = strv_free(v);
+
+ ASSERT_OK(safe_glob_test(fn2, GLOB_NOSORT|GLOB_BRACE, &v));
+ ASSERT_EQ(strv_length(v), 1u);
+ ASSERT_STREQ(v[0], fname);
+ ASSERT_NULL(v[1]);
+
(void) rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL);
}
--
2.51.0
@@ -1,380 +0,0 @@
From 2c3c84939b930986b31bc0f28e9a08bf43786255 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 9 Sep 2025 13:34:31 +0900
Subject: [PATCH 29/30] musl: test: several random fixlets for unit tests
---
src/boot/test-efi-string.c | 36 ++++++++++++++++++++------
src/libsystemd/sd-bus/test-bus-error.c | 6 +++++
src/test/meson.build | 1 +
src/test/test-errno-util.c | 12 ++++++++-
src/test/test-fileio.c | 10 ++++++-
src/test/test-locale-util.c | 7 ++++-
src/test/test-os-util.c | 6 ++++-
src/test/test-seccomp.c | 8 ++++--
src/test/test-time-util.c | 24 +++++++++++++++++
test/meson.build | 2 ++
test/test-sysusers.sh.in | 22 +++++++++++++++-
11 files changed, 119 insertions(+), 15 deletions(-)
diff --git a/src/boot/test-efi-string.c b/src/boot/test-efi-string.c
index e0d3bd9b71..af6dd1fb64 100644
--- a/src/boot/test-efi-string.c
+++ b/src/boot/test-efi-string.c
@@ -410,13 +410,27 @@ TEST(startswith8) {
ASSERT_NULL(startswith8(NULL, ""));
}
-#define TEST_FNMATCH_ONE(pattern, haystack, expect) \
- ({ \
- ASSERT_EQ(fnmatch(pattern, haystack, 0), expect ? 0 : FNM_NOMATCH); \
- ASSERT_EQ(efi_fnmatch(u##pattern, u##haystack), expect); \
+#define TEST_FNMATCH_ONE_FULL(pattern, haystack, expect, skip_libc) \
+ ({ \
+ if (!skip_libc) \
+ ASSERT_EQ(fnmatch(pattern, haystack, 0), expect ? 0 : FNM_NOMATCH); \
+ ASSERT_EQ(efi_fnmatch(u##pattern, u##haystack), expect); \
})
+#define TEST_FNMATCH_ONE(pattern, haystack, expect) \
+ TEST_FNMATCH_ONE_FULL(pattern, haystack, expect, false)
+
TEST(efi_fnmatch) {
+ bool skip_libc;
+
+#ifdef __GLIBC__
+ skip_libc = false;
+#else
+ /* It seems musl is too strict in handling "[]" (or has a bug?). Anyway, let's skip some test cases
+ * when built with musl. The behavior of efi_fnmatch() does not need to match musl's fnmatch(). */
+ skip_libc = true;
+#endif
+
TEST_FNMATCH_ONE("", "", true);
TEST_FNMATCH_ONE("abc", "abc", true);
TEST_FNMATCH_ONE("aBc", "abc", false);
@@ -447,18 +461,18 @@ TEST(efi_fnmatch) {
TEST_FNMATCH_ONE("[abc", "a", false);
TEST_FNMATCH_ONE("[][!] [][!] [][!]", "[ ] !", true);
TEST_FNMATCH_ONE("[]-] []-]", "] -", true);
- TEST_FNMATCH_ONE("[1\\]] [1\\]]", "1 ]", true);
+ TEST_FNMATCH_ONE_FULL("[1\\]] [1\\]]", "1 ]", true, skip_libc);
TEST_FNMATCH_ONE("[$-\\+]", "&", true);
TEST_FNMATCH_ONE("[1-3A-C] [1-3A-C]", "2 B", true);
TEST_FNMATCH_ONE("[3-5] [3-5] [3-5]", "3 4 5", true);
TEST_FNMATCH_ONE("[f-h] [f-h] [f-h]", "f g h", true);
- TEST_FNMATCH_ONE("[a-c-f] [a-c-f] [a-c-f] [a-c-f] [a-c-f]", "a b c - f", true);
- TEST_FNMATCH_ONE("[a-c-f]", "e", false);
+ TEST_FNMATCH_ONE_FULL("[a-c-f] [a-c-f] [a-c-f] [a-c-f] [a-c-f]", "a b c - f", true, skip_libc);
+ TEST_FNMATCH_ONE_FULL("[a-c-f]", "e", false, skip_libc);
TEST_FNMATCH_ONE("[--0] [--0] [--0]", "- . 0", true);
TEST_FNMATCH_ONE("[+--] [+--] [+--]", "+ , -", true);
TEST_FNMATCH_ONE("[f-l]", "m", false);
TEST_FNMATCH_ONE("[b]", "z-a", false);
- TEST_FNMATCH_ONE("[a\\-z]", "b", false);
+ TEST_FNMATCH_ONE_FULL("[a\\-z]", "b", false, skip_libc);
TEST_FNMATCH_ONE("?a*b[.-0]c", "/a/b/c", true);
TEST_FNMATCH_ONE("debian-*-*-*.*", "debian-jessie-2018-06-17-kernel-image-5.10.0-16-amd64.efi", true);
@@ -674,8 +688,14 @@ TEST(xvasprintf_status) {
test_printf_one("string");
test_printf_one("%%-%%%%");
+#ifdef __GLIBC__
test_printf_one("%p %p %32p %*p %*p", NULL, (void *) 0xF, &errno, 0, &saved_argc, 20, &saved_argv);
test_printf_one("%-10p %-32p %-*p %-*p", NULL, &errno, 0, &saved_argc, 20, &saved_argv);
+#else
+ /* musl prints NULL as 0, while glibc and our implementation print it as (nil). */
+ test_printf_one("%p %32p %*p %*p", (void *) 0xF, &errno, 0, &saved_argc, 20, &saved_argv);
+ test_printf_one("%-32p %-*p %-*p", &errno, 0, &saved_argc, 20, &saved_argv);
+#endif
test_printf_one("%c %3c %*c %*c %-8c", '1', '!', 0, 'a', 9, '_', '>');
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index c6b86be621..d69eb984dc 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -232,7 +232,13 @@ TEST(sd_bus_error_set_errnof) {
errno = EACCES;
assert_se(asprintf(&str, "%m") >= 0);
assert_se(streq(error.message, str));
+#ifdef __GLIBC__
assert_se(error._need_free == 0);
+#else
+ /* musl's strerror_l() always writes error message in the given buffer, hence the we need to
+ * free it. */
+ assert_se(error._need_free == 1);
+#endif
str = mfree(str);
sd_bus_error_free(&error);
diff --git a/src/test/meson.build b/src/test/meson.build
index 1dd61effc0..4bdfc65f04 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -16,6 +16,7 @@ test_env = {
'PROJECT_BUILD_ROOT' : meson.project_build_root(),
'SYSTEMD_SLOW_TESTS' : want_slow_tests ? '1' : '0',
'PYTHONDONTWRITEBYTECODE' : '1',
+ 'SYSTEMD_LIBC' : get_option('libc'),
}
if conf.get('ENABLE_LOCALED') == 1
test_env += {'SYSTEMD_LANGUAGE_FALLBACK_MAP' : language_fallback_map}
diff --git a/src/test/test-errno-util.c b/src/test/test-errno-util.c
index 1a0154fb05..1ad067b2c7 100644
--- a/src/test/test-errno-util.c
+++ b/src/test/test-errno-util.c
@@ -26,18 +26,28 @@ TEST(STRERROR) {
log_info("STRERROR(%d), STRERROR(%d) → %s, %s", 200, 201, STRERROR(200), STRERROR(201));
const char *a = STRERROR(200), *b = STRERROR(201);
+#ifdef __GLIBC__
ASSERT_NOT_NULL(strstr(a, "200"));
ASSERT_NOT_NULL(strstr(b, "201"));
+#else
+ /* musl provides catch all error message for unknown error number. */
+ ASSERT_STREQ(a, "No error information");
+ ASSERT_STREQ(b, "No error information");
+#endif
/* Check with negative values */
ASSERT_STREQ(a, STRERROR(-200));
ASSERT_STREQ(b, STRERROR(-201));
const char *c = STRERROR(INT_MAX);
+ log_info("STRERROR(%d) → %s", INT_MAX, c);
+#ifdef __GLIBC__
char buf[DECIMAL_STR_MAX(int)];
xsprintf(buf, "%d", INT_MAX); /* INT_MAX is hexadecimal, use printf to convert to decimal */
- log_info("STRERROR(%d) → %s", INT_MAX, c);
ASSERT_NOT_NULL(strstr(c, buf));
+#else
+ ASSERT_STREQ(c, "No error information");
+#endif
}
TEST(STRERROR_OR_ELSE) {
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index e0ae30bbb9..ad572056c2 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -400,7 +400,15 @@ TEST(write_string_stream) {
f = fdopen(fd, "r");
assert_se(f);
- assert_se(write_string_stream(f, "boohoo", 0) < 0);
+#ifdef __GLIBC__
+ ASSERT_ERROR(write_string_stream(f, "boohoo", 0), EBADF);
+#else
+ /* Even the file is opened with the read-only mode, fputs() and fputc() by musl succeed but nothing
+ * actually written, thus write_string_stream() also succeeds. */
+ ASSERT_OK(write_string_stream(f, "boohoo", 0));
+ rewind(f);
+ ASSERT_NULL(fgets(buf, sizeof(buf), f));
+#endif
f = safe_fclose(f);
f = fopen(fn, "r+");
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
index e264cff5dd..faa7ac4211 100644
--- a/src/test/test-locale-util.c
+++ b/src/test/test-locale-util.c
@@ -50,7 +50,12 @@ TEST(locale_is_installed) {
assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0);
/* Definitely not installed */
- assert_se(locale_is_installed("zz_ZZ") == 0);
+#ifdef __GLIBC__
+ ASSERT_OK_ZERO(locale_is_installed("zz_ZZ"));
+#else
+ /* musl seems to return a non-null locale object even if it is not installed. */
+ ASSERT_OK_POSITIVE(locale_is_installed("zz_ZZ"));
+#endif
}
TEST(keymaps) {
diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c
index fcab1139c2..902062560f 100644
--- a/src/test/test-os-util.c
+++ b/src/test/test-os-util.c
@@ -127,7 +127,11 @@ TEST(os_release_support_ended) {
ASSERT_TRUE(os_release_support_ended("1999-01-01", false, NULL));
ASSERT_FALSE(os_release_support_ended("2037-12-31", false, NULL));
- assert_se(os_release_support_ended("-1-1-1", true, NULL) == -EINVAL);
+#ifdef __GLIBC__
+ ASSERT_ERROR(os_release_support_ended("-1-1-1", true, NULL), EINVAL);
+#else
+ ASSERT_ERROR(os_release_support_ended("-1-1-1", true, NULL), ERANGE);
+#endif
r = os_release_support_ended(NULL, false, NULL);
if (r < 0)
diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c
index 13c65a93cd..a9953b2c98 100644
--- a/src/test/test-seccomp.c
+++ b/src/test/test-seccomp.c
@@ -553,9 +553,13 @@ TEST(memory_deny_write_execute_mmap) {
assert_se(seccomp_memory_deny_write_execute() >= 0);
p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64)
+#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) || !defined(__GLIBC__)
assert_se(p == MAP_FAILED);
- assert_se(errno == EPERM);
+# ifdef __GLIBC__
+ ASSERT_EQ(errno, EPERM);
+# else
+ ASSERT_EQ(errno, ENOMEM);
+# endif
#endif
/* Depending on kernel, libseccomp, and glibc versions, other architectures
* might fail or not. Let's not assert success. */
diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c
index 7569f89ca0..203f8a148d 100644
--- a/src/test/test-time-util.c
+++ b/src/test/test-time-util.c
@@ -406,6 +406,25 @@ static void test_format_timestamp_impl(usec_t x) {
const char *xx = FORMAT_TIMESTAMP(x);
ASSERT_NOT_NULL(xx);
+#ifndef __GLIBC__
+ /* Because of the timezone change, format_timestamp() may set timezone that is currently unused.
+ * E.g. Africa/Juba may set EAT, but currently it uses CAT/CAST. */
+ const char *space;
+ ASSERT_NOT_NULL(space = strrchr(xx, ' '));
+ const char *tz = space + 1;
+ if (!streq_ptr(tz, tzname[0]) &&
+ !streq_ptr(tz, tzname[1]) &&
+ parse_gmtoff(tz, NULL) < 0) {
+
+ log_warning("@" USEC_FMT " → %s, timezone '%s' is currently unused, ignoring.", x, xx, tz);
+
+ /* Verify the generated string except for the timezone part. Of course, in most cases, parsed
+ * time does not match with the input, hence only check if it is parsable. */
+ ASSERT_OK(parse_timestamp(strndupa_safe(xx, space - xx), NULL));
+ return;
+ }
+#endif
+
usec_t y;
ASSERT_OK(parse_timestamp(xx, &y));
const char *yy = FORMAT_TIMESTAMP(y);
@@ -1116,7 +1135,12 @@ TEST(in_utc_timezone) {
assert_se(setenv("TZ", ":UTC", 1) >= 0);
assert_se(in_utc_timezone());
ASSERT_STREQ(tzname[0], "UTC");
+#ifdef __GLIBC__
ASSERT_STREQ(tzname[1], "UTC");
+#else
+ /* musl sets an empty string to tzname[1] when DST is not used by the timezone. */
+ ASSERT_STREQ(tzname[1], "");
+#endif
assert_se(timezone == 0);
assert_se(daylight == 0);
diff --git a/test/meson.build b/test/meson.build
index 505a14aa81..1f1ec0a0e1 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -13,6 +13,7 @@ if conf.get('ENABLE_SYSUSERS') == 1
# https://github.com/mesonbuild/meson/issues/2681
args : exe.full_path(),
depends : exe,
+ env : test_env,
suite : 'sysusers')
if have_standalone_binaries
@@ -22,6 +23,7 @@ if conf.get('ENABLE_SYSUSERS') == 1
# https://github.com/mesonbuild/meson/issues/2681
args : exe.full_path(),
depends : exe,
+ env : test_env,
suite : 'sysusers')
endif
endif
diff --git a/test/test-sysusers.sh.in b/test/test-sysusers.sh.in
index 3218923590..ae7bfee2fe 100755
--- a/test/test-sysusers.sh.in
+++ b/test/test-sysusers.sh.in
@@ -13,6 +13,12 @@ TESTDIR=$(mktemp --tmpdir --directory "test-sysusers.XXXXXXXXXX")
# shellcheck disable=SC2064
trap "rm -rf '$TESTDIR'" EXIT INT QUIT PIPE
+skip_nis() {
+ # musl seems to not support NIS entries. Let's skip the test case for NIS entries.
+ local path=${1:?}
+ [[ "${SYSTEMD_LIBC:-}" == musl && "${path##*/}" == "test-11.input" ]]
+}
+
prepare_testdir() {
mkdir -p "$TESTDIR/etc/sysusers.d/"
mkdir -p "$TESTDIR/usr/lib/sysusers.d/"
@@ -50,6 +56,7 @@ rm -f "$TESTDIR"/etc/sysusers.d/* "$TESTDIR"/usr/lib/sysusers.d/*
# happy tests
for f in $(find "$SOURCE"/test-*.input | sort -V); do
+ skip_nis "$f" && continue
echo "*** Running $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
@@ -59,6 +66,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
done
for f in $(find "$SOURCE"/test-*.input | sort -V); do
+ skip_nis "$f" && continue
echo "*** Running $f on stdin"
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
@@ -68,6 +76,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
done
for f in $(find "$SOURCE"/test-*.input | sort -V); do
+ skip_nis "$f" && continue
echo "*** Running $f on stdin with --replace"
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
@@ -133,6 +142,7 @@ SYS_GID_MAX999
EOF
for f in $(find "$SOURCE"/test-*.input | sort -V); do
+ skip_nis "$f" && continue
echo "*** Running $f (with login.defs)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
@@ -149,6 +159,7 @@ mv "$TESTDIR/etc/login.defs" "$TESTDIR/etc/login.defs.moved"
ln -s ../../../../../etc/login.defs.moved "$TESTDIR/etc/login.defs"
for f in $(find "$SOURCE"/test-*.input | sort -V); do
+ skip_nis "$f" && continue
echo "*** Running $f (with login.defs symlinked)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
@@ -161,13 +172,22 @@ done
rm -f "$TESTDIR"/etc/sysusers.d/* "$TESTDIR"/usr/lib/sysusers.d/*
+preprocess_error() {
+ # Convert error message for ERANGE from glibc to musl.
+ if [[ "${SYSTEMD_LIBC:-}" == musl ]]; then
+ sed -e 's/Numerical result out of range/Result not representable/' "${1:?}"
+ else
+ cat "${1:?}"
+ fi
+}
+
# tests for error conditions
for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do
echo "*** Running test $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
SYSTEMD_LOG_LEVEL=info "$SYSUSERS" --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
- if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err" >&2; then
+ if ! diff -u "$TESTDIR/err" <(preprocess_error "${f%.*}.expected-err") >&2; then
echo >&2 "**** Unexpected error output for $f"
cat >&2 "$TESTDIR/err"
exit 1
--
2.51.0
@@ -1,352 +0,0 @@
From b9a6cd49a4d4c6a40968224f5aac20586273b171 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 9 Sep 2025 13:42:24 +0900
Subject: [PATCH 30/30] musl: ci: add build test and unit tests
---
.github/workflows/build-test-musl.sh | 125 ++++++++++++++++++++++++++
.github/workflows/linter.yml | 9 ++
.github/workflows/unit-tests-musl.sh | 55 ++++++++++++
.github/workflows/unit-tests-musl.yml | 112 +++++++++++++++++++++++
4 files changed, 301 insertions(+)
create mode 100755 .github/workflows/build-test-musl.sh
create mode 100755 .github/workflows/unit-tests-musl.sh
create mode 100644 .github/workflows/unit-tests-musl.yml
diff --git a/.github/workflows/build-test-musl.sh b/.github/workflows/build-test-musl.sh
new file mode 100755
index 0000000000..bc38924666
--- /dev/null
+++ b/.github/workflows/build-test-musl.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+
+if ! command -v musl-gcc 2>/dev/null; then
+ echo "musl-gcc is not installed, skipping the test."
+ exit 0
+fi
+
+. /etc/os-release
+
+TMPDIR=$(mktemp -d)
+
+cleanup() (
+ set +e
+
+ if [[ -d "$TMPDIR" ]]; then
+ rm -rf "$TMPDIR"
+ fi
+)
+
+trap cleanup EXIT ERR INT TERM
+
+mkdir -p "${TMPDIR}/build"
+mkdir -p "${TMPDIR}/include"
+
+CFLAGS="-idirafter ${TMPDIR}/include"
+
+LINKS=(
+ acl
+ archive.h
+ archive_entry.h
+ asm-generic
+ audit-records.h
+ audit_logging.h
+ bpf
+ bzlib.h
+ dwarf.h
+ elfutils
+ fido.h
+ gcrypt.h
+ gelf.h
+ gnutls
+ idn2.h
+ libaudit.h
+ libcryptsetup.h
+ libelf.h
+ libkmod.h
+ lz4.h
+ lz4frame.h
+ lz4hc.h
+ lzma
+ lzma.h
+ microhttpd.h
+ mtd
+ openssl
+ pcre2.h
+ pwquality.h
+ qrencode.h
+ seccomp-syscalls.h
+ seccomp.h
+ security
+ sys/acl.h
+ sys/capability.h
+ tss2
+ xen
+ xkbcommon
+ zconf.h
+ zlib.h
+ zstd.h
+ zstd_errors.h
+)
+
+if [[ "$ID" == arch ]]; then
+ LINKS+=(
+ asm
+ curl
+ gpg-error.h
+ libiptc
+ linux
+ )
+elif [[ "$ID" == centos ]]; then
+ LINKS+=(
+ asm
+ curl
+ gpg-error.h
+ linux
+ selinux
+ )
+elif [[ "$ID" == fedora ]]; then
+ LINKS+=(
+ asm
+ curl
+ gpg-error.h
+ libiptc
+ linux
+ selinux
+ )
+elif [[ "$ID" == debian ]] || [[ "$ID" == ubuntu ]]; then
+ # Currently, debian/ubuntu does not provide crypt.h for musl. Hence, this does not work.
+
+ CFLAGS="$CFLAGS -idirafter /usr/include/$(uname -m)-linux-gnu"
+
+ LINKS+=(
+ linux
+ selinux
+ sys/apparmor.h
+ )
+fi
+
+for t in "${LINKS[@]}"; do
+ [[ -e "/usr/include/$t" ]]
+ link="${TMPDIR}/include/${t}"
+ mkdir -p "${link%/*}"
+ ln -s "/usr/include/$t" "$link"
+done
+
+env CC=musl-gcc \
+ CXX=musl-gcc \
+ CFLAGS="$CFLAGS" \
+ CXXFLAGS="$CFLAGS" \
+ meson setup --werror -Ddbus-interfaces-dir=no -Dlibc=musl "${TMPDIR}/build"
+
+ninja -v -C "${TMPDIR}/build"
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index c107b2da1e..b92a00478c 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -49,6 +49,12 @@ jobs:
[Build]
ToolsTreeDistribution=fedora
ToolsTreeRelease=rawhide
+
+ [Content]
+ Packages=
+ libgcrypt-devel
+ libgpg-error-devel
+ musl-gcc
EOF
mkosi -f box -- true
@@ -77,3 +83,6 @@ jobs:
- name: Run clang-tidy
run: mkosi box -- meson test -C build --suite=clang-tidy --print-errorlogs --no-stdsplit
+
+ - name: Build with musl
+ run: mkosi box -- .github/workflows/build-test-musl.sh
diff --git a/.github/workflows/unit-tests-musl.sh b/.github/workflows/unit-tests-musl.sh
new file mode 100755
index 0000000000..f6e2b2f536
--- /dev/null
+++ b/.github/workflows/unit-tests-musl.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# shellcheck disable=SC2206
+PHASES=(${@:-SETUP BUILD RUN CLEANUP})
+
+function info() {
+ echo -e "\033[33;1m$1\033[0m"
+}
+function run_meson() {
+ if ! meson "$@"; then
+ find . -type f -name meson-log.txt -exec cat '{}' +
+ return 1
+ fi
+}
+
+set -ex
+
+for phase in "${PHASES[@]}"; do
+ case $phase in
+ SETUP)
+ info "Setup phase"
+ # Alpine still uses split-usr.
+ for i in /bin/* /sbin/*; do
+ ln -rs "$i" "/usr/$i";
+ done
+ ;;
+ BUILD)
+ info "Build phase"
+ run_meson setup --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true -Dlibc=musl build
+ ninja -C build -v
+ ;;
+ RUN)
+ info "Run phase"
+ # Create dummy machine ID.
+ echo '052e58f661f94bd080e258b96aea3f7b' > /etc/machine-id
+
+ # Start dbus for several unit tests.
+ mkdir -p /var/run/dbus
+ /usr/bin/dbus-daemon --system || :
+
+ # Here, we explicitly set SYSTEMD_IN_CHROOT=yes as unfortunately runnin_in_chroot() does not
+ # correctly detect the environment.
+ env \
+ SYSTEMD_IN_CHROOT=yes \
+ meson test -C build -v
+ ;;
+ CLEANUP)
+ info "Cleanup phase"
+ ;;
+ *)
+ echo >&2 "Unknown phase '$phase'"
+ exit 1
+ esac
+done
diff --git a/.github/workflows/unit-tests-musl.yml b/.github/workflows/unit-tests-musl.yml
new file mode 100644
index 0000000000..e30974254c
--- /dev/null
+++ b/.github/workflows/unit-tests-musl.yml
@@ -0,0 +1,112 @@
+---
+# vi: ts=2 sw=2 et:
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+name: Unit tests (musl)
+on:
+ pull_request:
+ paths:
+ - '**/meson.build'
+ - '.github/workflows/**'
+ - 'meson_options.txt'
+ - 'src/**'
+ - 'test/fuzz/**'
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Repository checkout
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+
+ - name: Install build dependencies
+ uses: jirutka/setup-alpine@v1
+ with:
+ arch: x86_64
+ branch: edge
+ packages: >
+ acl
+ acl-dev
+ audit-dev
+ bash
+ bash-completion-dev
+ bpftool
+ build-base
+ bzip2-dev
+ coreutils
+ cryptsetup-dev
+ curl-dev
+ dbus
+ dbus-dev
+ elfutils-dev
+ gettext-dev
+ git
+ glib-dev
+ gnutls-dev
+ gperf
+ grep
+ iproute2
+ iptables-dev
+ kbd
+ kexec-tools
+ kmod
+ kmod-dev
+ libapparmor-dev
+ libarchive-dev
+ libbpf-dev
+ libcap-dev
+ libcap-utils
+ libfido2-dev
+ libgcrypt-dev
+ libidn2-dev
+ libmicrohttpd-dev
+ libpwquality-dev
+ libqrencode-dev
+ libseccomp-dev
+ libselinux-dev
+ libxkbcommon-dev
+ linux-pam-dev
+ lz4-dev
+ meson
+ openssl
+ openssl-dev
+ p11-kit-dev
+ pcre2-dev
+ pkgconf
+ polkit-dev
+ py3-elftools
+ py3-jinja2
+ py3-pefile
+ py3-pytest
+ py3-lxml
+ quota-tools
+ rsync
+ sfdisk
+ tpm2-tss-dev
+ tpm2-tss-esys
+ tpm2-tss-rc
+ tpm2-tss-tcti-device
+ tzdata
+ util-linux-dev
+ util-linux-login
+ util-linux-misc
+ valgrind-dev
+ xen-dev
+ zlib-dev
+ zstd-dev
+
+ - name: Setup
+ run: .github/workflows/unit-tests-musl.sh SETUP
+ shell: alpine.sh --root {0}
+ - name: Build
+ run: .github/workflows/unit-tests-musl.sh BUILD
+ shell: alpine.sh {0}
+ - name: Run
+ run: .github/workflows/unit-tests-musl.sh RUN
+ shell: alpine.sh --root {0}
+ - name: Cleanup
+ run: .github/workflows/unit-tests-musl.sh CLEANUP
+ shell: alpine.sh --root {0}
--
2.51.0