glib/gio/gio-launch-desktop.c
Simon McVittie 763643ceaa gio-launch-desktop: Redirect stdout, stderr to systemd Journal
This prevents a launched process's output from being mixed up with the
output of the parent process, which can lead to the wrong program being
blamed for warning messages.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2022-07-25 01:00:54 +02:00

249 lines
6.1 KiB
C

/* 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>
/*
* 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;
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);
}