···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+4747+int main(int argc, const char** argv)
4848+{
4949+ setupSocket();
5050+ listenForConnections();
5151+5252+ if (g_serverSocket != -1)
5353+ close(g_serverSocket);
5454+ return 0;
5555+}
5656+5757+void setupSocket(void)
5858+{
5959+ struct sockaddr_un addr = {
6060+ .sun_family = AF_UNIX,
6161+ .sun_path = SHELLSPAWN_SOCKPATH
6262+ };
6363+6464+ g_serverSocket = socket(AF_UNIX, SOCK_STREAM, 0);
6565+ if (g_serverSocket == -1)
6666+ {
6767+ perror("Creating unix socket");
6868+ exit(EXIT_FAILURE);
6969+ }
7070+7171+ fcntl(g_serverSocket, F_SETFD, FD_CLOEXEC);
7272+ unlink(SHELLSPAWN_SOCKPATH);
7373+7474+ if (bind(g_serverSocket, (struct sockaddr*) &addr, sizeof(addr)) == -1)
7575+ {
7676+ perror("Binding the unix socket");
7777+ exit(EXIT_FAILURE);
7878+ }
7979+8080+ chmod(addr.sun_path, 0600);
8181+8282+ if (listen(g_serverSocket, 1) == -1)
8383+ {
8484+ perror("Listening on unix socket");
8585+ exit(EXIT_FAILURE);
8686+ }
8787+}
8888+8989+void listenForConnections(void)
9090+{
9191+ int sock;
9292+ struct sockaddr_un addr;
9393+ socklen_t len = sizeof(addr);
9494+9595+ while (true)
9696+ {
9797+ sock = accept(g_serverSocket, (struct sockaddr*) &addr, &len);
9898+ if (sock == -1)
9999+ break;
100100+101101+ if (fork() == 0)
102102+ {
103103+ fcntl(sock, F_SETFD, FD_CLOEXEC);
104104+ spawnShell(sock);
105105+ exit(EXIT_SUCCESS);
106106+ }
107107+ else
108108+ {
109109+ close(sock);
110110+ }
111111+ }
112112+}
113113+114114+void spawnShell(int fd)
115115+{
116116+ pid_t shell_pid = -1;
117117+ int shellfd[3] = { -1, -1, -1 };
118118+ int pipefd[2];
119119+ int rv;
120120+ struct pollfd pfd[2];
121121+ char** argv = NULL;
122122+ int argc = 1;
123123+ struct msghdr msg;
124124+ struct iovec iov;
125125+ char cmsgbuf[CMSG_SPACE(sizeof(int)) * 3];
126126+ int kq;
127127+128128+ bool read_cmds = true;
129129+130130+ argv = (char**) malloc(sizeof(char*) * 2);
131131+ argv[0] = "/bin/bash";
132132+133133+ // Read commands from client
134134+ while (read_cmds)
135135+ {
136136+ struct shellspawn_cmd cmd;
137137+ char* param = NULL;
138138+139139+ memset(&msg, 0, sizeof(msg));
140140+ msg.msg_control = cmsgbuf;
141141+ msg.msg_controllen = sizeof(cmsgbuf);
142142+143143+ iov.iov_base = &cmd;
144144+ iov.iov_len = sizeof(cmd);
145145+ msg.msg_iov = &iov;
146146+ msg.msg_iovlen = 1;
147147+148148+ if (recvmsg(fd, &msg, 0) != sizeof(cmd))
149149+ {
150150+ if (DBG) puts("bad recvmsg");
151151+ goto err;
152152+ }
153153+154154+ if (cmd.data_length != 0)
155155+ {
156156+ param = (char*) malloc(cmd.data_length + 1);
157157+ if (read(fd, param, cmd.data_length) != cmd.data_length)
158158+ goto err;
159159+ param[cmd.data_length] = '\0';
160160+ }
161161+162162+ switch (cmd.cmd)
163163+ {
164164+ case SHELLSPAWN_ADDARG:
165165+ {
166166+ if (param != NULL)
167167+ {
168168+ argv = (char**) realloc(argv, sizeof(char*) * (argc + 1));
169169+ argv[argc] = param;
170170+ if (DBG) printf("add arg: %s\n", param);
171171+ argc++;
172172+ }
173173+ break;
174174+ }
175175+ case SHELLSPAWN_SETENV:
176176+ {
177177+ if (param != NULL)
178178+ {
179179+ if (DBG) printf("set env: %s\n", param);
180180+ putenv(param);
181181+ free(param);
182182+ }
183183+ break;
184184+ }
185185+ case SHELLSPAWN_CHDIR:
186186+ {
187187+ if (param != NULL)
188188+ {
189189+ if (DBG) printf("chdir: %s\n", param);
190190+ chdir(param);
191191+ free(param);
192192+ }
193193+ break;
194194+ }
195195+ case SHELLSPAWN_GO:
196196+ {
197197+ struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
198198+199199+ if (cmptr == NULL)
200200+ {
201201+ if (DBG) puts("bad cmptr");
202202+ goto err;
203203+ }
204204+ if (cmptr->cmsg_level != SOL_SOCKET
205205+ || cmptr->cmsg_type != SCM_RIGHTS)
206206+ {
207207+ if (DBG) puts("bad cmsg level/type");
208208+ goto err;
209209+ }
210210+ if (cmptr->cmsg_len != CMSG_LEN(sizeof(int) * 3))
211211+ {
212212+ if (DBG) printf("bad cmsg_len: %d\n", cmptr->cmsg_len);
213213+ goto err;
214214+ }
215215+216216+ memcpy(shellfd, CMSG_DATA(cmptr), sizeof(int) * 3);
217217+218218+ if (DBG) printf("go, fds={ %d, %d, %d }\n", shellfd[0], shellfd[1], shellfd[2]);
219219+ free(param);
220220+ read_cmds = false;
221221+ break;
222222+ }
223223+ }
224224+ }
225225+226226+ // Add terminating NULL
227227+ argv = (char**) realloc(argv, sizeof(char*) * (argc + 1));
228228+ argv[argc] = NULL;
229229+230230+ if (pipe(pipefd) == -1)
231231+ goto err;
232232+233233+ setsid();
234234+ setpgrp();
235235+236236+ close(STDIN_FILENO);
237237+ close(STDOUT_FILENO);
238238+ close(STDERR_FILENO);
239239+240240+ dup2(shellfd[0], STDIN_FILENO);
241241+ dup2(shellfd[1], STDOUT_FILENO);
242242+ dup2(shellfd[2], STDERR_FILENO);
243243+244244+ ioctl(STDIN_FILENO, TIOCSCTTY, STDIN_FILENO);
245245+246246+ shell_pid = fork();
247247+ if (shell_pid == 0)
248248+ {
249249+ close(fd);
250250+251251+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
252252+253253+ // In future, we may support spawning something else than Bash
254254+ // and check the provided shell against /etc/shells
255255+ execv("/bin/bash", argv);
256256+257257+ rv = errno;
258258+ write(pipefd[1], &rv, sizeof(rv));
259259+ close(pipefd[1]);
260260+261261+ exit(EXIT_FAILURE);
262262+ }
263263+264264+ // Check that exec succeeded
265265+ close(pipefd[1]); // close the write end
266266+ if (read(pipefd[0], &rv, sizeof(rv)) == sizeof(rv))
267267+ {
268268+ errno = rv;
269269+ goto err;
270270+ }
271271+ close(pipefd[0]);
272272+273273+ // Now we start passing signals
274274+ // and check for child process exit
275275+276276+ kq = kqueue();
277277+278278+ {
279279+ struct kevent changes[2];
280280+ EV_SET(&changes[0], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
281281+ EV_SET(&changes[1], shell_pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, NULL);
282282+283283+ if (kevent(kq, changes, 2, NULL, 0, NULL) == -1)
284284+ goto err;
285285+ }
286286+287287+ while (true)
288288+ {
289289+ struct kevent ev;
290290+291291+ if (kevent(kq, NULL, 0, &ev, 1, NULL) <= 0)
292292+ {
293293+ if (DBG) puts("kevent fail");
294294+ goto err;
295295+ }
296296+297297+ if (ev.filter == EVFILT_PROC && (ev.fflags & NOTE_EXIT))
298298+ {
299299+ if (DBG) puts("subprocess exit");
300300+ break;
301301+ }
302302+ else if (ev.filter == EVFILT_READ)
303303+ {
304304+ struct shellspawn_cmd cmd;
305305+306306+ if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))
307307+ {
308308+ if (DBG) puts("Cannot read cmd");
309309+ break;
310310+ }
311311+312312+ switch (cmd.cmd)
313313+ {
314314+ case SHELLSPAWN_SIGNAL:
315315+ {
316316+ int linux_signal, darwin_signal;
317317+318318+ if (cmd.data_length != sizeof(int))
319319+ goto err;
320320+321321+ if (read(fd, &linux_signal, sizeof(int)) != sizeof(int))
322322+ goto err;
323323+324324+ // Convert Linux signal number to Darwin signal number
325325+ darwin_signal = signum_linux_to_bsd(linux_signal);
326326+ if (DBG) printf("rcvd signal %d -> %d\n", linux_signal, darwin_signal);
327327+328328+ if (darwin_signal != 0)
329329+ {
330330+ int fg_pid = tcgetpgrp(shellfd[0]);
331331+ if (fg_pid != -1)
332332+ {
333333+ if (DBG) printf("fg_pid = %d\n", fg_pid);
334334+ kill(fg_pid, darwin_signal);
335335+ }
336336+ else
337337+ kill(-shell_pid, darwin_signal);
338338+ }
339339+340340+ break;
341341+ }
342342+ default:
343343+ goto err;
344344+ }
345345+ }
346346+ }
347347+348348+ // Kill the child process in case it's still running
349349+ kill(shell_pid, SIGKILL);
350350+351351+ // Close shell fds
352352+ for (int i = 0; i < 3; i++)
353353+ {
354354+ if (shellfd[i] != -1)
355355+ close(shellfd[0]);
356356+ }
357357+358358+ // Reap the child
359359+ int wstatus;
360360+ waitpid(shell_pid, &wstatus, WEXITED);
361361+ wstatus = WEXITSTATUS(wstatus);
362362+363363+ // Report exit code back to the client
364364+ write(fd, &wstatus, sizeof(int));
365365+366366+ if (DBG) printf("Shell terminated with exit code %d\n", wstatus);
367367+ close(fd);
368368+ return;
369369+err:
370370+ if (DBG) fprintf(stderr, "Error spawning shell: %s\n", strerror(errno));
371371+372372+ for (int i = 0; i < 3; i++)
373373+ {
374374+ if (shellfd[i] != -1)
375375+ close(shellfd[0]);
376376+ }
377377+378378+ if (shell_pid != -1)
379379+ kill(shell_pid, SIGKILL);
380380+381381+ close(fd);
382382+}
383383+
+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"
4249···50575158int main(int argc, char ** argv, char ** envp)
5259{
5353- pid_t pidInit, pidChild;
6060+ pid_t pidInit;
5461 int wstatus;
55625663 if (argc <= 1)
···156163 // If prefix's init is not running, start it up
157164 if (pidInit == 0)
158165 {
166166+ char socketPath[4096];
167167+168168+ snprintf(socketPath, sizeof(socketPath), "%s" SHELLSPAWN_SOCKPATH, prefix);
169169+170170+ unlink(socketPath);
171171+159172 setupWorkdir();
160173 pidInit = spawnInitProcess();
161174 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)
175175+176176+ // Wait until shellspawn starts
177177+ for (int i = 0; i < 15; i++)
170178 {
171171- fprintf(stderr, "Cannot resolve path: %s\n", strerror(errno));
172172- exit(1);
179179+ if (access(socketPath, F_OK) == 0)
180180+ break;
181181+ sleep(1);
173182 }
183183+ }
174184175175- const char *argv_child[argc + 1];
185185+ seteuid(g_originalUid);
176186177177- argv_child[0] = MLDR_PATH;
178178-179179- fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1);
180180- strcpy(fullPath, SYSTEM_ROOT);
181181- strcat(fullPath, path);
182182- argv_child[1] = fullPath;
183183-184184- for (int i = 2; i < argc; i++)
185185- argv_child[i] = argv[i];
186186- argv_child[argc] = NULL;
187187-188188- pidChild = spawnChild(pidInit, MLDR_PATH, argv_child);
189189- free(path);
190190- free(fullPath);
191191- }
192192- else
187187+ if (strcmp(argv[1], "shell") == 0)
193188 {
194189 // Spawn the shell
195190 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- }
191191+ spawnShell((const char**) &argv[2]);
212192 else
213213- pidChild = spawnChild(pidInit, MLDR_PATH,
214214- (const char *[3]) {MLDR_PATH, "/bin/bash", NULL});
193193+ spawnShell(NULL);
215194 }
216216- if (pidChild == -ENOMEM)
195195+ else
217196 {
218218- pidInit = 0;
219219- goto start_init;
220220- }
197197+ char *fullPath;
198198+ char** child_argv;
199199+ char *path = realpath(argv[1], NULL);
221200222222- // Drop the privileges so that we can be killed, etc by the user
223223- seteuid(g_originalUid);
201201+ fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1);
202202+ strcpy(fullPath, SYSTEM_ROOT);
203203+ strcat(fullPath, path);
224204225225- waitpid(pidChild, &wstatus, 0);
205205+ argv[1] = fullPath;
206206+ spawnShell((const char**) &argv[1]);
207207+ }
226208227227- if (WIFEXITED(wstatus))
228228- return WEXITSTATUS(wstatus);
229229- if (WIFSIGNALED(wstatus))
230230- return WTERMSIG(wstatus);
231209 return 0;
232210}
233211234234-static void joinNamespace(pid_t pid, int type, const char* typeName)
212212+static void pushShellspawnCommandData(int sockfd, shellspawn_cmd_type_t type, const void* data, size_t data_length)
235213{
236236- int fdNS;
237237- char pathNS[4096];
238238-239239- snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/%s", pid, typeName);
214214+ struct shellspawn_cmd* cmd;
215215+ size_t length;
240216241241- fdNS = open(pathNS, O_RDONLY);
217217+ length = sizeof(*cmd) + data_length;
242218243243- if (fdNS < 0)
219219+ cmd = (struct shellspawn_cmd*) malloc(length);
220220+ cmd->cmd = type;
221221+ cmd->data_length = data_length;
222222+223223+ if (data != NULL)
224224+ memcpy(cmd->data, data, data_length);
225225+226226+ if (write(sockfd, cmd, length) != length)
244227 {
245245- fprintf(stderr, "Cannot open %s namespace file: %s\n", typeName, strerror(errno));
246246- exit(1);
228228+ fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
229229+ exit(EXIT_FAILURE);
247230 }
231231+}
248232249249- // Calling setns() with a PID namespace doesn't move our process into it,
250250- // but our child process will be spawned inside the namespace
251251- if (setns(fdNS, type) != 0)
233233+static void pushShellspawnCommand(int sockfd, shellspawn_cmd_type_t type, const char* value)
234234+{
235235+ if (!value)
236236+ pushShellspawnCommandData(sockfd, type, NULL, 0);
237237+ else
238238+ pushShellspawnCommandData(sockfd, type, value, strlen(value) + 1);
239239+}
240240+241241+static void pushShellspawnCommandFDs(int sockfd, shellspawn_cmd_type_t type, const int fds[3])
242242+{
243243+ struct shellspawn_cmd cmd;
244244+ char cmsgbuf[CMSG_SPACE(sizeof(int) * 3)];
245245+ struct msghdr msg;
246246+ struct iovec iov;
247247+ struct cmsghdr *cmptr;
248248+249249+ cmd.cmd = type;
250250+ cmd.data_length = 0;
251251+252252+ iov.iov_base = &cmd;
253253+254254+ memset(&msg, 0, sizeof(msg));
255255+ msg.msg_control = cmsgbuf;
256256+ msg.msg_controllen = sizeof(cmsgbuf);
257257+258258+ iov.iov_base = &cmd;
259259+ iov.iov_len = sizeof(cmd);
260260+ msg.msg_iov = &iov;
261261+ msg.msg_iovlen = 1;
262262+263263+ cmptr = CMSG_FIRSTHDR(&msg);
264264+265265+ cmptr->cmsg_len = CMSG_LEN(sizeof(int) * 3);
266266+ cmptr->cmsg_level = SOL_SOCKET;
267267+ cmptr->cmsg_type = SCM_RIGHTS;
268268+ memcpy(CMSG_DATA(cmptr), fds, sizeof(fds[0])*3);
269269+270270+ if (sendmsg(sockfd, &msg, 0) < 0)
252271 {
253253- fprintf(stderr, "Cannot join %s namespace: %s\n", typeName, strerror(errno));
254254- exit(1);
272272+ fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
273273+ exit(EXIT_FAILURE);
255274 }
256256- close(fdNS);
257275}
258276259259-pid_t spawnChild(int pidInit, const char *path, const char *const argv[])
277277+static int _shSockfd = -1;
278278+static struct termios orig_termios;
279279+static int pty_master;
280280+static void signalHandler(int signo)
260281{
261261- pid_t pidChild;
262262- char curPath[4096];
282282+ // printf("Received signal %d\n", signo);
263283264264- if (getcwd(curPath, sizeof(curPath)) == NULL)
284284+ // Forward window size changes
285285+ if (signo == SIGWINCH && pty_master != -1)
265286 {
266266- fprintf(stderr, "Cannot get current directory: %s\n", strerror(errno));
267267- exit(1);
287287+ struct winsize win;
288288+289289+ ioctl(0, TIOCGWINSZ, &win);
290290+ ioctl(pty_master, TIOCSWINSZ, &win);
268291 }
292292+293293+ // Foreground process loopkup in shellspawn doesn't work
294294+ // if we're not running in TTY mode, so shellspawn falls back
295295+ // to forwarding signals to the Bash subprocess.
296296+ //
297297+ // Hence we translate SIGINT to SIGTERM for user convenience,
298298+ // because Bash will not terminate on SIGINT.
299299+ if (pty_master == -1 && signo == SIGINT)
300300+ signo = SIGTERM;
269301270270- joinNamespace(pidInit, CLONE_NEWPID, "pid");
271271- joinNamespace(pidInit, CLONE_NEWUTS, "uts");
302302+ pushShellspawnCommandData(_shSockfd, SHELLSPAWN_SIGNAL, &signo, sizeof(signo));
303303+}
272304273273- pidChild = fork();
274274- if (pidChild < 0)
305305+static void shellLoop(int sockfd, int master)
306306+{
307307+ struct sigaction sa;
308308+ struct pollfd pfds[3];
309309+ const int fdcount = (master != -1) ? 3 : 1; // do we do pty proxying?
310310+311311+ // Vars for signal handler
312312+ _shSockfd = sockfd;
313313+ pty_master = master;
314314+315315+ memset(&sa, 0, sizeof(sa));
316316+ sa.sa_handler = signalHandler;
317317+ sigfillset(&sa.sa_mask);
318318+319319+ for (int i = 1; i < 32; i++)
320320+ sigaction(i, &sa, NULL);
321321+322322+ pfds[2].fd = master;
323323+ pfds[2].events = POLLOUT;
324324+ pfds[2].revents = 0;
325325+ pfds[1].fd = STDIN_FILENO;
326326+ pfds[1].events = POLLIN;
327327+ pfds[1].revents = 0;
328328+ pfds[0].fd = sockfd;
329329+ pfds[0].events = POLLIN;
330330+ pfds[0].revents = 0;
331331+332332+ if (master != -1)
333333+ fcntl(master, F_SETFL, O_NONBLOCK);
334334+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
335335+ fcntl(sockfd, F_SETFL, O_NONBLOCK);
336336+337337+ while (1)
275338 {
276276- if (errno == ENOMEM)
339339+ char buf[4096];
340340+341341+ if (poll(pfds, fdcount, -1) < 0)
277342 {
278278- // This condition happens specifically when the init process is a zombie.
279279- // We should simply start a new init process.
280280- return -ENOMEM;
343343+ if (errno != EINTR)
344344+ {
345345+ perror("poll");
346346+ break;
347347+ }
281348 }
282282- fprintf(stderr, "Cannot spawn a child process: %s\n", strerror(errno));
283283- exit(1);
284284- }
285349286286- if (pidChild == 0)
287287- {
288288- // This is the child process
289289-290290- // We still have the outside PIDs in /proc
291291- joinNamespace(pidInit, CLONE_NEWNS, "mnt");
350350+ if (pfds[2].revents & POLLOUT)
351351+ {
352352+ int rd;
353353+ do
354354+ {
355355+ rd = read(master, buf, sizeof(buf));
356356+ if (rd > 0)
357357+ write(STDOUT_FILENO, buf, rd);
358358+ }
359359+ while (rd == sizeof(buf));
360360+ }
292361293293- /*
294294- snprintf(pathNS, sizeof(pathNS), SYSTEM_ROOT "/proc/%d/ns/user", pidInit);
295295- fdNS = open(pathNS, O_RDONLY);
296296- if (fdNS < 0)
362362+ if (pfds[1].revents & POLLIN)
297363 {
298298- fprintf(stderr, "Cannot open user namespace file: %s\n", strerror(errno));
299299- exit(1);
364364+ int rd;
365365+ do
366366+ {
367367+ rd = read(STDIN_FILENO, buf, sizeof(buf));
368368+ if (rd > 0)
369369+ write(master, buf, rd);
370370+ }
371371+ while (rd == sizeof(buf));
300372 }
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);
307373308308- /*
309309- if (setns(fdNS, CLONE_NEWUSER) != 0)
374374+ if (pfds[0].revents & (POLLHUP | POLLIN))
310375 {
311311- fprintf(stderr, "Cannot join user namespace: %s\n", strerror(errno));
312312- exit(1);
376376+ int exitStatus;
377377+378378+ if (read(sockfd, &exitStatus, sizeof(int)) == sizeof(int))
379379+ exit(exitStatus);
380380+ else
381381+ exit(1);
313382 }
314314- close(fdNS);
315315- */
383383+ }
384384+}
316385317317- setupChild(curPath);
386386+static void restoreTermios(void)
387387+{
388388+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
389389+}
318390319319- execv(path, (char * const *) argv);
391391+static void setupPtys(int fds[3], int* master)
392392+{
393393+ struct winsize win;
394394+ struct termios termios;
395395+ bool tty = true;
320396321321- fprintf(stderr, "Cannot exec the target program: %s\n", strerror(errno));
397397+ if (tcgetattr(STDIN_FILENO, &termios) < 0)
398398+ tty = false;
399399+400400+ if (openpty(master, &fds[0], NULL, &termios, &win) < 0)
401401+ {
402402+ perror("opentty");
322403 exit(1);
323404 }
405405+ fds[2] = fds[1] = fds[0];
324406325325- return pidChild;
407407+ if (tty)
408408+ {
409409+ orig_termios = termios;
410410+411411+ ioctl(0, TIOCGWINSZ, &win);
412412+413413+ termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
414414+ termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
415415+ INPCK | ISTRIP | IXON | PARMRK);
416416+ termios.c_oflag &= ~OPOST;
417417+ termios.c_cc[VMIN] = 1;
418418+ termios.c_cc[VTIME] = 0;
419419+420420+ if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) < 0)
421421+ {
422422+ perror("tcsetattr");
423423+ exit(1);
424424+ }
425425+ ioctl(*master, TIOCSWINSZ, &win);
426426+427427+ atexit(restoreTermios);
428428+ }
326429}
327430328328-void setupChild(const char *curPath)
431431+void spawnShell(const char** argv)
329432{
433433+ size_t total_len = 0;
434434+ int count;
330435 char buffer1[4096];
331436 char buffer2[4096];
437437+ int sockfd;
438438+ struct sockaddr_un addr;
439439+ char* buffer;
332440441441+ if (argv != NULL)
442442+ {
443443+ for (count = 0; argv[count] != NULL; count++)
444444+ total_len += strlen(argv[count]);
333445334334- unsetenv("LESSOPEN");
335335- unsetenv("LESSCLOSE");
336336- unsetenv("LESSECHO");
446446+ buffer = malloc(total_len + count*3);
337447338338- setenv("PATH",
339339- "/usr/bin:"
448448+ char *to = buffer;
449449+ for (int i = 0; argv[i] != NULL; i++)
450450+ {
451451+ if (to != buffer)
452452+ to = stpcpy(to, " ");
453453+ to = stpcpy(to, "'");
454454+ to = stpcpy(to, argv[i]);
455455+ to = stpcpy(to, "'");
456456+ }
457457+ }
458458+ else
459459+ buffer = NULL;
460460+461461+ // Connect to the shellspawn daemon in the container
462462+ addr.sun_family = AF_UNIX;
463463+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSPAWN_SOCKPATH, prefix);
464464+465465+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
466466+ if (sockfd == -1)
467467+ {
468468+ fprintf(stderr, "Error creating a unix domain socket: %s\n", strerror(errno));
469469+ exit(1);
470470+ }
471471+472472+ if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) == -1)
473473+ {
474474+ fprintf(stderr, "Error connecting to shellspawn in the container: %s\n", strerror(errno));
475475+ exit(1);
476476+ }
477477+478478+ // Push environment variables
479479+ pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV,
480480+ "PATH=/usr/bin:"
340481 "/bin:"
341482 "/usr/sbin:"
342483 "/sbin:"
343343- "/usr/local/bin",
344344- 1);
484484+ "/usr/local/bin");
345485346346- sscanf(getenv("HOME"), "/home/%4096s", buffer1);
347347- snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
348348- setenv("HOME", buffer2, 1);
486486+ if (sscanf(getenv("HOME"), "/home/%4096s", buffer1) == 1)
487487+ {
488488+ snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", buffer1);
489489+ pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2);
490490+ }
349491350350- if (sscanf(curPath, "/home/%4096s", buffer1) == 1)
492492+ // Push shell arguments
493493+ if (buffer != NULL)
351494 {
352352- // We're currently inside our home directory
353353- snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
354354- setenv("PWD", buffer2, 1);
355355- chdir(buffer2);
495495+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c");
496496+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer);
497497+498498+ free(buffer);
356499 }
357357- else
500500+501501+ if (getcwd(buffer1, sizeof(buffer1)) != NULL)
358502 {
359359- snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", curPath);
360360- setenv("PWD", buffer2, 1);
361361- chdir(buffer2);
503503+ snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", buffer1);
504504+ pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2);
362505 }
506506+507507+ int fds[3], master = -1;
508508+509509+ if (isatty(STDIN_FILENO))
510510+ setupPtys(fds, &master);
511511+ else
512512+ fds[0] = dup(STDIN_FILENO); // dup() because we close() a few lines below
513513+514514+ if (master == -1 || !isatty(STDOUT_FILENO))
515515+ fds[1] = STDOUT_FILENO;
516516+ if (master == -1 || !isatty(STDERR_FILENO))
517517+ fds[2] = STDERR_FILENO;
518518+519519+ pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds);
520520+ close(fds[0]);
521521+522522+ shellLoop(sockfd, master);
523523+524524+ if (master != -1)
525525+ close(master);
526526+ close(sockfd);
363527}
364528365529void showHelp(const char* argv0)
···432596{
433597 pid_t pid;
434598 int pipefd[2];
435435- // char idmap[100];
599599+ char path[1024];
436600 char buffer[1];
437601 FILE *file;
438602···540704 // if we enable user namespaces
541705542706 darlingPreInit();
707707+ spawnLaunchd();
708708+543709 // Never returns
544710 }
545711···575741 fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno));
576742 }
577743 */
578578-579579- // This is for development only!
580580- if (getenv("TRY_LAUNCHD") != NULL)
581581- {
582582- int status = 0;
583583- waitpid(pid, &status, 0);
584584- }
585744586745 // Here's where we resume the child
587746 // if we enable user namespaces
···616775 fclose(fp);
617776}
618777619619-void darlingPreInit(void)
778778+void spawnLaunchd(void)
620779{
621621- // TODO: Run /usr/libexec/makewhatis
780780+ puts("Bootstrapping the container with launchd...");
622781623623- // This is for development only!
624624- if (getenv("TRY_LAUNCHD") != NULL)
625625- {
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();
631631- }
632632-633633- // TODO: this is where we will exec() launchd in future.
634634- // Instead, we just reap zombies.
635635- while (1)
782782+ execl(MLDR_PATH, "mldr!/sbin/launchd", "launchd", NULL);
783783+784784+ fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
785785+ abort();
786786+}
787787+788788+static void wipeDir(const char* dirpath)
789789+{
790790+ char path[4096];
791791+ struct dirent* ent;
792792+ DIR* dir = opendir(dirpath);
793793+794794+ if (!dir)
795795+ return;
796796+797797+ while ((ent = readdir(dir)) != NULL)
636798 {
637637- int status, sig;
638638- sigset_t chld;
799799+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
800800+ continue;
639801640640- sigemptyset(&chld);
641641- sigaddset(&chld, SIGCHLD);
642642- sigwait(&chld, &sig);
802802+ snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name);
643803644644- while (waitpid(-1, &status, 0) != -1);
804804+ if (ent->d_type == DT_DIR)
805805+ {
806806+ wipeDir(path);
807807+ rmdir(path);
808808+ }
809809+ else
810810+ unlink(path);
645811 }
812812+813813+ closedir(dir);
814814+}
815815+816816+void darlingPreInit(void)
817817+{
818818+ // TODO: Run /usr/libexec/makewhatis
819819+ const char* dirs[] = {
820820+ "/var/tmp",
821821+ "/var/run"
822822+ };
823823+824824+ for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
825825+ wipeDir(dirs[i]);
646826}
647827648828char* defaultPrefixPath(void)
+3-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);