locale-util: dlopen() libintl instead of linking against it
dgettext() lives in libc on glibc and in libintl.so.8 on musl with
gettext. Resolve it via dlsym() so neither configuration produces a
hard link-time dependency on libintl: try libintl.so.8 first and fall
back to RTLD_DEFAULT (which finds dgettext in libc on glibc).
The _() macro now expands to a runtime check that returns the
untranslated string if dlopen_libintl() has not run successfully, so
callers don't have to gate every translatable message on a runtime
check. pam_systemd_home — currently the only consumer of _() — calls
dlopen_libintl() best-effort from each PAM entry point.
The meson find_library('intl') dance is replaced with a has_header()
check; the only thing we need at build time is the prototype.
This commit is contained in:
+5
-14
@@ -983,20 +983,11 @@ librt = cc.find_library('rt')
|
||||
libm = cc.find_library('m')
|
||||
libdl = cc.find_library('dl')
|
||||
|
||||
# On some distributions that use 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 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
|
||||
# Header presence check only — dgettext itself is resolved via dlopen_libintl() at runtime, so we never
|
||||
# link against libintl. On glibc dgettext lives in libc; on musl gettext-dev provides libintl.h alongside
|
||||
# libintl.so.8 which we dlopen() if present.
|
||||
if not cc.has_header('libintl.h')
|
||||
error('libintl.h not found (install gettext / gettext-dev)')
|
||||
endif
|
||||
|
||||
# On some architectures, libatomic is required. But on some installations,
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef __GLIBC__
|
||||
#include "sd-dlopen.h"
|
||||
#endif
|
||||
|
||||
#include "dirent-util.h"
|
||||
#include "dlfcn-util.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -22,6 +27,32 @@
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
#ifdef __GLIBC__
|
||||
DLSYM_PROTOTYPE(dgettext) = dgettext;
|
||||
#else
|
||||
DLSYM_PROTOTYPE(dgettext) = NULL;
|
||||
#endif
|
||||
|
||||
int dlopen_libintl(int log_level) {
|
||||
#ifdef __GLIBC__
|
||||
return 1;
|
||||
#else
|
||||
static void *libintl_dl = NULL;
|
||||
|
||||
SD_ELF_NOTE_DLOPEN(
|
||||
"intl",
|
||||
"Support for message translation via gettext",
|
||||
SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
|
||||
"libintl.so.8");
|
||||
|
||||
return dlopen_many_sym_or_warn(
|
||||
&libintl_dl,
|
||||
"libintl.so.8",
|
||||
log_level,
|
||||
DLSYM_ARG(dgettext));
|
||||
#endif
|
||||
}
|
||||
|
||||
static char* normalize_locale(const char *name) {
|
||||
const char *e;
|
||||
|
||||
|
||||
+12
-1
@@ -1,9 +1,18 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <libintl.h> /* IWYU pragma: export */
|
||||
#include <locale.h> /* IWYU pragma: export */
|
||||
|
||||
#include "basic-forward.h"
|
||||
#include "dlfcn-util.h"
|
||||
|
||||
/* format_arg(2) propagates the format-string nature of the second argument to the return value, so that
|
||||
* printf(_("Hello %s"), name) still gets checked. It survives both DLSYM_PROTOTYPE's typeof() and the
|
||||
* ternary in _() below — verified on gcc and clang. */
|
||||
extern DLSYM_PROTOTYPE(dgettext) __attribute__((format_arg(2)));
|
||||
|
||||
int dlopen_libintl(int log_level);
|
||||
|
||||
typedef enum LocaleVariable {
|
||||
/* We don't list LC_ALL here on purpose. People should be
|
||||
@@ -31,7 +40,9 @@ int get_locales(char ***ret);
|
||||
bool locale_is_valid(const char *name);
|
||||
int locale_is_installed(const char *name);
|
||||
|
||||
#define _(String) dgettext(GETTEXT_PACKAGE, String)
|
||||
/* Falls back to the untranslated string if dlopen_libintl() hasn't run or has failed, so callers don't have
|
||||
* to gate every translatable message on a runtime check. */
|
||||
#define _(String) (sym_dgettext ? sym_dgettext(GETTEXT_PACKAGE, (String)) : (String))
|
||||
#define N_(String) String
|
||||
|
||||
bool is_locale_utf8(void);
|
||||
|
||||
@@ -115,7 +115,6 @@ modules += [
|
||||
'conditions' : ['HAVE_PAM'],
|
||||
'sources' : pam_systemd_home_sources,
|
||||
'dependencies' : [
|
||||
libintl,
|
||||
libpam_cflags,
|
||||
threads,
|
||||
],
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <libintl.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
@@ -792,6 +790,8 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
|
||||
if (r < 0)
|
||||
return PAM_SERVICE_ERR;
|
||||
|
||||
(void) dlopen_libintl(LOG_DEBUG); /* best-effort: messages won't be translated if this fails */
|
||||
|
||||
pam_log_setup();
|
||||
|
||||
if (parse_env(pamh, &flags) < 0)
|
||||
@@ -857,6 +857,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
|
||||
if (r < 0)
|
||||
return PAM_SERVICE_ERR;
|
||||
|
||||
(void) dlopen_libintl(LOG_DEBUG); /* best-effort: messages won't be translated if this fails */
|
||||
|
||||
pam_log_setup();
|
||||
|
||||
if (parse_env(pamh, &flags) < 0)
|
||||
@@ -914,6 +916,8 @@ _public_ PAM_EXTERN int pam_sm_close_session(
|
||||
if (r < 0)
|
||||
return PAM_SERVICE_ERR;
|
||||
|
||||
(void) dlopen_libintl(LOG_DEBUG); /* best-effort: messages won't be translated if this fails */
|
||||
|
||||
pam_log_setup();
|
||||
|
||||
if (parse_argv(pamh,
|
||||
@@ -979,6 +983,8 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
|
||||
if (r < 0)
|
||||
return PAM_SERVICE_ERR;
|
||||
|
||||
(void) dlopen_libintl(LOG_DEBUG); /* best-effort: messages won't be translated if this fails */
|
||||
|
||||
pam_log_setup();
|
||||
|
||||
if (parse_env(pamh, &flags) < 0)
|
||||
@@ -1098,6 +1104,8 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
|
||||
if (r < 0)
|
||||
return PAM_SERVICE_ERR;
|
||||
|
||||
(void) dlopen_libintl(LOG_DEBUG); /* best-effort: messages won't be translated if this fails */
|
||||
|
||||
pam_log_setup();
|
||||
|
||||
if (parse_argv(pamh,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "libcrypt-util.h"
|
||||
#include "libfido2-util.h"
|
||||
#include "libmount-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "main-func.h"
|
||||
#include "microhttpd-util.h"
|
||||
#include "module-util.h"
|
||||
@@ -65,6 +66,7 @@ static int run(int argc, char **argv) {
|
||||
ASSERT_DLOPEN(dlopen_libblkid, HAVE_BLKID);
|
||||
ASSERT_DLOPEN(dlopen_libcrypt, HAVE_LIBCRYPT);
|
||||
ASSERT_DLOPEN(dlopen_libfido2, HAVE_LIBFIDO2);
|
||||
ASSERT_OK(dlopen_libintl(LOG_DEBUG)); /* Required to be available at build time. */
|
||||
ASSERT_DLOPEN(dlopen_libkmod, HAVE_KMOD);
|
||||
ASSERT_DLOPEN(dlopen_libmount, HAVE_LIBMOUNT);
|
||||
ASSERT_DLOPEN(dlopen_libpam, HAVE_PAM);
|
||||
|
||||
Reference in New Issue
Block a user