···11+/*
22+This file is part of Darling.
33+44+Copyright (C) 2017 Lubos Dolezel
55+66+Darling is free software: you can redistribute it and/or modify
77+it under the terms of the GNU General Public License as published by
88+the Free Software Foundation, either version 3 of the License, or
99+(at your option) any later version.
1010+1111+Darling is distributed in the hope that it will be useful,
1212+but WITHOUT ANY WARRANTY; without even the implied warranty of
1313+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1414+GNU General Public License for more details.
1515+1616+You should have received a copy of the GNU General Public License
1717+along with Darling. If not, see <http://www.gnu.org/licenses/>.
1818+*/
1919+2020+#include <sys/socket.h>
2121+#include <sys/un.h>
2222+#include <sys/stat.h>
2323+#include <stdlib.h>
2424+#include <unistd.h>
2525+#include <fcntl.h>
2626+#include <stdbool.h>
2727+#include <string.h>
2828+#include <stdio.h>
2929+#include <errno.h>
3030+#include <sys/poll.h>
3131+#include <sys/types.h>
3232+#include <sys/wait.h>
3333+#include <sys/event.h>
3434+#include <sys/ioctl.h>
3535+#include <signal.h>
3636+#include "shellspawn.h"
3737+#include "duct_signals.h"
3838+3939+#define DBG 0
4040+4141+int g_serverSocket = -1;
4242+4343+void setupSocket(void);
4444+void listenForConnections(void);
4545+void spawnShell(int fd);
4646+void setupSigchild(void);
4747+void reapAll(void);
4848+4949+int main(int argc, const char** argv)
5050+{
5151+ setupSigchild();
5252+ setupSocket();
5353+ listenForConnections();
5454+5555+ if (g_serverSocket != -1)
5656+ close(g_serverSocket);
5757+ return 0;
5858+}
5959+6060+void setupSocket(void)
6161+{
6262+ struct sockaddr_un addr = {
6363+ .sun_family = AF_UNIX,
6464+ .sun_path = SHELLSPAWN_SOCKPATH
6565+ };
6666+6767+ g_serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
6868+ if (g_serverSocket == -1)
6969+ {
7070+ perror("Creating unix socket");
7171+ exit(EXIT_FAILURE);
7272+ }
7373+7474+ fcntl(g_serverSocket, F_SETFD, FD_CLOEXEC);
7575+ unlink(SHELLSPAWN_SOCKPATH);
7676+7777+ if (bind(g_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) == -1)
7878+ {
7979+ perror("Binding the unix socket");
8080+ exit(EXIT_FAILURE);
8181+ }
8282+8383+ chmod(addr.sun_path, 0600);
8484+8585+ if (listen(g_serverSocket, 1) == -1)
8686+ {
8787+ perror("Listening on unix socket");
8888+ exit(EXIT_FAILURE);
8989+ }
9090+}
9191+9292+void listenForConnections(void)
9393+{
9494+ int sock;
9595+ struct sockaddr_un addr;
9696+ socklen_t len = sizeof(addr);
9797+9898+ while (true)
9999+ {
100100+ sock = accept(g_serverSocket, (struct sockaddr*) &addr, &len);
101101+ if (sock == -1)
102102+ break;
103103+104104+ if (fork() == 0)
105105+ {
106106+ fcntl(sock, F_SETFD, FD_CLOEXEC);
107107+ spawnShell(sock);
108108+ exit(EXIT_SUCCESS);
109109+ }
110110+ else
111111+ {
112112+ close(sock);
113113+ }
114114+ }
115115+}
116116+117117+void spawnShell(int fd)
118118+{
119119+ pid_t shell_pid = -1;
120120+ int shellfd[3] = { -1, -1, -1 };
121121+ int pipefd[2];
122122+ int rv;
123123+ struct pollfd pfd[2];
124124+ char** argv = NULL;
125125+ int argc = 1;
126126+ struct msghdr msg;
127127+ struct iovec iov;
128128+ char cmsgbuf[CMSG_SPACE(sizeof(int)) * 3];
129129+ int kq;
130130+131131+ bool read_cmds = true;
132132+133133+ argv = (char**) malloc(sizeof(char*) * 2);
134134+ argv[0] = "/bin/bash";
135135+136136+ // Read commands from client
137137+ while (read_cmds)
138138+ {
139139+ struct shellspawn_cmd cmd;
140140+ char* param = NULL;
141141+142142+ memset(&msg, 0, sizeof(msg));
143143+ msg.msg_control = cmsgbuf;
144144+ msg.msg_controllen = sizeof(cmsgbuf);
145145+146146+ iov.iov_base = &cmd;
147147+ iov.iov_len = sizeof(cmd);
148148+ msg.msg_iov = &iov;
149149+ msg.msg_iovlen = 1;
150150+151151+ if (recvmsg(fd, &msg, 0) != sizeof(cmd))
152152+ {
153153+ if (DBG) puts("bad recvmsg");
154154+ goto err;
155155+ }
156156+157157+ if (cmd.data_length != 0)
158158+ {
159159+ param = (char*) malloc(cmd.data_length + 1);
160160+ if (read(fd, param, cmd.data_length) != cmd.data_length)
161161+ goto err;
162162+ param[cmd.data_length] = '\0';
163163+ }
164164+165165+ switch (cmd.cmd)
166166+ {
167167+ case SHELLSPAWN_ADDARG:
168168+ {
169169+ if (param != NULL)
170170+ {
171171+ argv = (char**) realloc(argv, sizeof(char*) * (argc + 1));
172172+ argv[argc] = param;
173173+ if (DBG) printf("add arg: %s\n", param);
174174+ argc++;
175175+ }
176176+ break;
177177+ }
178178+ case SHELLSPAWN_SETENV:
179179+ {
180180+ if (param != NULL)
181181+ {
182182+ if (DBG) printf("set env: %s\n", param);
183183+ putenv(param);
184184+ free(param);
185185+ }
186186+ break;
187187+ }
188188+ case SHELLSPAWN_CHDIR:
189189+ {
190190+ if (param != NULL)
191191+ {
192192+ if (DBG) printf("chdir: %s\n", param);
193193+ chdir(param);
194194+ free(param);
195195+ }
196196+ break;
197197+ }
198198+ case SHELLSPAWN_GO:
199199+ {
200200+ struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
201201+202202+ if (cmptr == NULL)
203203+ {
204204+ if (DBG) puts("bad cmptr");
205205+ goto err;
206206+ }
207207+ if (cmptr->cmsg_level != SOL_SOCKET
208208+ || cmptr->cmsg_type != SCM_RIGHTS)
209209+ {
210210+ if (DBG) puts("bad cmsg level/type");
211211+ goto err;
212212+ }
213213+ if (cmptr->cmsg_len != CMSG_LEN(sizeof(int) * 3))
214214+ {
215215+ if (DBG) printf("bad cmsg_len: %d\n", cmptr->cmsg_len);
216216+ goto err;
217217+ }
218218+219219+ memcpy(shellfd, CMSG_DATA(cmptr), sizeof(int) * 3);
220220+221221+ if (DBG) printf("go, fds={ %d, %d, %d }\n", shellfd[0], shellfd[1], shellfd[2]);
222222+ free(param);
223223+ read_cmds = false;
224224+ break;
225225+ }
226226+ }
227227+ }
228228+229229+ // Add terminating NULL
230230+ argv = (char**) realloc(argv, sizeof(char*) * (argc + 1));
231231+ argv[argc] = NULL;
232232+233233+ if (pipe(pipefd) == -1)
234234+ goto err;
235235+236236+ setsid();
237237+ setpgrp();
238238+239239+ close(STDIN_FILENO);
240240+ close(STDOUT_FILENO);
241241+ close(STDERR_FILENO);
242242+243243+ dup2(shellfd[0], STDIN_FILENO);
244244+ dup2(shellfd[1], STDOUT_FILENO);
245245+ dup2(shellfd[2], STDERR_FILENO);
246246+247247+ ioctl(STDIN_FILENO, TIOCSCTTY, STDIN_FILENO);
248248+249249+ shell_pid = fork();
250250+ if (shell_pid == 0)
251251+ {
252252+ close(fd);
253253+254254+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
255255+256256+ // In future, we may support spawning something else than Bash
257257+ // and check the provided shell against /etc/shells
258258+ execv("/bin/bash", argv);
259259+260260+ rv = errno;
261261+ write(pipefd[1], &rv, sizeof(rv));
262262+ close(pipefd[1]);
263263+264264+ exit(EXIT_FAILURE);
265265+ }
266266+267267+ // Check that exec succeeded
268268+ close(pipefd[1]); // close the write end
269269+ if (read(pipefd[0], &rv, sizeof(rv)) == sizeof(rv))
270270+ {
271271+ errno = rv;
272272+ goto err;
273273+ }
274274+ close(pipefd[0]);
275275+276276+ // Now we start passing signals
277277+ // and check for child process exit
278278+279279+ kq = kqueue();
280280+281281+ {
282282+ struct kevent changes[2];
283283+ EV_SET(&changes[0], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
284284+ EV_SET(&changes[1], shell_pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, NULL);
285285+286286+ if (kevent(kq, changes, 2, NULL, 0, NULL) == -1)
287287+ goto err;
288288+ }
289289+290290+ while (true)
291291+ {
292292+ struct kevent ev;
293293+294294+ if (kevent(kq, NULL, 0, &ev, 1, NULL) <= 0)
295295+ {
296296+ if (DBG) puts("kevent fail");
297297+ goto err;
298298+ }
299299+300300+ if (ev.filter == EVFILT_PROC && (ev.fflags & NOTE_EXIT))
301301+ {
302302+ if (DBG) puts("subprocess exit");
303303+ break;
304304+ }
305305+ else if (ev.filter == EVFILT_READ)
306306+ {
307307+ struct shellspawn_cmd cmd;
308308+309309+ if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))
310310+ {
311311+ if (DBG) puts("Cannot read cmd");
312312+ break;
313313+ }
314314+315315+ switch (cmd.cmd)
316316+ {
317317+ case SHELLSPAWN_SIGNAL:
318318+ {
319319+ int linux_signal, darwin_signal;
320320+321321+ if (cmd.data_length != sizeof(int))
322322+ goto err;
323323+324324+ if (read(fd, &linux_signal, sizeof(int)) != sizeof(int))
325325+ goto err;
326326+327327+ // Convert Linux signal number to Darwin signal number
328328+ darwin_signal = signum_linux_to_bsd(linux_signal);
329329+ if (DBG) printf("rcvd signal %d -> %d\n", linux_signal, darwin_signal);
330330+331331+ if (darwin_signal != 0)
332332+ {
333333+ int fg_pid = tcgetpgrp(shellfd[0]);
334334+ if (fg_pid != -1)
335335+ {
336336+ if (DBG) printf("fg_pid = %d\n", fg_pid);
337337+ kill(fg_pid, darwin_signal);
338338+ }
339339+ else
340340+ kill(-shell_pid, darwin_signal);
341341+ }
342342+343343+ break;
344344+ }
345345+ default:
346346+ goto err;
347347+ }
348348+ }
349349+ }
350350+351351+ // Kill the child process in case it's still running
352352+ kill(shell_pid, SIGKILL);
353353+354354+ // Close shell fds
355355+ for (int i = 0; i < 3; i++)
356356+ {
357357+ if (shellfd[i] != -1)
358358+ close(shellfd[0]);
359359+ }
360360+361361+ // Reap the child
362362+ int wstatus;
363363+ waitpid(shell_pid, &wstatus, WEXITED);
364364+ wstatus = WEXITSTATUS(wstatus);
365365+366366+ // Report exit code back to the client
367367+ write(fd, &wstatus, sizeof(int));
368368+369369+ if (DBG) printf("Shell terminated with exit code %d\n", wstatus);
370370+ close(fd);
371371+372372+ reapAll();
373373+ return;
374374+err:
375375+ if (DBG) fprintf(stderr, "Error spawning shell: %s\n", strerror(errno));
376376+377377+ for (int i = 0; i < 3; i++)
378378+ {
379379+ if (shellfd[i] != -1)
380380+ close(shellfd[0]);
381381+ }
382382+383383+ if (shell_pid != -1)
384384+ kill(shell_pid, SIGKILL);
385385+386386+ close(fd);
387387+ reapAll();
388388+}
389389+390390+void setupSigchild(void)
391391+{
392392+ struct sigaction sigchld_action = {
393393+ .sa_handler = SIG_DFL,
394394+ .sa_flags = SA_NOCLDWAIT
395395+ };
396396+ sigaction(SIGCHLD, &sigchld_action, NULL);
397397+}
398398+399399+void reapAll(void)
400400+{
401401+ while (waitpid((pid_t)(-1), 0, WNOHANG) > 0);
402402+}
+47
src/shellspawn/shellspawn.h
···11+/*
22+This file is part of Darling.
33+44+Copyright (C) 2017 Lubos Dolezel
55+66+Darling is free software: you can redistribute it and/or modify
77+it under the terms of the GNU General Public License as published by
88+the Free Software Foundation, either version 3 of the License, or
99+(at your option) any later version.
1010+1111+Darling is distributed in the hope that it will be useful,
1212+but WITHOUT ANY WARRANTY; without even the implied warranty of
1313+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1414+GNU General Public License for more details.
1515+1616+You should have received a copy of the GNU General Public License
1717+along with Darling. If not, see <http://www.gnu.org/licenses/>.
1818+*/
1919+2020+#ifndef _SHELLSPAWN_H
2121+#define _SHELLSPAWN_H
2222+2323+#ifdef TESTING
2424+#define SHELLSPAWN_SOCKPATH "/tmp/shellspawn.sock"
2525+#else
2626+#define SHELLSPAWN_SOCKPATH "/var/run/shellspawn.sock"
2727+#endif
2828+2929+typedef unsigned short shellspawn_cmd_type_t;
3030+3131+enum {
3232+ SHELLSPAWN_ADDARG = 1, // add shell argument
3333+ SHELLSPAWN_SETENV, // add env variable string
3434+ SHELLSPAWN_CHDIR,
3535+ SHELLSPAWN_GO, // execute the shell now, must also contain file descriptors
3636+ SHELLSPAWN_SIGNAL, // pass a signal from client
3737+};
3838+3939+struct __attribute__((packed)) shellspawn_cmd
4040+{
4141+ shellspawn_cmd_type_t cmd;
4242+ unsigned short data_length;
4343+ char data[];
4444+};
4545+4646+#endif
4747+
···3636#include <sys/utsname.h>
3737#include <sys/stat.h>
3838#include <sys/syscall.h>
3939+#include <sys/inotify.h>
4040+#include <sys/poll.h>
4141+#include <sys/socket.h>
4242+#include <sys/un.h>
3943#include <getopt.h>
4444+#include <termios.h>
4545+#include <pty.h>
4646+#include "../shellspawn/shellspawn.h"
4047#include "darling.h"
4148#include "darling-config.h"
42494350#define MLDR_PATH "/bin/mldr"
44515252+// Between Linux 4.9 and 4.11, a strange bug has been introduced
5353+// which prevents connecting to Unix sockets if the socket was
5454+// created in a different mount namespace or under overlayfs
5555+// (dunno which one is really responsible for this).
5656+#define USE_LINUX_4_11_HACK 1
5757+4558const char* DARLING_INIT_COMM = "darling-init";
4659char *prefix;
4760uid_t g_originalUid, g_originalGid;
···50635164int main(int argc, char ** argv, char ** envp)
5265{
5353- pid_t pidInit, pidChild;
6666+ pid_t pidInit;
5467 int wstatus;
55685669 if (argc <= 1)
···156169 // If prefix's init is not running, start it up
157170 if (pidInit == 0)
158171 {
172172+ char socketPath[4096];
173173+174174+ snprintf(socketPath, sizeof(socketPath), "%s" SHELLSPAWN_SOCKPATH, prefix);
175175+176176+ unlink(socketPath);
177177+159178 setupWorkdir();
160179 pidInit = spawnInitProcess();
161180 putInitPid(pidInit);
162162- }
163163-164164- if (strcmp(argv[1], "shell") != 0)
165165- {
166166- char *path = realpath(argv[1], NULL);
167167- char *fullPath;
168168-169169- if (path == NULL)
181181+182182+ // Wait until shellspawn starts
183183+ for (int i = 0; i < 15; i++)
170184 {
171171- fprintf(stderr, "Cannot resolve path: %s\n", strerror(errno));
172172- exit(1);
185185+ if (access(socketPath, F_OK) == 0)
186186+ break;
187187+ sleep(1);
173188 }
174174-175175- const char *argv_child[argc + 1];
176176-177177- argv_child[0] = MLDR_PATH;
189189+ }
178190179179- fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1);
180180- strcpy(fullPath, SYSTEM_ROOT);
181181- strcat(fullPath, path);
182182- argv_child[1] = fullPath;
191191+#if USE_LINUX_4_11_HACK
192192+ joinNamespace(pidInit, CLONE_NEWNS, "mnt");
193193+#endif
183194184184- for (int i = 2; i < argc; i++)
185185- argv_child[i] = argv[i];
186186- argv_child[argc] = NULL;
195195+ seteuid(g_originalUid);
187196188188- pidChild = spawnChild(pidInit, MLDR_PATH, argv_child);
189189- free(path);
190190- free(fullPath);
191191- }
192192- else
197197+ if (strcmp(argv[1], "shell") == 0)
193198 {
194199 // Spawn the shell
195200 if (argc > 2)
196196- {
197197- size_t total_len = 0;
198198- for (int i = 2; i < argc; i++)
199199- total_len += strlen(argv[i]);
200200-201201- char buffer[total_len + argc];
202202-203203- char *to = buffer;
204204- for (int i = 2; i < argc; i++)
205205- to = stpcpy(stpcpy(to, argv[i]), " ");
206206- // Overwrite the last whitespace
207207- *(to - 1) = '\0';
208208-209209- pidChild = spawnChild(pidInit, MLDR_PATH,
210210- (const char *[5]) {MLDR_PATH, "/bin/bash", "-c", buffer, NULL});
211211- }
201201+ spawnShell((const char**) &argv[2]);
212202 else
213213- pidChild = spawnChild(pidInit, MLDR_PATH,
214214- (const char *[3]) {MLDR_PATH, "/bin/bash", NULL});
203203+ spawnShell(NULL);
215204 }
216216- if (pidChild == -ENOMEM)
205205+ else
217206 {
218218- pidInit = 0;
219219- goto start_init;
220220- }
207207+ char *fullPath;
208208+ char** child_argv;
209209+ char *path = realpath(argv[1], NULL);
221210222222- // Drop the privileges so that we can be killed, etc by the user
223223- seteuid(g_originalUid);
211211+ fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1);
212212+ strcpy(fullPath, SYSTEM_ROOT);
213213+ strcat(fullPath, path);
224214225225- waitpid(pidChild, &wstatus, 0);
215215+ argv[1] = fullPath;
216216+ spawnShell((const char**) &argv[1]);
217217+ }
226218227227- if (WIFEXITED(wstatus))
228228- return WEXITSTATUS(wstatus);
229229- if (WIFSIGNALED(wstatus))
230230- return WTERMSIG(wstatus);
231219 return 0;
232220}
233221234234-static void joinNamespace(pid_t pid, int type, const char* typeName)
222222+void joinNamespace(pid_t pid, int type, const char* typeName)
235223{
236224 int fdNS;
237225 char pathNS[4096];
···256244 close(fdNS);
257245}
258246259259-pid_t spawnChild(int pidInit, const char *path, const char *const argv[])
247247+static void pushShellspawnCommandData(int sockfd, shellspawn_cmd_type_t type, const void* data, size_t data_length)
260248{
261261- pid_t pidChild;
262262- char curPath[4096];
249249+ struct shellspawn_cmd* cmd;
250250+ size_t length;
251251+252252+ length = sizeof(*cmd) + data_length;
253253+254254+ cmd = (struct shellspawn_cmd*) malloc(length);
255255+ cmd->cmd = type;
256256+ cmd->data_length = data_length;
257257+258258+ if (data != NULL)
259259+ memcpy(cmd->data, data, data_length);
263260264264- if (getcwd(curPath, sizeof(curPath)) == NULL)
261261+ if (write(sockfd, cmd, length) != length)
265262 {
266266- fprintf(stderr, "Cannot get current directory: %s\n", strerror(errno));
267267- exit(1);
263263+ fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
264264+ exit(EXIT_FAILURE);
268265 }
266266+}
269267270270- joinNamespace(pidInit, CLONE_NEWPID, "pid");
271271- joinNamespace(pidInit, CLONE_NEWUTS, "uts");
268268+static void pushShellspawnCommand(int sockfd, shellspawn_cmd_type_t type, const char* value)
269269+{
270270+ if (!value)
271271+ pushShellspawnCommandData(sockfd, type, NULL, 0);
272272+ else
273273+ pushShellspawnCommandData(sockfd, type, value, strlen(value) + 1);
274274+}
272275273273- pidChild = fork();
274274- if (pidChild < 0)
276276+static void pushShellspawnCommandFDs(int sockfd, shellspawn_cmd_type_t type, const int fds[3])
277277+{
278278+ struct shellspawn_cmd cmd;
279279+ char cmsgbuf[CMSG_SPACE(sizeof(int) * 3)];
280280+ struct msghdr msg;
281281+ struct iovec iov;
282282+ struct cmsghdr *cmptr;
283283+284284+ cmd.cmd = type;
285285+ cmd.data_length = 0;
286286+287287+ iov.iov_base = &cmd;
288288+289289+ memset(&msg, 0, sizeof(msg));
290290+ msg.msg_control = cmsgbuf;
291291+ msg.msg_controllen = sizeof(cmsgbuf);
292292+293293+ iov.iov_base = &cmd;
294294+ iov.iov_len = sizeof(cmd);
295295+ msg.msg_iov = &iov;
296296+ msg.msg_iovlen = 1;
297297+298298+ cmptr = CMSG_FIRSTHDR(&msg);
299299+300300+ cmptr->cmsg_len = CMSG_LEN(sizeof(int) * 3);
301301+ cmptr->cmsg_level = SOL_SOCKET;
302302+ cmptr->cmsg_type = SCM_RIGHTS;
303303+ memcpy(CMSG_DATA(cmptr), fds, sizeof(fds[0])*3);
304304+305305+ if (sendmsg(sockfd, &msg, 0) < 0)
275306 {
276276- if (errno == ENOMEM)
277277- {
278278- // This condition happens specifically when the init process is a zombie.
279279- // We should simply start a new init process.
280280- return -ENOMEM;
281281- }
282282- fprintf(stderr, "Cannot spawn a child process: %s\n", strerror(errno));
283283- exit(1);
307307+ fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
308308+ exit(EXIT_FAILURE);
284309 }
310310+}
285311286286- if (pidChild == 0)
312312+static int _shSockfd = -1;
313313+static struct termios orig_termios;
314314+static int pty_master;
315315+static void signalHandler(int signo)
316316+{
317317+ // printf("Received signal %d\n", signo);
318318+319319+ // Forward window size changes
320320+ if (signo == SIGWINCH && pty_master != -1)
287321 {
288288- // This is the child process
322322+ struct winsize win;
289323290290- // We still have the outside PIDs in /proc
291291- joinNamespace(pidInit, CLONE_NEWNS, "mnt");
324324+ ioctl(0, TIOCGWINSZ, &win);
325325+ ioctl(pty_master, TIOCSWINSZ, &win);
326326+ }
327327+328328+ // Foreground process loopkup in shellspawn doesn't work
329329+ // if we're not running in TTY mode, so shellspawn falls back
330330+ // to forwarding signals to the Bash subprocess.
331331+ //
332332+ // Hence we translate SIGINT to SIGTERM for user convenience,
333333+ // because Bash will not terminate on SIGINT.
334334+ if (pty_master == -1 && signo == SIGINT)
335335+ signo = SIGTERM;
292336293293- /*
294294- snprintf(pathNS, sizeof(pathNS), SYSTEM_ROOT "/proc/%d/ns/user", pidInit);
295295- fdNS = open(pathNS, O_RDONLY);
296296- if (fdNS < 0)
337337+ pushShellspawnCommandData(_shSockfd, SHELLSPAWN_SIGNAL, &signo, sizeof(signo));
338338+}
339339+340340+static void shellLoop(int sockfd, int master)
341341+{
342342+ struct sigaction sa;
343343+ struct pollfd pfds[3];
344344+ const int fdcount = (master != -1) ? 3 : 1; // do we do pty proxying?
345345+346346+ // Vars for signal handler
347347+ _shSockfd = sockfd;
348348+ pty_master = master;
349349+350350+ memset(&sa, 0, sizeof(sa));
351351+ sa.sa_handler = signalHandler;
352352+ sigfillset(&sa.sa_mask);
353353+354354+ for (int i = 1; i < 32; i++)
355355+ sigaction(i, &sa, NULL);
356356+357357+ pfds[2].fd = master;
358358+ pfds[2].events = POLLIN;
359359+ pfds[2].revents = 0;
360360+ pfds[1].fd = STDIN_FILENO;
361361+ pfds[1].events = POLLIN;
362362+ pfds[1].revents = 0;
363363+ pfds[0].fd = sockfd;
364364+ pfds[0].events = POLLIN;
365365+ pfds[0].revents = 0;
366366+367367+ if (master != -1)
368368+ fcntl(master, F_SETFL, O_NONBLOCK);
369369+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
370370+ fcntl(sockfd, F_SETFL, O_NONBLOCK);
371371+372372+ while (1)
373373+ {
374374+ char buf[4096];
375375+376376+ if (poll(pfds, fdcount, -1) < 0)
297377 {
298298- fprintf(stderr, "Cannot open user namespace file: %s\n", strerror(errno));
299299- exit(1);
378378+ if (errno != EINTR)
379379+ {
380380+ perror("poll");
381381+ break;
382382+ }
300383 }
301301- */
302302-303303- // Drop the privileges. It's important to drop GID first, because
304304- // non-root users can't change their GID.
305305- setresgid(g_originalGid, g_originalGid, g_originalGid);
306306- setresuid(g_originalUid, g_originalUid, g_originalUid);
307384308308- /*
309309- if (setns(fdNS, CLONE_NEWUSER) != 0)
385385+ if (pfds[2].revents & POLLIN)
310386 {
311311- fprintf(stderr, "Cannot join user namespace: %s\n", strerror(errno));
312312- exit(1);
387387+ int rd;
388388+ do
389389+ {
390390+ rd = read(master, buf, sizeof(buf));
391391+ if (rd > 0)
392392+ write(STDOUT_FILENO, buf, rd);
393393+ }
394394+ while (rd == sizeof(buf));
313395 }
314314- close(fdNS);
315315- */
316396317317- setupChild(curPath);
397397+ if (pfds[1].revents & POLLIN)
398398+ {
399399+ int rd;
400400+ do
401401+ {
402402+ rd = read(STDIN_FILENO, buf, sizeof(buf));
403403+ if (rd > 0)
404404+ write(master, buf, rd);
405405+ }
406406+ while (rd == sizeof(buf));
407407+ }
318408319319- execv(path, (char * const *) argv);
409409+ if (pfds[0].revents & (POLLHUP | POLLIN))
410410+ {
411411+ int exitStatus;
412412+413413+ if (read(sockfd, &exitStatus, sizeof(int)) == sizeof(int))
414414+ exit(exitStatus);
415415+ else
416416+ exit(1);
417417+ }
418418+ }
419419+}
420420+421421+static void restoreTermios(void)
422422+{
423423+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
424424+}
425425+426426+// Glibc openpty() fails for me on Debian, because grantpty() fails to chown() the pty node with error code EPERM.
427427+// This is a more lenient version of openpty() that just works.
428428+static int openpty_darling(int* amaster, int* aslave, char* name_unused, const struct termios* tos, const struct winsize* wsz)
429429+{
430430+ const char* slave_name;
431431+432432+ *amaster = posix_openpt(O_RDWR);
433433+ if (*amaster == -1)
434434+ return -1;
435435+436436+ grantpt(*amaster);
437437+ if (unlockpt(*amaster) < 0)
438438+ return -1;
439439+440440+ slave_name = ptsname(*amaster);
441441+ *aslave = open(slave_name, O_RDWR | O_NOCTTY);
442442+ if (*aslave == -1)
443443+ return -1;
320444321321- fprintf(stderr, "Cannot exec the target program: %s\n", strerror(errno));
445445+ if (tos != NULL)
446446+ tcsetattr(*amaster, TCSANOW, tos);
447447+ if (wsz != NULL)
448448+ ioctl(*amaster, TIOCSWINSZ, wsz);
449449+450450+ return 0;
451451+}
452452+453453+static void setupPtys(int fds[3], int* master)
454454+{
455455+ struct winsize win;
456456+ struct termios termios;
457457+ bool tty = true;
458458+459459+ if (tcgetattr(STDIN_FILENO, &termios) < 0)
460460+ tty = false;
461461+462462+ if (openpty_darling(master, &fds[0], NULL, &termios, NULL) < 0)
463463+ {
464464+ perror("openpty");
322465 exit(1);
323466 }
467467+ fds[2] = fds[1] = fds[0];
324468325325- return pidChild;
469469+ if (tty)
470470+ {
471471+ orig_termios = termios;
472472+473473+ ioctl(0, TIOCGWINSZ, &win);
474474+475475+ termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
476476+ termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
477477+ INPCK | ISTRIP | IXON | PARMRK);
478478+ termios.c_oflag &= ~OPOST;
479479+ termios.c_cc[VMIN] = 1;
480480+ termios.c_cc[VTIME] = 0;
481481+482482+ if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) < 0)
483483+ {
484484+ perror("tcsetattr");
485485+ exit(1);
486486+ }
487487+ ioctl(*master, TIOCSWINSZ, &win);
488488+489489+ atexit(restoreTermios);
490490+ }
326491}
327492328328-void setupChild(const char *curPath)
493493+void spawnShell(const char** argv)
329494{
495495+ size_t total_len = 0;
496496+ int count;
330497 char buffer1[4096];
331498 char buffer2[4096];
499499+ int sockfd;
500500+ struct sockaddr_un addr;
501501+ char* buffer;
332502503503+ if (argv != NULL)
504504+ {
505505+ for (count = 0; argv[count] != NULL; count++)
506506+ total_len += strlen(argv[count]);
507507+508508+ buffer = malloc(total_len + count*3);
333509334334- unsetenv("LESSOPEN");
335335- unsetenv("LESSCLOSE");
336336- unsetenv("LESSECHO");
510510+ char *to = buffer;
511511+ for (int i = 0; argv[i] != NULL; i++)
512512+ {
513513+ if (to != buffer)
514514+ to = stpcpy(to, " ");
515515+ to = stpcpy(to, "'");
516516+ to = stpcpy(to, argv[i]);
517517+ to = stpcpy(to, "'");
518518+ }
519519+ }
520520+ else
521521+ buffer = NULL;
337522338338- setenv("PATH",
339339- "/usr/bin:"
523523+ // Connect to the shellspawn daemon in the container
524524+ addr.sun_family = AF_UNIX;
525525+#if USE_LINUX_4_11_HACK
526526+ strcpy(addr.sun_path, SHELLSPAWN_SOCKPATH);
527527+#else
528528+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSPAWN_SOCKPATH, prefix);
529529+#endif
530530+531531+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
532532+ if (sockfd == -1)
533533+ {
534534+ fprintf(stderr, "Error creating a unix domain socket: %s\n", strerror(errno));
535535+ exit(1);
536536+ }
537537+538538+ if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) == -1)
539539+ {
540540+ fprintf(stderr, "Error connecting to shellspawn in the container (%s): %s\n", addr.sun_path, strerror(errno));
541541+ exit(1);
542542+ }
543543+544544+ // Push environment variables
545545+ pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV,
546546+ "PATH=/usr/bin:"
340547 "/bin:"
341548 "/usr/sbin:"
342549 "/sbin:"
343343- "/usr/local/bin",
344344- 1);
550550+ "/usr/local/bin");
345551346346- sscanf(getenv("HOME"), "/home/%4096s", buffer1);
347347- snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
348348- setenv("HOME", buffer2, 1);
552552+ if (sscanf(getenv("HOME"), "/home/%4096s", buffer1) == 1)
553553+ {
554554+ snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", buffer1);
555555+ pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2);
556556+ }
349557350350- if (sscanf(curPath, "/home/%4096s", buffer1) == 1)
558558+ // Push shell arguments
559559+ if (buffer != NULL)
351560 {
352352- // We're currently inside our home directory
353353- snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
354354- setenv("PWD", buffer2, 1);
355355- chdir(buffer2);
561561+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c");
562562+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer);
563563+564564+ free(buffer);
356565 }
357357- else
566566+567567+ if (getcwd(buffer1, sizeof(buffer1)) != NULL)
358568 {
359359- snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", curPath);
360360- setenv("PWD", buffer2, 1);
361361- chdir(buffer2);
569569+ snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", buffer1);
570570+ pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2);
362571 }
572572+573573+ int fds[3], master = -1;
574574+575575+ if (isatty(STDIN_FILENO))
576576+ setupPtys(fds, &master);
577577+ else
578578+ fds[0] = dup(STDIN_FILENO); // dup() because we close() a few lines below
579579+580580+ if (master == -1 || !isatty(STDOUT_FILENO))
581581+ fds[1] = STDOUT_FILENO;
582582+ if (master == -1 || !isatty(STDERR_FILENO))
583583+ fds[2] = STDERR_FILENO;
584584+585585+ pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds);
586586+ close(fds[0]);
587587+588588+ shellLoop(sockfd, master);
589589+590590+ if (master != -1)
591591+ close(master);
592592+ close(sockfd);
363593}
364594365595void showHelp(const char* argv0)
···432662{
433663 pid_t pid;
434664 int pipefd[2];
435435- // char idmap[100];
665665+ char path[1024];
436666 char buffer[1];
437667 FILE *file;
438668···540770 // if we enable user namespaces
541771542772 darlingPreInit();
773773+ spawnLaunchd();
774774+543775 // Never returns
544776 }
545777···575807 fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno));
576808 }
577809 */
578578-579579- // This is for development only!
580580- if (getenv("TRY_LAUNCHD") != NULL)
581581- {
582582- int status = 0;
583583- waitpid(pid, &status, 0);
584584- }
585810586811 // Here's where we resume the child
587812 // if we enable user namespaces
···616841 fclose(fp);
617842}
618843619619-void darlingPreInit(void)
844844+void spawnLaunchd(void)
620845{
621621- // TODO: Run /usr/libexec/makewhatis
846846+ puts("Bootstrapping the container with launchd...");
622847623623- // This is for development only!
624624- if (getenv("TRY_LAUNCHD") != NULL)
848848+ // putenv("KQUEUE_DEBUG=1");
849849+ execl(MLDR_PATH, "mldr!/sbin/launchd", "launchd", NULL);
850850+851851+ fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
852852+ abort();
853853+}
854854+855855+static void wipeDir(const char* dirpath)
856856+{
857857+ char path[4096];
858858+ struct dirent* ent;
859859+ DIR* dir = opendir(dirpath);
860860+861861+ if (!dir)
862862+ return;
863863+864864+ while ((ent = readdir(dir)) != NULL)
625865 {
626626- // putenv("KQUEUE_DEBUG=1");
627627- execl("/bin/mldr", "mldr!/sbin/launchd", "launchd", NULL);
628628-629629- fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
630630- abort();
866866+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
867867+ continue;
868868+869869+ snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name);
870870+871871+ if (ent->d_type == DT_DIR)
872872+ {
873873+ wipeDir(path);
874874+ rmdir(path);
875875+ }
876876+ else
877877+ unlink(path);
631878 }
632632-633633- // TODO: this is where we will exec() launchd in future.
634634- // Instead, we just reap zombies.
635635- while (1)
636636- {
637637- int status, sig;
638638- sigset_t chld;
879879+880880+ closedir(dir);
881881+}
639882640640- sigemptyset(&chld);
641641- sigaddset(&chld, SIGCHLD);
642642- sigwait(&chld, &sig);
883883+void darlingPreInit(void)
884884+{
885885+ // TODO: Run /usr/libexec/makewhatis
886886+ const char* dirs[] = {
887887+ "/var/tmp",
888888+ "/var/run"
889889+ };
643890644644- while (waitpid(-1, &status, 0) != -1);
645645- }
891891+ for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
892892+ wipeDir(dirs[i]);
646893}
647894648895char* defaultPrefixPath(void)
···749996 "/private/var/db",
750997 "/var",
751998 "/var/run",
752752- "/var/tmp"
999999+ "/var/tmp",
10001000+ "/var/log"
7531001 };
75410027551003 fprintf(stderr, "Setting up a new Darling prefix at %s\n", prefix);
···8251073 }
8261074 fclose(fp);
8271075828828- if (strcmp(exeBuf, DARLING_INIT_COMM) != 0)
10761076+ if (strcmp(exeBuf, "mldr") != 0)
8291077 {
8301078 unlink(pidPath);
8311079 return 0;
+4-4
src/startup/darling.h
···4040// Creates the given directory, exit()ing if not possible
4141void createDir(const char* path);
42424343-// Spawn the specified proceess inside the namespaces that PID 1 is in
4444-// Returns the PID of the child
4545-// exit()s on error
4646-pid_t spawnChild(int pidInit, const char *path, const char *const argv[]);
4343+void spawnShell(const char** argv);
47444845// Set up some environment variables
4946// As well as the working directory
···5754pid_t spawnInitProcess(void);
58555956void putInitPid(pid_t pidInit);
5757+void mapUids(pid_t pid);
60585959+void spawnLaunchd(void);
6160void darlingPreInit(void);
62616362void checkPrefixOwner(void);
···6564int isModuleLoaded(void);
66656766void loadKernelModule(void);
6767+void joinNamespace(pid_t pid, int type, const char* typeName);
68686969#endif