···48484949int main(int argc, const char** argv)
5050{
5151- setupSigchild();
5151+ // in order to read the exit status of the process,
5252+ // we have to allow it to become a zombie, which is prevented
5353+ // when we set the SIGCHLD signal to SA_NOCLDWAIT
5454+ //setupSigchild();
5255 setupSocket();
5356 listenForConnections();
5457···133136 argv = (char**) malloc(sizeof(char*) * 3);
134137 argv[0] = "/bin/bash";
135138 argv[1] = "--login";
139139+140140+ char* alloc_exec = NULL;
136141137142 // Read commands from client
138143 while (read_cmds)
···238243239244 break;
240245 }
246246+ case SHELLSPAWN_SETEXEC:
247247+ {
248248+ argc = 0;
249249+ argv = realloc(argv, 0);
250250+ alloc_exec = param;
251251+ if (DBG) printf("setexec: %s\n", param);
252252+ break;
253253+ }
241254 }
242255 }
243256···270283271284 // In future, we may support spawning something else than Bash
272285 // and check the provided shell against /etc/shells
273273- execv("/bin/bash", argv);
286286+ execv(alloc_exec ? alloc_exec : "/bin/bash", argv);
274287275288 rv = errno;
276289 write(pipefd[1], &rv, sizeof(rv));
277290 close(pipefd[1]);
278291279292 exit(EXIT_FAILURE);
293293+ }
294294+295295+ if (alloc_exec)
296296+ {
297297+ free(alloc_exec);
298298+ alloc_exec = NULL;
280299 }
281300282301 // Check that exec succeeded
···379398380399 // Reap the child
381400 int wstatus;
382382- waitpid(shell_pid, &wstatus, WEXITED);
401401+ if (waitpid(shell_pid, &wstatus, 0) != shell_pid)
402402+ perror("waitpid");
383403 wstatus = WEXITSTATUS(wstatus);
384404385405 // Report exit code back to the client
+2-1
src/shellspawn/shellspawn.h
···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
3535+ SHELLSPAWN_GO, // execute the shell/executable now, must also contain file descriptors
3636 SHELLSPAWN_SIGNAL, // pass a signal from client
3737 SHELLSPAWN_SETUIDGID, // set virtual uid and gid
3838+ SHELLSPAWN_SETEXEC, // set the executable to spawn (instead of a shell). must be given before any ADDARG commands.
3839};
39404041struct __attribute__((packed)) shellspawn_cmd
+117-49
src/startup/darling.c
···11/*
22This file is part of Darling.
3344-Copyright (C) 2016-2020 Lubos Dolezel
44+Copyright (C) 2016-2023 Lubos Dolezel
5566Darling is free software: you can redistribute it and/or modify
77it under the terms of the GNU General Public License as published by
···202202 }
203203 else
204204 {
205205+ bool doExec = strcmp(argv[1], "exec") == 0;
206206+ int argvIndex = doExec ? 2 : 1;
207207+208208+ if (doExec && argc <= 2)
209209+ {
210210+ printf("'exec' subcommand requires a binary to execute.\n");
211211+ return 1;
212212+ }
213213+205214 char *fullPath;
206206- char *path = realpath(argv[1], NULL);
215215+ char *path = realpath(argv[argvIndex], NULL);
207216208217 if (path == NULL)
209218 {
210210- printf("'%s' is not a supported command or a file.\n", argv[1]);
219219+ printf("'%s' is not a supported command or a file.\n", argv[argvIndex]);
211220 return 1;
212221 }
213222···216225 strcat(fullPath, path);
217226 free(path);
218227219219- argv[1] = fullPath;
220220- spawnShell((const char**) &argv[1]);
228228+ argv[argvIndex] = fullPath;
229229+230230+ if (doExec)
231231+ spawnBinary(argv[argvIndex], (const char**) &argv[argvIndex]);
232232+ else
233233+ spawnShell((const char**) &argv[argvIndex]);
221234 }
222235223236 return 0;
···532545 return len;
533546}
534547535535-void spawnShell(const char** argv)
548548+int connectToShellspawn(void)
536549{
537537- size_t total_len = 0;
538538- int count;
539539- char buffer2[4096];
540540- int sockfd;
541550 struct sockaddr_un addr;
542542- char* buffer;
543543-544544- if (argv != NULL)
545545- {
546546- for (count = 0; argv[count] != NULL; count++)
547547- total_len += escapeQuotes(NULL, argv[count]);
548548-549549- buffer = malloc(total_len + count*3);
550550-551551- char *to = buffer;
552552- for (int i = 0; argv[i] != NULL; i++)
553553- {
554554- if (to != buffer)
555555- to = stpcpy(to, " ");
556556- to = stpcpy(to, "'");
557557- to += escapeQuotes(to, argv[i]);
558558- to = stpcpy(to, "'");
559559- }
560560- }
561561- else
562562- buffer = NULL;
551551+ int sockfd;
563552564553 // Connect to the shellspawn daemon in the container
565554 addr.sun_family = AF_UNIX;
···585574 exit(1);
586575 }
587576577577+ return sockfd;
578578+}
579579+580580+void setupShellspawnEnv(int sockfd)
581581+{
582582+ char buffer2[4096];
583583+588584 // Push environment variables
589585 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV,
590586 "PATH=/usr/bin:"
···610606611607 snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", login);
612608 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2);
609609+}
613610614614- // Push shell arguments
615615- if (buffer != NULL)
616616- {
617617- pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c");
618618- pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer);
619619-620620- free(buffer);
621621- }
622622-611611+void setupWorkingDir(int sockfd)
612612+{
613613+ char buffer2[4096];
623614 snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", g_workingDirectory);
624615 pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2);
616616+}
625617618618+void setupIDs(int sockfd)
619619+{
626620 int ids[2] = { g_originalUid, g_originalGid };
627621 pushShellspawnCommandData(sockfd, SHELLSPAWN_SETUIDGID, ids, sizeof(ids));
622622+}
628623629629- int fds[3], master = -1;
630630-624624+void setupFDs(int fds[3], int* master)
625625+{
626626+ *master = -1;
627627+631628 if (isatty(STDIN_FILENO))
632632- setupPtys(fds, &master);
629629+ setupPtys(fds, master);
633630 else
634634- fds[0] = dup(STDIN_FILENO); // dup() because we close() a few lines below
631631+ fds[0] = dup(STDIN_FILENO); // dup() because we close() after spawning
635632636636- if (master == -1 || !isatty(STDOUT_FILENO))
633633+ if (*master == -1 || !isatty(STDOUT_FILENO))
637634 fds[1] = STDOUT_FILENO;
638638- if (master == -1 || !isatty(STDERR_FILENO))
635635+ if (*master == -1 || !isatty(STDERR_FILENO))
639636 fds[2] = STDERR_FILENO;
637637+}
640638639639+void spawnGo(int sockfd, int fds[3], int master)
640640+{
641641 pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds);
642642 close(fds[0]);
643643···648648 close(sockfd);
649649}
650650651651+void spawnShell(const char** argv)
652652+{
653653+ size_t total_len = 0;
654654+ int count;
655655+ int sockfd;
656656+ char* buffer;
657657+ int fds[3], master;
658658+659659+ if (argv != NULL)
660660+ {
661661+ for (count = 0; argv[count] != NULL; count++)
662662+ total_len += escapeQuotes(NULL, argv[count]);
663663+664664+ buffer = malloc(total_len + count*3);
665665+666666+ char *to = buffer;
667667+ for (int i = 0; argv[i] != NULL; i++)
668668+ {
669669+ if (to != buffer)
670670+ to = stpcpy(to, " ");
671671+ to = stpcpy(to, "'");
672672+ to += escapeQuotes(to, argv[i]);
673673+ to = stpcpy(to, "'");
674674+ }
675675+ }
676676+ else
677677+ buffer = NULL;
678678+679679+ sockfd = connectToShellspawn();
680680+681681+ setupShellspawnEnv(sockfd);
682682+683683+ // Push shell arguments
684684+ if (buffer != NULL)
685685+ {
686686+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c");
687687+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer);
688688+689689+ free(buffer);
690690+ }
691691+692692+ setupWorkingDir(sockfd);
693693+ setupIDs(sockfd);
694694+ setupFDs(fds, &master);
695695+ spawnGo(sockfd, fds, master);
696696+}
697697+698698+void spawnBinary(const char* binary, const char** argv)
699699+{
700700+ int fds[3], master;
701701+ int sockfd;
702702+703703+ sockfd = connectToShellspawn();
704704+ setupShellspawnEnv(sockfd);
705705+706706+ pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary);
707707+708708+ for (; *argv != NULL; ++argv)
709709+ pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, *argv);
710710+711711+ setupWorkingDir(sockfd);
712712+ setupIDs(sockfd);
713713+ setupFDs(fds, &master);
714714+ spawnGo(sockfd, fds, master);
715715+}
716716+651717void showHelp(const char* argv0)
652718{
653719 fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n");
654654- fprintf(stderr, "Copyright (C) 2012-2020 Lubos Dolezel\n\n");
720720+ fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n\n");
655721656722 fprintf(stderr, "Usage:\n");
657657- fprintf(stderr, "\t%s program-path [arguments...]\n", argv0);
723723+ fprintf(stderr, "\t%s <program-path> [arguments...]\n", argv0);
658724 fprintf(stderr, "\t%s shell [arguments...]\n", argv0);
725725+ fprintf(stderr, "\t%s exec <program-path> [arguments...]\n", argv0);
726726+ fprintf(stderr, "\t%s shutdown\n", argv0);
659727 fprintf(stderr, "\n");
660728 fprintf(stderr, "Environment variables:\n"
661729 "DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n");
···663731664732void showVersion(const char* argv0) {
665733 fprintf(stderr, "%s " GIT_BRANCH " @ " GIT_COMMIT_HASH "\n", argv0);
666666- fprintf(stderr, "Copyright (C) 2012-2020 Lubos Dolezel\n");
734734+ fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n");
667735}
668736669737void missingSetuidRoot(void)
+7
src/startup/darling.h
···4040// Creates the given directory, exit()ing if not possible
4141void createDir(const char* path);
42424343+int connectToShellspawn(void);
4444+void setupShellspawnEnv(int shellspawnFD);
4545+void setupWorkingDir(int shellspawnFD);
4646+void setupIDs(int shellspawnFD);
4747+void setupFDs(int fds[3], int* master);
4848+void spawnGo(int shellspawnFD, int fds[3], int master);
4349void spawnShell(const char** argv);
5050+void spawnBinary(const char* binary, const char** argv);
44514552// Set up some environment variables
4653// As well as the working directory