this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Temporarily revert 9980fa0a1090ee03f74cc7d62a0fc181def7691c

Turns out that it causes regressions for some users (see #1443). I'm going to move this change into a branch for testing and debugging and merge it back in once it's working reliably for everyone.

This change isn't too important for most users of Darling; at the moment, the only significant thing it does is allow xcodebuild to work, but that's not essential at the moment.

+11 -505
+2 -4
src/shellspawn/CMakeLists.txt
··· 3 3 add_definitions(-nostdinc) 4 4 5 5 add_darling_executable(shellspawn shellspawn.c duct_signals.c) 6 - add_darling_executable(shellsession shellsession.c) 7 6 8 - install(TARGETS shellspawn shellsession DESTINATION libexec/darling/usr/libexec) 9 - install(FILES org.darlinghq.shellsession.plist DESTINATION libexec/darling/System/Library/LaunchDaemons) 10 - install(FILES org.darlinghq.shellspawn.plist DESTINATION libexec/darling/System/Library/LaunchAgents) 7 + install(TARGETS shellspawn DESTINATION libexec/darling/usr/libexec) 8 + install(FILES org.darlinghq.shellspawn.plist DESTINATION libexec/darling/System/Library/LaunchDaemons) 11 9
-17
src/shellspawn/org.darlinghq.shellsession.plist
··· 1 - <?xml version="1.0" encoding="UTF-8"?> 2 - <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 3 - "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 4 - <plist version="1.0"> 5 - <dict> 6 - <key>Label</key> 7 - <string>org.darlinghq.shellsession</string> 8 - <key>ProgramArguments</key> 9 - <array> 10 - <string>/usr/libexec/shellsession</string> 11 - </array> 12 - <key>RunAtLoad</key> 13 - <true/> 14 - <key>KeepAlive</key> 15 - <true/> 16 - </dict> 17 - </plist>
-373
src/shellspawn/shellsession.c
··· 1 - /* 2 - * This file is part of Darling. 3 - * 4 - * Copyright (C) 2023 Darling Developers 5 - * 6 - * Darling is free software: you can redistribute it and/or modify 7 - * it under the terms of the GNU General Public License as published by 8 - * the Free Software Foundation, either version 3 of the License, or 9 - * (at your option) any later version. 10 - * 11 - * Darling is distributed in the hope that it will be useful, 12 - * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 - * GNU General Public License for more details. 15 - * 16 - * You should have received a copy of the GNU General Public License 17 - * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 - */ 19 - 20 - #include <stdlib.h> 21 - #include <string.h> 22 - #include <stdio.h> 23 - #include <stdbool.h> 24 - 25 - #include <unistd.h> 26 - #include <sys/socket.h> 27 - #include <sys/un.h> 28 - #include <sys/fcntl.h> 29 - #include <sys/stat.h> 30 - #include <sys/syslimits.h> 31 - #include <errno.h> 32 - #include <pwd.h> 33 - 34 - #include <liblaunch/launch_priv.h> 35 - #include <bootstrap_priv.h> 36 - #include <vproc_priv.h> 37 - 38 - #include "shellsession.h" 39 - #include "shellspawn.h" 40 - 41 - #define MAX_ACCESS_TRIES 10 42 - 43 - static void setup_socket_addr(struct sockaddr_un* addr); 44 - static void setup_session_socket_addr(unsigned int uid, struct sockaddr_un* addr); 45 - static int setup_socket(const struct sockaddr_un* addr); 46 - static void listen_for_connections(int server_socket); 47 - static void spawn_session(int sockfd); 48 - static void send_client_socket(int sockfd, int socket_to_send); 49 - static int try_connect_session(const struct sockaddr_un* addr); 50 - static void run_session_manager(unsigned int uid, unsigned int gid); 51 - 52 - int main(int argc, const char* const* argv) { 53 - struct sockaddr_un server_addr; 54 - int server_socket = -1; 55 - 56 - setup_socket_addr(&server_addr); 57 - 58 - server_socket = setup_socket(&server_addr); 59 - if (server_socket < 0) { 60 - exit(EXIT_FAILURE); 61 - } 62 - 63 - listen_for_connections(server_socket); 64 - 65 - if (server_socket >= 0) { 66 - close(server_socket); 67 - } 68 - 69 - return 0; 70 - }; 71 - 72 - static void setup_socket_addr(struct sockaddr_un* addr) { 73 - memset(addr, 0, sizeof(*addr)); 74 - addr->sun_family = AF_UNIX; 75 - snprintf(addr->sun_path, sizeof(addr->sun_path), "%s", SHELLSESSION_SOCKPATH); 76 - }; 77 - 78 - static void setup_session_socket_addr(unsigned int uid, struct sockaddr_un* addr) { 79 - memset(addr, 0, sizeof(*addr)); 80 - addr->sun_family = AF_UNIX; 81 - snprintf(addr->sun_path, sizeof(addr->sun_path), "%s.%d", SHELLSPAWN_SOCKPATH, uid); 82 - }; 83 - 84 - static int setup_socket(const struct sockaddr_un* addr) { 85 - int server_socket = -1; 86 - 87 - server_socket = socket(AF_UNIX, SOCK_STREAM, 0); 88 - if (server_socket == -1) { 89 - perror("Creating unix socket"); 90 - goto err; 91 - } 92 - 93 - fcntl(server_socket, F_SETFD, FD_CLOEXEC); 94 - unlink(addr->sun_path); 95 - 96 - if (bind(server_socket, (struct sockaddr*)addr, sizeof(*addr)) == -1) { 97 - perror("Binding the unix socket"); 98 - goto err; 99 - } 100 - 101 - chmod(addr->sun_path, S_IRUSR | S_IWUSR); 102 - 103 - if (listen(server_socket, 1) == -1) { 104 - perror("Listening on unix socket"); 105 - goto err; 106 - } 107 - 108 - return server_socket; 109 - 110 - err: 111 - if (server_socket >= 0) { 112 - close(server_socket); 113 - } 114 - return -1; 115 - }; 116 - 117 - static void listen_for_connections(int server_socket) { 118 - int sock; 119 - struct sockaddr_un client_addr; 120 - socklen_t len = sizeof(client_addr); 121 - 122 - while (true) { 123 - sock = accept(server_socket, (struct sockaddr*)&client_addr, &len); 124 - if (sock == -1) { 125 - break; 126 - } 127 - 128 - if (fork() == 0) { 129 - // we don't need the server socket 130 - close(server_socket); 131 - server_socket = -1; 132 - 133 - fcntl(sock, F_SETFD, FD_CLOEXEC); 134 - spawn_session(sock); 135 - 136 - exit(EXIT_SUCCESS); 137 - } else { 138 - close(sock); 139 - } 140 - } 141 - }; 142 - 143 - void spawn_session(int sockfd) { 144 - shellsession_cmd_t cmd; 145 - struct sockaddr_un session_addr; 146 - int client_socket = -1; 147 - char session_started_path[PATH_MAX]; 148 - int started_fd = -1; 149 - 150 - if (read(sockfd, &cmd, sizeof(cmd)) != sizeof(cmd)) { 151 - goto err; 152 - } 153 - 154 - setup_session_socket_addr(cmd.uid, &session_addr); 155 - snprintf(session_started_path, sizeof(session_started_path), "%s.%d", SHELLSESSION_STARTED_PATH, cmd.uid); 156 - 157 - started_fd = open(session_started_path, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 158 - 159 - if (started_fd < 0 && errno == EEXIST) { 160 - // we already had a session set up; avoid setting up a new one. 161 - 162 - // wait until shellspawn starts 163 - for (int i = 0; i < MAX_ACCESS_TRIES; i++) { 164 - if (access(session_addr.sun_path, F_OK) == 0) { 165 - break; 166 - } 167 - 168 - sleep(1); 169 - } 170 - 171 - client_socket = try_connect_session(&session_addr); 172 - if (client_socket < 0) { 173 - goto err; 174 - } 175 - 176 - // now reply to the client with the session client socket 177 - send_client_socket(sockfd, client_socket); 178 - close(sockfd); 179 - return; 180 - } else if (started_fd < 0) { 181 - perror("Failed to create session indicator file"); 182 - goto err; 183 - } 184 - 185 - // we didn't have a session set up already, so let's create one. 186 - 187 - // we only need to create the session-running file; we don't need to write anything to it 188 - close(started_fd); 189 - 190 - if (fork() == 0) { 191 - // this is the new session manager process 192 - close(sockfd); 193 - run_session_manager(cmd.uid, cmd.gid); 194 - } else { 195 - // wait until shellspawn starts 196 - for (int i = 0; i < MAX_ACCESS_TRIES; i++) { 197 - if (access(session_addr.sun_path, F_OK) == 0) { 198 - break; 199 - } 200 - 201 - sleep(1); 202 - } 203 - 204 - client_socket = try_connect_session(&session_addr); 205 - if (client_socket < 0) { 206 - fprintf(stderr, "Error connecting to shellspawn session\n"); 207 - exit(EXIT_FAILURE); 208 - } 209 - 210 - // now reply to the client with the session client socket 211 - send_client_socket(sockfd, client_socket); 212 - close(sockfd); 213 - } 214 - 215 - return; 216 - 217 - err: 218 - close(sockfd); 219 - }; 220 - 221 - static void send_client_socket(int sockfd, int socket_to_send) { 222 - char dummy = '\0'; 223 - struct msghdr msg; 224 - char cmsgbuf[CMSG_SPACE(sizeof(int))]; 225 - struct cmsghdr *cmptr; 226 - struct iovec iov; 227 - 228 - memset(&msg, 0, sizeof(msg)); 229 - memset(&iov, 0, sizeof(iov)); 230 - 231 - iov.iov_base = &dummy; 232 - iov.iov_len = sizeof(dummy); 233 - 234 - msg.msg_iov = &iov; 235 - msg.msg_iovlen = 1; 236 - msg.msg_control = cmsgbuf; 237 - msg.msg_controllen = sizeof(cmsgbuf); 238 - 239 - cmptr = CMSG_FIRSTHDR(&msg); 240 - cmptr->cmsg_len = CMSG_LEN(sizeof(int)); 241 - cmptr->cmsg_level = SOL_SOCKET; 242 - cmptr->cmsg_type = SCM_RIGHTS; 243 - memcpy(CMSG_DATA(cmptr), &socket_to_send, sizeof(socket_to_send)); 244 - 245 - if (sendmsg(sockfd, &msg, 0) != sizeof(dummy)) { 246 - fprintf(stderr, "Error sending reply: %s\n", strerror(errno)); 247 - exit(EXIT_FAILURE); 248 - } 249 - }; 250 - 251 - static int try_connect_session(const struct sockaddr_un* addr) { 252 - int client_socket = -1; 253 - 254 - client_socket = socket(AF_UNIX, SOCK_STREAM, 0); 255 - if (client_socket < 0) { 256 - goto err; 257 - } 258 - 259 - if (connect(client_socket, (struct sockaddr*)addr, sizeof(*addr)) != 0) { 260 - goto err; 261 - } 262 - 263 - return client_socket; 264 - 265 - err: 266 - if (client_socket >= 0) { 267 - close(client_socket); 268 - } 269 - return -1; 270 - }; 271 - 272 - static void run_session_manager(unsigned int uid, unsigned int gid) { 273 - const char* login = NULL; 274 - struct passwd* pw = NULL; 275 - char* homedirTmp = NULL; 276 - mach_port_t subset = MACH_PORT_NULL; 277 - mach_port_t dummyService = MACH_PORT_NULL; 278 - kern_return_t kr = KERN_SUCCESS; 279 - 280 - // we are the shellspawn instance for the user's session. this means we're in charge 281 - // of the user's session and are responsible for setting it up as macOS would. 282 - // we are essentially going to be doing the same kind of setup that LoginWindow 283 - // would do on macOS. 284 - 285 - // switch ourselves to run under the user's UID and GID (but under Darling, this is all fake anyways) 286 - setgid(gid); 287 - setuid(uid); 288 - 289 - // fix up env vars to what they should be 290 - pw = getpwuid(uid); 291 - if (pw != NULL) 292 - login = pw->pw_name; 293 - 294 - if (!login) 295 - login = getlogin(); 296 - 297 - setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin", 1); 298 - setenv("TMPDIR", "/private/tmp", 1); 299 - 300 - asprintf(&homedirTmp, "HOME=/Users/%s", login); 301 - putenv(homedirTmp); 302 - homedirTmp = NULL; // `putenv()` assumes ownership of the string 303 - 304 - // 305 - // IMPORTANT NOTE 306 - // 307 - // okay, so i have no clue how this whole session switching is supposed to work. 308 - // or rather, i do have *some* idea: we need to call `create_and_switch_to_per_session_launchd` 309 - // to switch to a per-user launchd (which will also trigger LaunchAgents for the user). 310 - // the problem is that the way this function works internally is that it calls `_vprocmgr_move_subset_to_user`. 311 - // this function, in turn, moves the current subset into the new per-user launchd session. 312 - // obviously, this means we must already have a subset prior to calling `create_and_switch_to_per_session_launchd`. 313 - // 314 - // the thing is, i can't see how (the modern version of) LoginWindow does this. maybe the API has changed since then 315 - // and it no longer moves a subset into the new session, but i can't see it creating a new subset at any point during 316 - // the session setup. but clearly, *we* need to do this, so let's go ahead and do so. 317 - // 318 - 319 - if ((kr = bootstrap_subset(bootstrap_port, mach_task_self(), &subset)) != KERN_SUCCESS) { 320 - fprintf(stderr, "Failed to create bootstrap subset: %d\n", kr); 321 - exit(EXIT_FAILURE); 322 - } 323 - 324 - // replace the bootstrap port with our subset port 325 - if ((kr = task_set_bootstrap_port(mach_task_self(), subset)) != KERN_SUCCESS) { 326 - fprintf(stderr, "Failed to replace bootstrap port: %d\n", kr); 327 - exit(EXIT_FAILURE); 328 - } 329 - 330 - // we no longer need the old bootstrap port 331 - mach_port_deallocate(mach_task_self(), bootstrap_port); 332 - 333 - bootstrap_port = subset; 334 - 335 - // 336 - // ANOTHER IMPORTANT NOTE 337 - // 338 - // launchd requires the new subset to have at least one Mach service registered before switching to the per-user 339 - // session. to that end, we simply create a bogus service here. 340 - // 341 - if ((kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &dummyService)) != KERN_SUCCESS) { 342 - fprintf(stderr, "Failed to allocate dummy service port: %d\n", kr); 343 - exit(EXIT_FAILURE); 344 - } 345 - 346 - if ((kr = mach_port_insert_right(mach_task_self(), dummyService, dummyService, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { 347 - fprintf(stderr, "Failed to insert send right into dummy service port: %d\n", kr); 348 - exit(EXIT_FAILURE); 349 - } 350 - 351 - if ((kr = bootstrap_register(bootstrap_port, "org.darlinghq.shellsession.dummy-service", dummyService)) != KERN_SUCCESS) { 352 - fprintf(stderr, "Failed to register dummy service: %d\n", kr); 353 - exit(EXIT_FAILURE); 354 - } 355 - 356 - // set up a launchd session 357 - if (create_and_switch_to_per_session_launchd("shellspawn", LAUNCH_GLOBAL_ON_DEMAND) < 0) { 358 - fprintf(stderr, "Failed to set up launchd user session.\n"); 359 - exit(EXIT_FAILURE); 360 - } 361 - 362 - // unset the global-on-demand flag to kickstart jobs that need to run at load 363 - if (!_vproc_set_global_on_demand(false)) { 364 - fprintf(stderr, "Failed to unset global-on-demand flag.\n"); 365 - exit(EXIT_FAILURE); 366 - } 367 - 368 - // now we just need to stay alive for launchd to keep our session alive 369 - // (launchd will start shellspawn as a LaunchAgent, and that's what the `darling` command will actually send commands to) 370 - while (true) { 371 - pause(); 372 - } 373 - };
-37
src/shellspawn/shellsession.h
··· 1 - /* 2 - * This file is part of Darling. 3 - * 4 - * Copyright (C) 2023 Darling Developers 5 - * 6 - * Darling is free software: you can redistribute it and/or modify 7 - * it under the terms of the GNU General Public License as published by 8 - * the Free Software Foundation, either version 3 of the License, or 9 - * (at your option) any later version. 10 - * 11 - * Darling is distributed in the hope that it will be useful, 12 - * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 - * GNU General Public License for more details. 15 - * 16 - * You should have received a copy of the GNU General Public License 17 - * along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 - */ 19 - 20 - #ifndef _SHELLSESSION_H_ 21 - #define _SHELLSESSION_H_ 22 - 23 - #ifdef TESTING 24 - #define SHELLSESSION_SOCKPATH "/tmp/shellsession.sock" 25 - #define SHELLSESSION_STARTED_PATH "/tmp/shellsession.running" 26 - #else 27 - #define SHELLSESSION_SOCKPATH "/var/run/shellsession.sock" 28 - #define SHELLSESSION_STARTED_PATH "/var/run/shellsession.running" 29 - #endif 30 - 31 - typedef struct shellsession_cmd { 32 - unsigned int uid; 33 - unsigned int gid; 34 - } shellsession_cmd_t; 35 - 36 - #endif // _SHELLSESSION_H_ 37 -
+2 -3
src/shellspawn/shellspawn.c
··· 64 64 { 65 65 struct sockaddr_un addr = { 66 66 .sun_family = AF_UNIX, 67 + .sun_path = SHELLSPAWN_SOCKPATH 67 68 }; 68 - 69 - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s.%d", SHELLSPAWN_SOCKPATH, getuid()); 70 69 71 70 g_serverSocket = socket(AF_UNIX, SOCK_STREAM, 0); 72 71 if (g_serverSocket == -1) ··· 76 75 } 77 76 78 77 fcntl(g_serverSocket, F_SETFD, FD_CLOEXEC); 79 - unlink(addr.sun_path); 78 + unlink(SHELLSPAWN_SOCKPATH); 80 79 81 80 if (bind(g_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) == -1) 82 81 {
+7 -71
src/startup/darling.c
··· 37 37 #include <pty.h> 38 38 #include <pwd.h> 39 39 #include "../shellspawn/shellspawn.h" 40 - #include "../shellspawn/shellsession.h" 41 40 #include "darling.h" 42 41 #include "darling-config.h" 43 42 ··· 170 169 { 171 170 char socketPath[4096]; 172 171 173 - snprintf(socketPath, sizeof(socketPath), "%s" SHELLSESSION_SOCKPATH, prefix); 172 + snprintf(socketPath, sizeof(socketPath), "%s" SHELLSPAWN_SOCKPATH, prefix); 174 173 175 174 unlink(socketPath); 176 175 ··· 546 545 return len; 547 546 } 548 547 549 - int connectToShellsession(void) 548 + int connectToShellspawn(void) 550 549 { 551 550 struct sockaddr_un addr; 552 551 int sockfd; 553 552 554 - // Connect to the shellsession daemon in the container 553 + // Connect to the shellspawn daemon in the container 555 554 addr.sun_family = AF_UNIX; 556 555 #if USE_LINUX_4_11_HACK 557 556 addr.sun_path[0] = '\0'; 558 557 559 558 strcpy(addr.sun_path, prefix); 560 - strcat(addr.sun_path, SHELLSESSION_SOCKPATH); 559 + strcat(addr.sun_path, SHELLSPAWN_SOCKPATH); 561 560 #else 562 - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSESSION_SOCKPATH, prefix); 561 + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSPAWN_SOCKPATH, prefix); 563 562 #endif 564 563 565 564 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ··· 578 577 return sockfd; 579 578 } 580 579 581 - static int startShellspawnSession(int sockfd) 582 - { 583 - shellsession_cmd_t cmd; 584 - 585 - cmd.uid = g_originalGid; 586 - cmd.gid = g_originalGid; 587 - 588 - if (write(sockfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 589 - { 590 - fprintf(stderr, "Error sending command to shellsession: %s\n", strerror(errno)); 591 - exit(EXIT_FAILURE); 592 - } 593 - 594 - char dummy = '\0'; 595 - char cmsgbuf[CMSG_SPACE(sizeof(int))]; 596 - struct msghdr msg; 597 - struct iovec iov; 598 - struct cmsghdr *cmptr; 599 - int sessionFD; 600 - 601 - memset(&msg, 0, sizeof(msg)); 602 - msg.msg_control = cmsgbuf; 603 - msg.msg_controllen = sizeof(cmsgbuf); 604 - 605 - iov.iov_base = &dummy; 606 - iov.iov_len = sizeof(dummy); 607 - msg.msg_iov = &iov; 608 - msg.msg_iovlen = 1; 609 - 610 - if (recvmsg(sockfd, &msg, 0) != sizeof(dummy)) 611 - { 612 - fprintf(stderr, "Error receiving reply from shellsession: %s\n", strerror(errno)); 613 - exit(EXIT_FAILURE); 614 - } 615 - 616 - cmptr = CMSG_FIRSTHDR(&msg); 617 - 618 - if (cmptr == NULL || cmptr->cmsg_level != SOL_SOCKET || cmptr->cmsg_type != SCM_RIGHTS) 619 - { 620 - fprintf(stderr, "Invalid reply from shellsession: no attached FD\n"); 621 - exit(EXIT_FAILURE); 622 - } 623 - 624 - if (cmptr->cmsg_len != CMSG_LEN(sizeof(int))) 625 - { 626 - fprintf(stderr, "Invalid reply from shellsession: invalid CMSG length %lu (expected %lu)\n", cmptr->cmsg_len, sizeof(int)); 627 - exit(EXIT_FAILURE); 628 - } 629 - 630 - memcpy(&sessionFD, CMSG_DATA(cmptr), sizeof(int)); 631 - 632 - if (sessionFD < 0) 633 - { 634 - fprintf(stderr, "Invalid reply from shellsession: invalid FD %d\n", sessionFD); 635 - exit(EXIT_FAILURE); 636 - } 637 - 638 - return sessionFD; 639 - } 640 - 641 580 void setupShellspawnEnv(int sockfd) 642 581 { 643 582 static const char* skip_vars[] = { ··· 763 702 else 764 703 buffer = NULL; 765 704 766 - sockfd = connectToShellsession(); 767 - sockfd = startShellspawnSession(sockfd); 705 + sockfd = connectToShellspawn(); 768 706 769 707 setupShellspawnEnv(sockfd); 770 708 ··· 788 726 int fds[3], master; 789 727 int sockfd; 790 728 791 - sockfd = connectToShellsession(); 792 - sockfd = startShellspawnSession(sockfd); 793 - 729 + sockfd = connectToShellspawn(); 794 730 setupShellspawnEnv(sockfd); 795 731 796 732 pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary);