Merge branch 'obsd-master'
[tmux.git] / compat / systemd.c
blob22773c421e26358171f9e33e98255f0f5eb34267
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2022 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/un.h>
22 #include <systemd/sd-bus.h>
23 #include <systemd/sd-daemon.h>
24 #include <systemd/sd-login.h>
25 #include <systemd/sd-id128.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
31 #include "tmux.h"
33 #ifndef SD_ID128_UUID_FORMAT_STR
34 #define SD_ID128_UUID_FORMAT_STR \
35 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
36 #endif
38 int
39 systemd_activated(void)
41 return (sd_listen_fds(0) >= 1);
44 int
45 systemd_create_socket(int flags, char **cause)
47 int fds;
48 int fd;
49 struct sockaddr_un sa;
50 socklen_t addrlen = sizeof sa;
52 fds = sd_listen_fds(0);
53 if (fds > 1) { /* too many file descriptors */
54 errno = E2BIG;
55 goto fail;
58 if (fds == 1) { /* socket-activated */
59 fd = SD_LISTEN_FDS_START;
60 if (!sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0)) {
61 errno = EPFNOSUPPORT;
62 goto fail;
64 if (getsockname(fd, (struct sockaddr *)&sa, &addrlen) == -1)
65 goto fail;
66 socket_path = xstrdup(sa.sun_path);
67 return (fd);
70 return (server_create_socket(flags, cause));
72 fail:
73 if (cause != NULL)
74 xasprintf(cause, "systemd socket error (%s)", strerror(errno));
75 return (-1);
78 int
79 systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
81 sd_bus_error error = SD_BUS_ERROR_NULL;
82 sd_bus_message *m = NULL, *reply = NULL;
83 sd_bus *bus = NULL;
84 char *name, *desc, *slice;
85 sd_id128_t uuid;
86 int r;
87 pid_t parent_pid;
89 /* Connect to the session bus. */
90 r = sd_bus_default_user(&bus);
91 if (r < 0) {
92 xasprintf(cause, "failed to connect to session bus: %s",
93 strerror(-r));
94 goto finish;
97 /* Start building the method call. */
98 r = sd_bus_message_new_method_call(bus, &m,
99 "org.freedesktop.systemd1",
100 "/org/freedesktop/systemd1",
101 "org.freedesktop.systemd1.Manager",
102 "StartTransientUnit");
103 if (r < 0) {
104 xasprintf(cause, "failed to create bus message: %s",
105 strerror(-r));
106 goto finish;
109 /* Generate a unique name for the new scope, to avoid collisions. */
110 r = sd_id128_randomize(&uuid);
111 if (r < 0) {
112 xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
113 goto finish;
115 xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
116 SD_ID128_FORMAT_VAL(uuid));
117 r = sd_bus_message_append(m, "s", name);
118 free(name);
119 if (r < 0) {
120 xasprintf(cause, "failed to append to bus message: %s",
121 strerror(-r));
122 goto finish;
125 /* Mode: fail if there's a queued unit with the same name. */
126 r = sd_bus_message_append(m, "s", "fail");
127 if (r < 0) {
128 xasprintf(cause, "failed to append to bus message: %s",
129 strerror(-r));
130 goto finish;
133 /* Start properties array. */
134 r = sd_bus_message_open_container(m, 'a', "(sv)");
135 if (r < 0) {
136 xasprintf(cause, "failed to start properties array: %s",
137 strerror(-r));
138 goto finish;
141 parent_pid = getpid();
142 xasprintf(&desc, "tmux child pane %ld launched by process %ld",
143 (long)pid, (long)parent_pid);
144 r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
145 free(desc);
146 if (r < 0) {
147 xasprintf(cause, "failed to append to properties: %s",
148 strerror(-r));
149 goto finish;
153 * Make sure that the session shells are terminated with SIGHUP since
154 * bash and friends tend to ignore SIGTERM.
156 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", 1);
157 if (r < 0) {
158 xasprintf(cause, "failed to append to properties: %s",
159 strerror(-r));
160 goto finish;
164 * Inherit the slice from the parent process, or default to
165 * "app-tmux.slice" if that fails.
167 r = sd_pid_get_user_slice(parent_pid, &slice);
168 if (r < 0) {
169 slice = xstrdup("app-tmux.slice");
171 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
172 free(slice);
173 if (r < 0) {
174 xasprintf(cause, "failed to append to properties: %s",
175 strerror(-r));
176 goto finish;
179 /* PIDs to add to the scope: length - 1 array of uint32_t. */
180 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
181 if (r < 0) {
182 xasprintf(cause, "failed to append to properties: %s",
183 strerror(-r));
184 goto finish;
187 /* Clean up the scope even if it fails. */
188 r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
189 "inactive-or-failed");
190 if (r < 0) {
191 xasprintf(cause, "failed to append to properties: %s",
192 strerror(-r));
193 goto finish;
196 /* End properties array. */
197 r = sd_bus_message_close_container(m);
198 if (r < 0) {
199 xasprintf(cause, "failed to end properties array: %s",
200 strerror(-r));
201 goto finish;
204 /* aux is currently unused and should be passed an empty array. */
205 r = sd_bus_message_append(m, "a(sa(sv))", 0);
206 if (r < 0) {
207 xasprintf(cause, "failed to append to bus message: %s",
208 strerror(-r));
209 goto finish;
212 /* Call the method with a timeout of 1 second = 1e6 us. */
213 r = sd_bus_call(bus, m, 1000000, &error, &reply);
214 if (r < 0) {
215 if (error.message != NULL) {
216 /* We have a specific error message from sd-bus. */
217 xasprintf(cause, "StartTransientUnit call failed: %s",
218 error.message);
219 } else {
220 xasprintf(cause, "StartTransientUnit call failed: %s",
221 strerror(-r));
223 goto finish;
226 finish:
227 sd_bus_error_free(&error);
228 sd_bus_message_unref(m);
229 sd_bus_message_unref(reply);
230 sd_bus_unref(bus);
232 return (r);