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 31int 32systemd_activated(void) 33{ 34 return (sd_listen_fds(0) >= 1); 35} 36 37int 38systemd_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 65fail: 66 if (cause != NULL) 67 xasprintf(cause, "systemd socket error (%s)", strerror(errno)); 68 return (-1); 69} 70 71int 72systemd_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 208finish: 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