1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright 2004 Ximian Inc.
4 * Copyright 2011-2022 systemd contributors
5 * Copyright (C) 2018 Endless Mobile, Inc.
6 * Copyright 2022 Collabora Ltd.
7 *
8 * SPDX-License-Identifier: LGPL-2.1-or-later
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General
21 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 *
23 * Author: Daniel Drake <drake@endlessm.com>
24 */
25
26 /*
27 * gio-launch-desktop: GDesktopAppInfo helper
28 * Executable wrapper to set GIO_LAUNCHED_DESKTOP_FILE_PID
29 * There are complications when doing this in a fork()/exec() codepath,
30 * and it cannot otherwise be done with posix_spawn().
31 * This wrapper is designed to be minimal and lightweight.
32 * It does not even link against glib.
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39
40 #if defined(__linux__) && !defined(__BIONIC__)
41 #include <alloca.h>
42 #include <errno.h>
43 #include <stddef.h>
44 #include <string.h>
45 #include <syslog.h>
46 #include <sys/socket.h>
47 #include <sys/un.h>
48
49 #include "gjournal-private.h"
50 #define GLIB_COMPILATION
51 #include "gmacros.h" /* For G_STATIC_ASSERT define */
52 #undef GLIB_COMPILATION
53
54 /*
55 * write_all:
56 * @fd: a file descriptor
57 * @vbuf: a buffer
58 * @to_write: length of @vbuf
59 *
60 * Write all bytes from @vbuf to @fd, blocking if necessary.
61 *
62 * Returns: 0 on success, -1 with errno set on failure
63 */
64 static int
65 write_all (int fd, const void *vbuf, size_t to_write)
66 {
67 const char *buf = vbuf;
68
69 while (to_write > 0)
70 {
71 ssize_t count = write (fd, buf, to_write);
72 if (count < 0)
73 {
74 if (errno != EINTR)
75 return -1;
76 }
77 else
78 {
79 to_write -= count;
80 buf += count;
81 }
82 }
83
84 return 0;
85 }
86
87 /*
88 * journal_stream_fd:
89 * @identifier: identifier (syslog tag) for logged messages
90 * @priority: a priority between `LOG_EMERG` and `LOG_DEBUG` inclusive
91 * @level_prefix: if nonzero, journald will interpret prefixes like <0>
92 * as specifying the priority for a line
93 *
94 * Reimplementation of sd_journal_stream_fd(), to avoid having to link
95 * gio-launch-desktop to libsystemd.
96 *
97 * Note that unlike the libsystemd version, this reports errors by returning
98 * -1 with errno set.
99 *
100 * Returns: a non-negative fd number, or -1 with errno set on error
101 */
102 static int
103 journal_stream_fd (const char *identifier,
104 int priority,
105 int level_prefix)
106 {
107 union
108 {
109 struct sockaddr sa;
110 struct sockaddr_un un;
111 } sa =
112 {
113 .un.sun_family = AF_UNIX,
114 .un.sun_path = "/run/systemd/journal/stdout",
115 };
116 socklen_t salen;
117 char *header;
118 int fd;
119 size_t l;
120 int saved_errno;
121 /* Arbitrary large size for the sending buffer, from systemd */
122 int large_buffer_size = 8 * 1024 * 1024;
123
124 G_STATIC_ASSERT (LOG_EMERG == 0 && sizeof "Linux ABI defines LOG_EMERG");
125 G_STATIC_ASSERT (LOG_DEBUG == 7 && sizeof "Linux ABI defines LOG_DEBUG");
126
127 fd = socket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
128
129 if (fd < 0)
130 goto fail;
131
132 salen = offsetof (struct sockaddr_un, sun_path) + strlen (sa.un.sun_path) + 1;
133
134 if (connect (fd, &sa.sa, salen) < 0)
135 goto fail;
136
137 if (shutdown (fd, SHUT_RD) < 0)
138 goto fail;
139
140 (void) setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &large_buffer_size,
141 (socklen_t) sizeof (large_buffer_size));
142
143 if (identifier == NULL)
144 identifier = "";
145
146 if (priority < 0)
147 priority = 0;
148
149 if (priority > 7)
150 priority = 7;
151
152 l = strlen (identifier);
153 header = alloca (l + 1 /* identifier, newline */
154 + 1 /* empty unit ID, newline */
155 + 2 /* priority, newline */
156 + 2 /* level prefix, newline */
157 + 2 /* don't forward to syslog */
158 + 2 /* don't forward to kmsg */
159 + 2 /* don't forward to console */);
160 memcpy (header, identifier, l);
161 header[l++] = '\n';
162 header[l++] = '\n'; /* empty unit ID */
163 header[l++] = '0' + priority;
164 header[l++] = '\n';
165 header[l++] = '0' + !!level_prefix;
166 header[l++] = '\n';
167 header[l++] = '0'; /* don't forward to syslog */
168 header[l++] = '\n';
169 header[l++] = '0'; /* don't forward to kmsg */
170 header[l++] = '\n';
171 header[l++] = '0'; /* don't forward to console */
172 header[l++] = '\n';
173
174 if (write_all (fd, header, l) < 0)
175 goto fail;
176
177 return fd;
178
179 fail:
180 saved_errno = errno;
181
182 if (fd >= 0)
183 close (fd);
184
185 errno = saved_errno;
186 return -1;
187 }
188
189 static void
190 set_up_journal (const char *argv1)
191 {
192 int stdout_is_journal;
193 int stderr_is_journal;
194 const char *identifier;
195 const char *slash;
196 int fd;
197
198 stdout_is_journal = _g_fd_is_journal (STDOUT_FILENO);
199 stderr_is_journal = _g_fd_is_journal (STDERR_FILENO);
200
201 if (!stdout_is_journal && !stderr_is_journal)
202 return;
203
204 identifier = getenv ("GIO_LAUNCHED_DESKTOP_FILE");
205
206 if (identifier == NULL)
207 identifier = argv1;
208
209 slash = strrchr (identifier, '/');
210
211 if (slash != NULL && slash[1] != '\0')
212 identifier = slash + 1;
213
214 fd = journal_stream_fd (identifier, LOG_INFO, 0);
215
216 /* Silently ignore failure to open the Journal */
217 if (fd < 0)
218 return;
219
220 if (stdout_is_journal && dup2 (fd, STDOUT_FILENO) != STDOUT_FILENO)
221 fprintf (stderr,
222 "gio-launch-desktop[%d]: Unable to redirect \"%s\" to Journal: %s",
223 getpid (),
224 identifier,
225 strerror (errno));
226
227 if (stderr_is_journal && dup2 (fd, STDERR_FILENO) != STDERR_FILENO)
228 fprintf (stderr,
229 "gio-launch-desktop[%d]: Unable to redirect \"%s\" to Journal: %s",
230 getpid (),
231 identifier,
232 strerror (errno));
233
234 close (fd);
235 }
236
237 #endif
238
239 int
240 main (int argc, char *argv[])
241 {
242 pid_t pid = getpid ();
243 char buf[50];
244 int r;
245
246 if (argc < 2)
247 return -1;
248
249 r = snprintf (buf, sizeof (buf), "GIO_LAUNCHED_DESKTOP_FILE_PID=%ld", (long) pid);
250 if (r < 0 || (size_t) r >= sizeof (buf))
251 return -1;
252
253 putenv (buf);
254
255 #if defined(__linux__) && !defined(__BIONIC__)
256 set_up_journal (argv[1]);
257 #endif
258
259 return execvp (argv[1], argv + 1);
260 }