diff options
author | Philip Withnall <philip@tecnocode.co.uk> | 2022-07-24 23:31:27 +0000 |
---|---|---|
committer | Philip Withnall <philip@tecnocode.co.uk> | 2022-07-24 23:31:27 +0000 |
commit | 30c840a7556f41e0f21751e91294f711304cd947 (patch) | |
tree | 091faeef7152e5580356dfa5d08837a833319d95 | |
parent | 9c1b2399ec95ff02aa6755dabf384507c27f508c (diff) | |
parent | f73641482586bd3e49b73067569aa79ecd56aa5d (diff) | |
download | glib-30c840a7556f41e0f21751e91294f711304cd947.tar.gz |
Merge branch 'wip/smcv/gio-launch-desktop-sd-journal' into 'main'
Bring back gio-launch-desktop, use it to redirect stdout/stderr to the Journal
Closes #2682
See merge request GNOME/glib!2819
-rw-r--r-- | gio/gdesktopappinfo.c | 53 | ||||
-rw-r--r-- | gio/gio-launch-desktop.c | 253 | ||||
-rw-r--r-- | gio/meson.build | 14 | ||||
-rw-r--r-- | gio/tests/meson.build | 4 | ||||
-rw-r--r-- | glib/gjournal-private.c | 78 | ||||
-rw-r--r-- | glib/gjournal-private.h | 25 | ||||
-rw-r--r-- | glib/gmessages.c | 32 | ||||
-rw-r--r-- | glib/meson.build | 4 | ||||
-rw-r--r-- | meson.build | 2 |
9 files changed, 413 insertions, 52 deletions
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index c0950ae75..c46a8f09b 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -51,6 +51,7 @@ #include "gfileicon.h" #include <glib/gstdio.h> #include "glibintl.h" +#include "glib-private.h" #include "giomodule-priv.h" #include "gappinfo.h" #include "gappinfoprivate.h" @@ -165,6 +166,7 @@ static const gchar *desktop_file_dirs_config_dir = NULL; static DesktopFileDir *desktop_file_dir_user_config = NULL; /* (owned) */ static DesktopFileDir *desktop_file_dir_user_data = NULL; /* (owned) */ static GMutex desktop_file_dir_lock; +static const gchar *gio_launch_desktop_path = NULL; /* Monitor 'changed' signal handler {{{2 */ static void desktop_file_dir_reset (DesktopFileDir *dir); @@ -2872,15 +2874,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, char *sn_id = NULL; char **wrapped_argv; int i; - gsize j; - const gchar * const wrapper_argv[] = - { - "/bin/sh", - "-e", - "-u", - "-c", "export GIO_LAUNCHED_DESKTOP_FILE_PID=$$; exec \"$@\"", - "sh", /* argv[0] for sh */ - }; old_uris = dup_uris; if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error)) @@ -2924,26 +2917,32 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, emit_launch_started (launch_context, info, sn_id); } - /* Wrap the @argv in a command which will set the - * `GIO_LAUNCHED_DESKTOP_FILE_PID` environment variable. We can’t set this - * in @envp along with `GIO_LAUNCHED_DESKTOP_FILE` because we need to know - * the PID of the new forked process. We can’t use setenv() between fork() - * and exec() because we’d rather use posix_spawn() for speed. - * - * `sh` should be available on all the platforms that `GDesktopAppInfo` - * currently supports (since they are all POSIX). If additional platforms - * need to be supported in future, it will probably have to be replaced - * with a wrapper program (grep the GLib git history for - * `gio-launch-desktop` for an example of this which could be - * resurrected). */ - wrapped_argv = g_new (char *, argc + G_N_ELEMENTS (wrapper_argv) + 1); - - for (j = 0; j < G_N_ELEMENTS (wrapper_argv); j++) - wrapped_argv[j] = g_strdup (wrapper_argv[j]); + if (g_once_init_enter (&gio_launch_desktop_path)) + { + const gchar *tmp = NULL; + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); + + /* Allow test suite to specify path to gio-launch-desktop */ + if (!is_setuid) + tmp = g_getenv ("GIO_LAUNCH_DESKTOP"); + + /* Allow build system to specify path to gio-launch-desktop */ + if (tmp == NULL && g_file_test (GIO_LAUNCH_DESKTOP, G_FILE_TEST_IS_EXECUTABLE)) + tmp = GIO_LAUNCH_DESKTOP; + + /* Fall back on usual searching in $PATH */ + if (tmp == NULL) + tmp = "gio-launch-desktop"; + g_once_init_leave (&gio_launch_desktop_path, tmp); + } + + wrapped_argv = g_new (char *, argc + 2); + wrapped_argv[0] = g_strdup (gio_launch_desktop_path); + for (i = 0; i < argc; i++) - wrapped_argv[i + G_N_ELEMENTS (wrapper_argv)] = g_steal_pointer (&argv[i]); + wrapped_argv[i + 1] = g_steal_pointer (&argv[i]); - wrapped_argv[i + G_N_ELEMENTS (wrapper_argv)] = NULL; + wrapped_argv[i + 1] = NULL; g_free (argv); argv = NULL; diff --git a/gio/gio-launch-desktop.c b/gio/gio-launch-desktop.c new file mode 100644 index 000000000..9ed6712f7 --- /dev/null +++ b/gio/gio-launch-desktop.c @@ -0,0 +1,253 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2004 Ximian Inc. + * Copyright 2011-2022 systemd contributors + * Copyright (C) 2018 Endless Mobile, Inc. + * Copyright 2022 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Daniel Drake <drake@endlessm.com> + */ + +/* + * gio-launch-desktop: GDesktopAppInfo helper + * Executable wrapper to set GIO_LAUNCHED_DESKTOP_FILE_PID + * There are complications when doing this in a fork()/exec() codepath, + * and it cannot otherwise be done with posix_spawn(). + * This wrapper is designed to be minimal and lightweight. + * It does not even link against glib. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +#if defined(__linux__) && !defined(__BIONIC__) +#include <alloca.h> +#include <assert.h> +#include <errno.h> +#include <stddef.h> +#include <string.h> +#include <syslog.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "gjournal-private.h" + +/* + * write_all: + * @fd: a file descriptor + * @vbuf: a buffer + * @to_write: length of @vbuf + * + * Write all bytes from @vbuf to @fd, blocking if necessary. + * + * Returns: 0 on success, -1 with errno set on failure + */ +static int +write_all (int fd, const void *vbuf, size_t to_write) +{ + const char *buf = vbuf; + + while (to_write > 0) + { + ssize_t count = write (fd, buf, to_write); + if (count < 0) + { + if (errno != EINTR) + return -1; + } + else + { + to_write -= count; + buf += count; + } + } + + return 0; +} + +/* + * journal_stream_fd: + * @identifier: identifier (syslog tag) for logged messages + * @priority: a priority between `LOG_EMERG` and `LOG_DEBUG` inclusive + * @level_prefix: if nonzero, journald will interpret prefixes like <0> + * as specifying the priority for a line + * + * Reimplementation of sd_journal_stream_fd(), to avoid having to link + * gio-launch-desktop to libsystemd. + * + * Note that unlike the libsystemd version, this reports errors by returning + * -1 with errno set. + * + * Returns: a non-negative fd number, or -1 with errno set on error + */ +static int +journal_stream_fd (const char *identifier, + int priority, + int level_prefix) +{ + union + { + struct sockaddr sa; + struct sockaddr_un un; + } sa = + { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; + socklen_t salen; + char *header; + int fd; + size_t l; + int saved_errno; + /* Arbitrary large size for the sending buffer, from systemd */ + int large_buffer_size = 8 * 1024 * 1024; + + static_assert (LOG_EMERG == 0, "Linux ABI defines LOG_EMERG"); + static_assert (LOG_DEBUG == 7, "Linux ABI defines LOG_DEBUG"); + + fd = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + + if (fd < 0) + goto fail; + + salen = offsetof (struct sockaddr_un, sun_path) + strlen (sa.un.sun_path) + 1; + + if (connect (fd, &sa.sa, salen) < 0) + goto fail; + + if (shutdown (fd, SHUT_RD) < 0) + goto fail; + + (void) setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &large_buffer_size, + (socklen_t) sizeof (large_buffer_size)); + + if (identifier == NULL) + identifier = ""; + + if (priority < 0) + priority = 0; + + if (priority > 7) + priority = 7; + + l = strlen (identifier); + header = alloca (l + 1 /* identifier, newline */ + + 1 /* empty unit ID, newline */ + + 2 /* priority, newline */ + + 2 /* level prefix, newline */ + + 2 /* don't forward to syslog */ + + 2 /* don't forward to kmsg */ + + 2 /* don't forward to console */); + memcpy (header, identifier, l); + header[l++] = '\n'; + header[l++] = '\n'; /* empty unit ID */ + header[l++] = '0' + priority; + header[l++] = '\n'; + header[l++] = '0' + !!level_prefix; + header[l++] = '\n'; + header[l++] = '0'; /* don't forward to syslog */ + header[l++] = '\n'; + header[l++] = '0'; /* don't forward to kmsg */ + header[l++] = '\n'; + header[l++] = '0'; /* don't forward to console */ + header[l++] = '\n'; + + if (write_all (fd, header, l) < 0) + goto fail; + + return fd; + +fail: + saved_errno = errno; + + if (fd >= 0) + close (fd); + + errno = saved_errno; + return -1; +} + +static void +set_up_journal (const char *argv1) +{ + const char *identifier; + const char *slash; + int fd; + + if (!_g_fd_is_journal (STDOUT_FILENO) && !_g_fd_is_journal (STDERR_FILENO)) + return; + + identifier = getenv ("GIO_LAUNCHED_DESKTOP_FILE"); + + if (identifier == NULL) + identifier = argv1; + + slash = strrchr (identifier, '/'); + + if (slash != NULL && slash[1] != '\0') + identifier = slash + 1; + + fd = journal_stream_fd (identifier, LOG_INFO, 0); + + /* Silently ignore failure to open the Journal */ + if (fd < 0) + return; + + if (dup2 (fd, STDOUT_FILENO) != STDOUT_FILENO) + fprintf (stderr, + "gio-launch-desktop[%d]: Unable to redirect \"%s\" to Journal: %s", + getpid (), + identifier, + strerror (errno)); + + if (dup2 (fd, STDERR_FILENO) != STDERR_FILENO) + fprintf (stderr, + "gio-launch-desktop[%d]: Unable to redirect \"%s\" to Journal: %s", + getpid (), + identifier, + strerror (errno)); + + close (fd); +} + +#endif + +int +main (int argc, char *argv[]) +{ + pid_t pid = getpid (); + char buf[50]; + int r; + + if (argc < 2) + return -1; + + r = snprintf (buf, sizeof (buf), "GIO_LAUNCHED_DESKTOP_FILE_PID=%ld", (long) pid); + if (r < 0 || (size_t) r >= sizeof (buf)) + return -1; + + putenv (buf); + +#if defined(__linux__) && !defined(__BIONIC__) + set_up_journal (argv[1]); +#endif + + return execvp (argv[1], argv + 1); +} diff --git a/gio/meson.build b/gio/meson.build index e64135419..69bb0603e 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -1,6 +1,7 @@ gio_c_args = [ '-DG_LOG_DOMAIN="GLib-GIO"', '-DGIO_COMPILATION', + '-DGIO_LAUNCH_DESKTOP="@0@"'.format(glib_prefix / multiarch_libexecdir / 'gio-launch-desktop'), '-DGIO_MODULE_DIR="@0@"'.format(glib_giomodulesdir), '-DLOCALSTATEDIR="@0@"'.format(glib_localstatedir), ] @@ -399,6 +400,19 @@ if host_system != 'windows' contenttype_sources += files('gcontenttype.c') appinfo_sources += files('gdesktopappinfo.c') gio_unix_include_headers += files('gdesktopappinfo.h') + launch_desktop_sources = files('gio-launch-desktop.c') + + if host_system == 'linux' + launch_desktop_sources += files('../glib/gjournal-private.c') + endif + + gio_launch_desktop = executable('gio-launch-desktop', launch_desktop_sources, + include_directories : glibinc, + install : true, + install_dir : multiarch_libexecdir, + c_args : gio_c_args, + # intl.lib is not compatible with SAFESEH + link_args : noseh_link_args) endif subdir('xdgmime') diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 282b911c7..c3e4e4cb3 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -149,6 +149,10 @@ test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) test_env.set('GIO_MODULE_DIR', '') +if host_system != 'windows' and not glib_have_cocoa + test_env.set('GIO_LAUNCH_DESKTOP', gio_launch_desktop.full_path()) +endif + # Check for libdbus1 - Optional - is only used in the GDBus test cases # 1.2.14 required for dbus_message_set_serial dbus1_dep = dependency('dbus-1', required : false, version : '>= 1.2.14') diff --git a/glib/gjournal-private.c b/glib/gjournal-private.c new file mode 100644 index 000000000..f45e2cf1c --- /dev/null +++ b/glib/gjournal-private.c @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Red Hat, Inc. + * Copyright 2016-2022 Collabora Ltd. + * Copyright 2017-2022 Endless OS Foundation, LLC + * Copyright 2018 Will Thompson + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "gjournal-private.h" + +#if defined(__linux__) && !defined(__BIONIC__) +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> + +/* + * Reimplementation of g_str_has_prefix(), necessary when compiled into + * gio-launch-desktop. + */ +static int +str_has_prefix (const char *str, + const char *prefix) +{ + return strncmp (str, prefix, strlen (prefix)) == 0; +} + +/* + * _g_fd_is_journal: + * @output_fd: output file descriptor to check + * + * Same as g_log_writer_is_journald(), but with no GLib dependencies. + * + * Returns: 1 if @output_fd points to the journal, 0 otherwise + */ +int +_g_fd_is_journal (int output_fd) +{ + /* FIXME: Use the new journal API for detecting whether we’re writing to the + * journal. See: https://github.com/systemd/systemd/issues/2473 + */ + union { + struct sockaddr_storage storage; + struct sockaddr sa; + struct sockaddr_un un; + } addr; + socklen_t addr_len; + int err; + + if (output_fd < 0) + return 0; + + /* Namespaced journals start with `/run/systemd/journal.${name}/` (see + * `RuntimeDirectory=systemd/journal.%i` in `systemd-journald@.service`. The + * default journal starts with `/run/systemd/journal/`. */ + memset (&addr, 0, sizeof (addr)); + addr_len = sizeof(addr); + err = getpeername (output_fd, &addr.sa, &addr_len); + if (err == 0 && addr.storage.ss_family == AF_UNIX) + return (str_has_prefix (addr.un.sun_path, "/run/systemd/journal/") || + str_has_prefix (addr.un.sun_path, "/run/systemd/journal.")); + + return 0; +} +#endif diff --git a/glib/gjournal-private.h b/glib/gjournal-private.h new file mode 100644 index 000000000..46e0e5a85 --- /dev/null +++ b/glib/gjournal-private.h @@ -0,0 +1,25 @@ +/* + * Copyright 2016-2022 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GLIB_JOURNAL_PRIVATE_H__ +#define __GLIB_JOURNAL_PRIVATE_H__ + +int _g_fd_is_journal (int output_fd); + +#endif diff --git a/glib/gmessages.c b/glib/gmessages.c index 97c5b31b1..bbea25375 100644 --- a/glib/gmessages.c +++ b/glib/gmessages.c @@ -202,6 +202,10 @@ #include "gpattern.h" #include "gthreadprivate.h" +#if defined(__linux__) && !defined(__BIONIC__) +#include "gjournal-private.h" +#endif + #ifdef G_OS_UNIX #include <unistd.h> #endif @@ -2223,32 +2227,10 @@ gboolean g_log_writer_is_journald (gint output_fd) { #if defined(__linux__) && !defined(__BIONIC__) - /* FIXME: Use the new journal API for detecting whether we’re writing to the - * journal. See: https://github.com/systemd/systemd/issues/2473 - */ - union { - struct sockaddr_storage storage; - struct sockaddr sa; - struct sockaddr_un un; - } addr; - socklen_t addr_len; - int err; - - if (output_fd < 0) - return FALSE; - - /* Namespaced journals start with `/run/systemd/journal.${name}/` (see - * `RuntimeDirectory=systemd/journal.%i` in `systemd-journald@.service`. The - * default journal starts with `/run/systemd/journal/`. */ - memset (&addr, 0, sizeof (addr)); - addr_len = sizeof(addr); - err = getpeername (output_fd, &addr.sa, &addr_len); - if (err == 0 && addr.storage.ss_family == AF_UNIX) - return (g_str_has_prefix (addr.un.sun_path, "/run/systemd/journal/") || - g_str_has_prefix (addr.un.sun_path, "/run/systemd/journal.")); -#endif - + return _g_fd_is_journal (output_fd); +#else return FALSE; +#endif } static void escape_string (GString *string); diff --git a/glib/meson.build b/glib/meson.build index 6062c11a1..bcfcba94f 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -336,6 +336,10 @@ else platform_deps = [] endif +if host_system == 'linux' + glib_sources += files('gjournal-private.c') +endif + if glib_have_cocoa glib_sources += files('gosxutils.m') framework_dep = dependency('appleframeworks', modules : ['Foundation', 'CoreFoundation', 'AppKit']) diff --git a/meson.build b/meson.build index 3f28a71ee..1d1122627 100644 --- a/meson.build +++ b/meson.build @@ -88,11 +88,13 @@ if get_option('multiarch') # to an architecture-dependent location, with a compatibility symlink # in the PATH. multiarch_bindir = get_option('libdir') / 'glib-2.0' + multiarch_libexecdir = multiarch_bindir pkgconfig_multiarch_bindir = '${libdir}/glib-2.0' else # For single-architecture distributions, just install them into the PATH # as was traditionally done. multiarch_bindir = get_option('bindir') + multiarch_libexecdir = get_option('libexecdir') pkgconfig_multiarch_bindir = '${bindir}' endif |