summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2022-07-24 23:31:27 +0000
committerPhilip Withnall <philip@tecnocode.co.uk>2022-07-24 23:31:27 +0000
commit30c840a7556f41e0f21751e91294f711304cd947 (patch)
tree091faeef7152e5580356dfa5d08837a833319d95
parent9c1b2399ec95ff02aa6755dabf384507c27f508c (diff)
parentf73641482586bd3e49b73067569aa79ecd56aa5d (diff)
downloadglib-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.c53
-rw-r--r--gio/gio-launch-desktop.c253
-rw-r--r--gio/meson.build14
-rw-r--r--gio/tests/meson.build4
-rw-r--r--glib/gjournal-private.c78
-rw-r--r--glib/gjournal-private.h25
-rw-r--r--glib/gmessages.c32
-rw-r--r--glib/meson.build4
-rw-r--r--meson.build2
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