diff --git a/src/shellspawn/CMakeLists.txt b/src/shellspawn/CMakeLists.txt
index d441f49ca..fa1c195c9 100644
--- a/src/shellspawn/CMakeLists.txt
+++ b/src/shellspawn/CMakeLists.txt
@@ -3,9 +3,7 @@ project(shellspawn)
add_definitions(-nostdinc)
add_darling_executable(shellspawn shellspawn.c duct_signals.c)
-add_darling_executable(shellsession shellsession.c)
-install(TARGETS shellspawn shellsession DESTINATION libexec/darling/usr/libexec)
-install(FILES org.darlinghq.shellsession.plist DESTINATION libexec/darling/System/Library/LaunchDaemons)
-install(FILES org.darlinghq.shellspawn.plist DESTINATION libexec/darling/System/Library/LaunchAgents)
+install(TARGETS shellspawn DESTINATION libexec/darling/usr/libexec)
+install(FILES org.darlinghq.shellspawn.plist DESTINATION libexec/darling/System/Library/LaunchDaemons)
diff --git a/src/shellspawn/org.darlinghq.shellsession.plist b/src/shellspawn/org.darlinghq.shellsession.plist
deleted file mode 100644
index c8b519d37..000000000
--- a/src/shellspawn/org.darlinghq.shellsession.plist
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
- Label
- org.darlinghq.shellsession
- ProgramArguments
-
- /usr/libexec/shellsession
-
- RunAtLoad
-
- KeepAlive
-
-
-
diff --git a/src/shellspawn/shellsession.c b/src/shellspawn/shellsession.c
deleted file mode 100644
index dbc1101c3..000000000
--- a/src/shellspawn/shellsession.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * This file is part of Darling.
- *
- * Copyright (C) 2023 Darling Developers
- *
- * Darling is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Darling is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Darling. If not, see .
- */
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include "shellsession.h"
-#include "shellspawn.h"
-
-#define MAX_ACCESS_TRIES 10
-
-static void setup_socket_addr(struct sockaddr_un* addr);
-static void setup_session_socket_addr(unsigned int uid, struct sockaddr_un* addr);
-static int setup_socket(const struct sockaddr_un* addr);
-static void listen_for_connections(int server_socket);
-static void spawn_session(int sockfd);
-static void send_client_socket(int sockfd, int socket_to_send);
-static int try_connect_session(const struct sockaddr_un* addr);
-static void run_session_manager(unsigned int uid, unsigned int gid);
-
-int main(int argc, const char* const* argv) {
- struct sockaddr_un server_addr;
- int server_socket = -1;
-
- setup_socket_addr(&server_addr);
-
- server_socket = setup_socket(&server_addr);
- if (server_socket < 0) {
- exit(EXIT_FAILURE);
- }
-
- listen_for_connections(server_socket);
-
- if (server_socket >= 0) {
- close(server_socket);
- }
-
- return 0;
-};
-
-static void setup_socket_addr(struct sockaddr_un* addr) {
- memset(addr, 0, sizeof(*addr));
- addr->sun_family = AF_UNIX;
- snprintf(addr->sun_path, sizeof(addr->sun_path), "%s", SHELLSESSION_SOCKPATH);
-};
-
-static void setup_session_socket_addr(unsigned int uid, struct sockaddr_un* addr) {
- memset(addr, 0, sizeof(*addr));
- addr->sun_family = AF_UNIX;
- snprintf(addr->sun_path, sizeof(addr->sun_path), "%s.%d", SHELLSPAWN_SOCKPATH, uid);
-};
-
-static int setup_socket(const struct sockaddr_un* addr) {
- int server_socket = -1;
-
- server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (server_socket == -1) {
- perror("Creating unix socket");
- goto err;
- }
-
- fcntl(server_socket, F_SETFD, FD_CLOEXEC);
- unlink(addr->sun_path);
-
- if (bind(server_socket, (struct sockaddr*)addr, sizeof(*addr)) == -1) {
- perror("Binding the unix socket");
- goto err;
- }
-
- chmod(addr->sun_path, S_IRUSR | S_IWUSR);
-
- if (listen(server_socket, 1) == -1) {
- perror("Listening on unix socket");
- goto err;
- }
-
- return server_socket;
-
-err:
- if (server_socket >= 0) {
- close(server_socket);
- }
- return -1;
-};
-
-static void listen_for_connections(int server_socket) {
- int sock;
- struct sockaddr_un client_addr;
- socklen_t len = sizeof(client_addr);
-
- while (true) {
- sock = accept(server_socket, (struct sockaddr*)&client_addr, &len);
- if (sock == -1) {
- break;
- }
-
- if (fork() == 0) {
- // we don't need the server socket
- close(server_socket);
- server_socket = -1;
-
- fcntl(sock, F_SETFD, FD_CLOEXEC);
- spawn_session(sock);
-
- exit(EXIT_SUCCESS);
- } else {
- close(sock);
- }
- }
-};
-
-void spawn_session(int sockfd) {
- shellsession_cmd_t cmd;
- struct sockaddr_un session_addr;
- int client_socket = -1;
- char session_started_path[PATH_MAX];
- int started_fd = -1;
-
- if (read(sockfd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
- goto err;
- }
-
- setup_session_socket_addr(cmd.uid, &session_addr);
- snprintf(session_started_path, sizeof(session_started_path), "%s.%d", SHELLSESSION_STARTED_PATH, cmd.uid);
-
- started_fd = open(session_started_path, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
- if (started_fd < 0 && errno == EEXIST) {
- // we already had a session set up; avoid setting up a new one.
-
- // wait until shellspawn starts
- for (int i = 0; i < MAX_ACCESS_TRIES; i++) {
- if (access(session_addr.sun_path, F_OK) == 0) {
- break;
- }
-
- sleep(1);
- }
-
- client_socket = try_connect_session(&session_addr);
- if (client_socket < 0) {
- goto err;
- }
-
- // now reply to the client with the session client socket
- send_client_socket(sockfd, client_socket);
- close(sockfd);
- return;
- } else if (started_fd < 0) {
- perror("Failed to create session indicator file");
- goto err;
- }
-
- // we didn't have a session set up already, so let's create one.
-
- // we only need to create the session-running file; we don't need to write anything to it
- close(started_fd);
-
- if (fork() == 0) {
- // this is the new session manager process
- close(sockfd);
- run_session_manager(cmd.uid, cmd.gid);
- } else {
- // wait until shellspawn starts
- for (int i = 0; i < MAX_ACCESS_TRIES; i++) {
- if (access(session_addr.sun_path, F_OK) == 0) {
- break;
- }
-
- sleep(1);
- }
-
- client_socket = try_connect_session(&session_addr);
- if (client_socket < 0) {
- fprintf(stderr, "Error connecting to shellspawn session\n");
- exit(EXIT_FAILURE);
- }
-
- // now reply to the client with the session client socket
- send_client_socket(sockfd, client_socket);
- close(sockfd);
- }
-
- return;
-
-err:
- close(sockfd);
-};
-
-static void send_client_socket(int sockfd, int socket_to_send) {
- char dummy = '\0';
- struct msghdr msg;
- char cmsgbuf[CMSG_SPACE(sizeof(int))];
- struct cmsghdr *cmptr;
- struct iovec iov;
-
- memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
-
- iov.iov_base = &dummy;
- iov.iov_len = sizeof(dummy);
-
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = cmsgbuf;
- msg.msg_controllen = sizeof(cmsgbuf);
-
- cmptr = CMSG_FIRSTHDR(&msg);
- cmptr->cmsg_len = CMSG_LEN(sizeof(int));
- cmptr->cmsg_level = SOL_SOCKET;
- cmptr->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmptr), &socket_to_send, sizeof(socket_to_send));
-
- if (sendmsg(sockfd, &msg, 0) != sizeof(dummy)) {
- fprintf(stderr, "Error sending reply: %s\n", strerror(errno));
- exit(EXIT_FAILURE);
- }
-};
-
-static int try_connect_session(const struct sockaddr_un* addr) {
- int client_socket = -1;
-
- client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
- if (client_socket < 0) {
- goto err;
- }
-
- if (connect(client_socket, (struct sockaddr*)addr, sizeof(*addr)) != 0) {
- goto err;
- }
-
- return client_socket;
-
-err:
- if (client_socket >= 0) {
- close(client_socket);
- }
- return -1;
-};
-
-static void run_session_manager(unsigned int uid, unsigned int gid) {
- const char* login = NULL;
- struct passwd* pw = NULL;
- char* homedirTmp = NULL;
- mach_port_t subset = MACH_PORT_NULL;
- mach_port_t dummyService = MACH_PORT_NULL;
- kern_return_t kr = KERN_SUCCESS;
-
- // we are the shellspawn instance for the user's session. this means we're in charge
- // of the user's session and are responsible for setting it up as macOS would.
- // we are essentially going to be doing the same kind of setup that LoginWindow
- // would do on macOS.
-
- // switch ourselves to run under the user's UID and GID (but under Darling, this is all fake anyways)
- setgid(gid);
- setuid(uid);
-
- // fix up env vars to what they should be
- pw = getpwuid(uid);
- if (pw != NULL)
- login = pw->pw_name;
-
- if (!login)
- login = getlogin();
-
- setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin", 1);
- setenv("TMPDIR", "/private/tmp", 1);
-
- asprintf(&homedirTmp, "HOME=/Users/%s", login);
- putenv(homedirTmp);
- homedirTmp = NULL; // `putenv()` assumes ownership of the string
-
- //
- // IMPORTANT NOTE
- //
- // okay, so i have no clue how this whole session switching is supposed to work.
- // or rather, i do have *some* idea: we need to call `create_and_switch_to_per_session_launchd`
- // to switch to a per-user launchd (which will also trigger LaunchAgents for the user).
- // the problem is that the way this function works internally is that it calls `_vprocmgr_move_subset_to_user`.
- // this function, in turn, moves the current subset into the new per-user launchd session.
- // obviously, this means we must already have a subset prior to calling `create_and_switch_to_per_session_launchd`.
- //
- // the thing is, i can't see how (the modern version of) LoginWindow does this. maybe the API has changed since then
- // 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
- // the session setup. but clearly, *we* need to do this, so let's go ahead and do so.
- //
-
- if ((kr = bootstrap_subset(bootstrap_port, mach_task_self(), &subset)) != KERN_SUCCESS) {
- fprintf(stderr, "Failed to create bootstrap subset: %d\n", kr);
- exit(EXIT_FAILURE);
- }
-
- // replace the bootstrap port with our subset port
- if ((kr = task_set_bootstrap_port(mach_task_self(), subset)) != KERN_SUCCESS) {
- fprintf(stderr, "Failed to replace bootstrap port: %d\n", kr);
- exit(EXIT_FAILURE);
- }
-
- // we no longer need the old bootstrap port
- mach_port_deallocate(mach_task_self(), bootstrap_port);
-
- bootstrap_port = subset;
-
- //
- // ANOTHER IMPORTANT NOTE
- //
- // launchd requires the new subset to have at least one Mach service registered before switching to the per-user
- // session. to that end, we simply create a bogus service here.
- //
- if ((kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &dummyService)) != KERN_SUCCESS) {
- fprintf(stderr, "Failed to allocate dummy service port: %d\n", kr);
- exit(EXIT_FAILURE);
- }
-
- if ((kr = mach_port_insert_right(mach_task_self(), dummyService, dummyService, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
- fprintf(stderr, "Failed to insert send right into dummy service port: %d\n", kr);
- exit(EXIT_FAILURE);
- }
-
- if ((kr = bootstrap_register(bootstrap_port, "org.darlinghq.shellsession.dummy-service", dummyService)) != KERN_SUCCESS) {
- fprintf(stderr, "Failed to register dummy service: %d\n", kr);
- exit(EXIT_FAILURE);
- }
-
- // set up a launchd session
- if (create_and_switch_to_per_session_launchd("shellspawn", LAUNCH_GLOBAL_ON_DEMAND) < 0) {
- fprintf(stderr, "Failed to set up launchd user session.\n");
- exit(EXIT_FAILURE);
- }
-
- // unset the global-on-demand flag to kickstart jobs that need to run at load
- if (!_vproc_set_global_on_demand(false)) {
- fprintf(stderr, "Failed to unset global-on-demand flag.\n");
- exit(EXIT_FAILURE);
- }
-
- // now we just need to stay alive for launchd to keep our session alive
- // (launchd will start shellspawn as a LaunchAgent, and that's what the `darling` command will actually send commands to)
- while (true) {
- pause();
- }
-};
diff --git a/src/shellspawn/shellsession.h b/src/shellspawn/shellsession.h
deleted file mode 100644
index a2d4c7421..000000000
--- a/src/shellspawn/shellsession.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * This file is part of Darling.
- *
- * Copyright (C) 2023 Darling Developers
- *
- * Darling is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Darling is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Darling. If not, see .
- */
-
-#ifndef _SHELLSESSION_H_
-#define _SHELLSESSION_H_
-
-#ifdef TESTING
-#define SHELLSESSION_SOCKPATH "/tmp/shellsession.sock"
-#define SHELLSESSION_STARTED_PATH "/tmp/shellsession.running"
-#else
-#define SHELLSESSION_SOCKPATH "/var/run/shellsession.sock"
-#define SHELLSESSION_STARTED_PATH "/var/run/shellsession.running"
-#endif
-
-typedef struct shellsession_cmd {
- unsigned int uid;
- unsigned int gid;
-} shellsession_cmd_t;
-
-#endif // _SHELLSESSION_H_
-
diff --git a/src/shellspawn/shellspawn.c b/src/shellspawn/shellspawn.c
index a32603498..2f99825bc 100644
--- a/src/shellspawn/shellspawn.c
+++ b/src/shellspawn/shellspawn.c
@@ -64,10 +64,9 @@ void setupSocket(void)
{
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
+ .sun_path = SHELLSPAWN_SOCKPATH
};
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s.%d", SHELLSPAWN_SOCKPATH, getuid());
-
g_serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (g_serverSocket == -1)
{
@@ -76,7 +75,7 @@ void setupSocket(void)
}
fcntl(g_serverSocket, F_SETFD, FD_CLOEXEC);
- unlink(addr.sun_path);
+ unlink(SHELLSPAWN_SOCKPATH);
if (bind(g_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
diff --git a/src/startup/darling.c b/src/startup/darling.c
index 7912e9782..6014f46e8 100644
--- a/src/startup/darling.c
+++ b/src/startup/darling.c
@@ -37,7 +37,6 @@ along with Darling. If not, see .
#include
#include
#include "../shellspawn/shellspawn.h"
-#include "../shellspawn/shellsession.h"
#include "darling.h"
#include "darling-config.h"
@@ -170,7 +169,7 @@ int main(int argc, char ** argv)
{
char socketPath[4096];
- snprintf(socketPath, sizeof(socketPath), "%s" SHELLSESSION_SOCKPATH, prefix);
+ snprintf(socketPath, sizeof(socketPath), "%s" SHELLSPAWN_SOCKPATH, prefix);
unlink(socketPath);
@@ -546,20 +545,20 @@ static size_t escapeQuotes(char *dest, const char *src)
return len;
}
-int connectToShellsession(void)
+int connectToShellspawn(void)
{
struct sockaddr_un addr;
int sockfd;
- // Connect to the shellsession daemon in the container
+ // Connect to the shellspawn daemon in the container
addr.sun_family = AF_UNIX;
#if USE_LINUX_4_11_HACK
addr.sun_path[0] = '\0';
strcpy(addr.sun_path, prefix);
- strcat(addr.sun_path, SHELLSESSION_SOCKPATH);
+ strcat(addr.sun_path, SHELLSPAWN_SOCKPATH);
#else
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSESSION_SOCKPATH, prefix);
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSPAWN_SOCKPATH, prefix);
#endif
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -578,66 +577,6 @@ int connectToShellsession(void)
return sockfd;
}
-static int startShellspawnSession(int sockfd)
-{
- shellsession_cmd_t cmd;
-
- cmd.uid = g_originalGid;
- cmd.gid = g_originalGid;
-
- if (write(sockfd, &cmd, sizeof(cmd)) != sizeof(cmd))
- {
- fprintf(stderr, "Error sending command to shellsession: %s\n", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- char dummy = '\0';
- char cmsgbuf[CMSG_SPACE(sizeof(int))];
- struct msghdr msg;
- struct iovec iov;
- struct cmsghdr *cmptr;
- int sessionFD;
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_control = cmsgbuf;
- msg.msg_controllen = sizeof(cmsgbuf);
-
- iov.iov_base = &dummy;
- iov.iov_len = sizeof(dummy);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- if (recvmsg(sockfd, &msg, 0) != sizeof(dummy))
- {
- fprintf(stderr, "Error receiving reply from shellsession: %s\n", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- cmptr = CMSG_FIRSTHDR(&msg);
-
- if (cmptr == NULL || cmptr->cmsg_level != SOL_SOCKET || cmptr->cmsg_type != SCM_RIGHTS)
- {
- fprintf(stderr, "Invalid reply from shellsession: no attached FD\n");
- exit(EXIT_FAILURE);
- }
-
- if (cmptr->cmsg_len != CMSG_LEN(sizeof(int)))
- {
- fprintf(stderr, "Invalid reply from shellsession: invalid CMSG length %lu (expected %lu)\n", cmptr->cmsg_len, sizeof(int));
- exit(EXIT_FAILURE);
- }
-
- memcpy(&sessionFD, CMSG_DATA(cmptr), sizeof(int));
-
- if (sessionFD < 0)
- {
- fprintf(stderr, "Invalid reply from shellsession: invalid FD %d\n", sessionFD);
- exit(EXIT_FAILURE);
- }
-
- return sessionFD;
-}
-
void setupShellspawnEnv(int sockfd)
{
static const char* skip_vars[] = {
@@ -763,8 +702,7 @@ void spawnShell(const char** argv)
else
buffer = NULL;
- sockfd = connectToShellsession();
- sockfd = startShellspawnSession(sockfd);
+ sockfd = connectToShellspawn();
setupShellspawnEnv(sockfd);
@@ -788,9 +726,7 @@ void spawnBinary(const char* binary, const char** argv)
int fds[3], master;
int sockfd;
- sockfd = connectToShellsession();
- sockfd = startShellspawnSession(sockfd);
-
+ sockfd = connectToShellspawn();
setupShellspawnEnv(sockfd);
pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary);