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