xref: /netbsd-src/external/bsd/tmux/dist/compat/systemd.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2022 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
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.
9  *
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.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/un.h>
21 
22 #include <systemd/sd-bus.h>
23 #include <systemd/sd-daemon.h>
24 #include <systemd/sd-login.h>
25 #include <systemd/sd-id128.h>
26 
27 #include <string.h>
28 
29 #include "tmux.h"
30 
31 int
32 systemd_activated(void)
33 {
34 	return (sd_listen_fds(0) >= 1);
35 }
36 
37 int
38 systemd_create_socket(int flags, char **cause)
39 {
40 	int			fds;
41 	int			fd;
42 	struct sockaddr_un	sa;
43 	socklen_t		addrlen = sizeof sa;
44 
45 	fds = sd_listen_fds(0);
46 	if (fds > 1) { /* too many file descriptors */
47 		errno = E2BIG;
48 		goto fail;
49 	}
50 
51 	if (fds == 1) { /* socket-activated */
52 		fd = SD_LISTEN_FDS_START;
53 		if (!sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0)) {
54 			errno = EPFNOSUPPORT;
55 			goto fail;
56 		}
57 		if (getsockname(fd, (struct sockaddr *)&sa, &addrlen) == -1)
58 			goto fail;
59 		socket_path = xstrdup(sa.sun_path);
60 		return (fd);
61 	}
62 
63 	return (server_create_socket(flags, cause));
64 
65 fail:
66 	if (cause != NULL)
67 		xasprintf(cause, "systemd socket error (%s)", strerror(errno));
68 	return (-1);
69 }
70 
71 int
72 systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
73 {
74 	sd_bus_error	 error = SD_BUS_ERROR_NULL;
75 	sd_bus_message	*m = NULL, *reply = NULL;
76 	sd_bus 		*bus = NULL;
77 	char		*name, *desc, *slice;
78 	sd_id128_t	 uuid;
79 	int		 r;
80 	pid_t		 parent_pid;
81 
82 	/* Connect to the session bus. */
83 	r = sd_bus_default_user(&bus);
84 	if (r < 0) {
85 		xasprintf(cause, "failed to connect to session bus: %s",
86 		    strerror(-r));
87 		goto finish;
88 	}
89 
90 	/* Start building the method call. */
91 	r = sd_bus_message_new_method_call(bus, &m,
92 	    "org.freedesktop.systemd1",
93 	    "/org/freedesktop/systemd1",
94 	    "org.freedesktop.systemd1.Manager",
95 	    "StartTransientUnit");
96 	if (r < 0) {
97 		xasprintf(cause, "failed to create bus message: %s",
98 		    strerror(-r));
99 		goto finish;
100 	}
101 
102 	/* Generate a unique name for the new scope, to avoid collisions. */
103 	r = sd_id128_randomize(&uuid);
104 	if (r < 0) {
105 		xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
106 		goto finish;
107 	}
108 	xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
109 	    SD_ID128_FORMAT_VAL(uuid));
110 	r = sd_bus_message_append(m, "s", name);
111 	free(name);
112 	if (r < 0) {
113 		xasprintf(cause, "failed to append to bus message: %s",
114 		    strerror(-r));
115 		goto finish;
116 	}
117 
118 	/* Mode: fail if there's a queued unit with the same name. */
119 	r = sd_bus_message_append(m, "s", "fail");
120 	if (r < 0) {
121 		xasprintf(cause, "failed to append to bus message: %s",
122 		    strerror(-r));
123 		goto finish;
124 	}
125 
126 	/* Start properties array. */
127 	r = sd_bus_message_open_container(m, 'a', "(sv)");
128 	if (r < 0) {
129 		xasprintf(cause, "failed to start properties array: %s",
130 		    strerror(-r));
131 		goto finish;
132 	}
133 
134 	parent_pid = getpid();
135 	xasprintf(&desc, "tmux child pane %ld launched by process %ld",
136 	    (long)pid, (long)parent_pid);
137 	r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
138 	free(desc);
139 	if (r < 0) {
140 		xasprintf(cause, "failed to append to properties: %s",
141 		    strerror(-r));
142 		goto finish;
143 	}
144 
145 	/*
146 	 * Inherit the slice from the parent process, or default to
147 	 * "app-tmux.slice" if that fails.
148 	 */
149 	r = sd_pid_get_user_slice(parent_pid, &slice);
150 	if (r < 0) {
151 		slice = xstrdup("app-tmux.slice");
152 	}
153 	r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
154 	free(slice);
155 	if (r < 0) {
156 		xasprintf(cause, "failed to append to properties: %s",
157 		    strerror(-r));
158 		goto finish;
159 	}
160 
161 	/* PIDs to add to the scope: length - 1 array of uint32_t. */
162 	r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
163 	if (r < 0) {
164 		xasprintf(cause, "failed to append to properties: %s",
165 		    strerror(-r));
166 		goto finish;
167 	}
168 
169 	/* Clean up the scope even if it fails. */
170 	r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
171 	    "inactive-or-failed");
172 	if (r < 0) {
173 		xasprintf(cause, "failed to append to properties: %s",
174 		    strerror(-r));
175 		goto finish;
176 	}
177 
178 	/* End properties array. */
179 	r = sd_bus_message_close_container(m);
180 	if (r < 0) {
181 		xasprintf(cause, "failed to end properties array: %s",
182 		    strerror(-r));
183 		goto finish;
184 	}
185 
186 	/* aux is currently unused and should be passed an empty array. */
187 	r = sd_bus_message_append(m, "a(sa(sv))", 0);
188 	if (r < 0) {
189 		xasprintf(cause, "failed to append to bus message: %s",
190 		    strerror(-r));
191 		goto finish;
192 	}
193 
194 	/* Call the method with a timeout of 1 second = 1e6 us. */
195 	r = sd_bus_call(bus, m, 1000000, &error, &reply);
196 	if (r < 0) {
197 		if (error.message != NULL) {
198 			/* We have a specific error message from sd-bus. */
199 			xasprintf(cause, "StartTransientUnit call failed: %s",
200 			    error.message);
201 		} else {
202 			xasprintf(cause, "StartTransientUnit call failed: %s",
203 			    strerror(-r));
204 		}
205 		goto finish;
206 	}
207 
208 finish:
209 	sd_bus_error_free(&error);
210 	sd_bus_message_unref(m);
211 	sd_bus_message_unref(reply);
212 	sd_bus_unref(bus);
213 
214 	return (r);
215 }
216