/ src / startup / darling.c
darling.c
   1  /*
   2  This file is part of Darling.
   3  
   4  Copyright (C) 2016-2023 Lubos Dolezel
   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 <stdio.h>
  21  #include <sys/types.h>
  22  #include <unistd.h>
  23  #include <stdlib.h>
  24  #include <string.h>
  25  #include <alloca.h>
  26  #include <errno.h>
  27  #include <fcntl.h>
  28  #include <sys/stat.h>
  29  #include <signal.h>
  30  #include <stdbool.h>
  31  #include <sched.h>
  32  #include <sys/poll.h>
  33  #include <sys/socket.h>
  34  #include <sys/un.h>
  35  #include <getopt.h>
  36  #include <termios.h>
  37  #include <pty.h>
  38  #include <pwd.h>
  39  #include "../shellspawn/shellspawn.h"
  40  #include "darling.h"
  41  #include "darling-config.h"
  42  
  43  // Between Linux 4.9 and 4.11, a strange bug has been introduced
  44  // which prevents connecting to Unix sockets if the socket was
  45  // created in a different mount namespace or under overlayfs
  46  // (dunno which one is really responsible for this).
  47  #define USE_LINUX_4_11_HACK 1
  48  
  49  char *prefix;
  50  uid_t g_originalUid, g_originalGid;
  51  bool g_fixPermissions = false;
  52  char g_workingDirectory[4096];
  53  
  54  int main(int argc, char ** argv)
  55  {
  56  	pid_t pidInit;
  57  
  58  	if (argc <= 1)
  59  	{
  60  		showHelp(argv[0]);
  61  		return 1;
  62  	}
  63  
  64  	if (geteuid() != 0)
  65  	{
  66  		missingSetuidRoot();
  67  		return 1;
  68  	}
  69  
  70  	g_originalUid = getuid();
  71  	g_originalGid = getgid();
  72  
  73  	setuid(0);
  74  	setgid(0);
  75  
  76  	prefix = getenv("DPREFIX");
  77  	if (!prefix)
  78  		prefix = defaultPrefixPath();
  79  	if (!prefix)
  80  		return 1;
  81  	if (strlen(prefix) > 255)
  82  	{
  83  		fprintf(stderr, "Prefix path too long\n");
  84  		return 1;
  85  	}
  86  	unsetenv("DPREFIX");
  87  	getcwd(g_workingDirectory, sizeof(g_workingDirectory));
  88  
  89  	if (!checkPrefixDir())
  90  	{
  91  		setupPrefix();
  92  		g_fixPermissions = true;
  93  	}
  94  	checkPrefixOwner();
  95  
  96  	int c;
  97  	while (1)
  98  	{
  99  		static struct option long_options[] =
 100  		{
 101  			{"help", 	no_argument, 0, 0},
 102  			{"version", no_argument, 0, 0},
 103  			{0, 		0, 			 0, 0}
 104  		};
 105  		int option_index = 0;
 106  
 107  		c = getopt_long(argc, argv, "+", long_options, &option_index);
 108  
 109  		if (c == -1)
 110  		{
 111  			break;
 112  		}
 113  
 114  		switch (c)
 115  		{
 116  			case 0:
 117  			if (strcmp(long_options[option_index].name, "help") == 0)
 118  			{
 119  				showHelp(argv[0]);
 120  				exit(EXIT_SUCCESS);
 121  			}
 122  			else if (strcmp(long_options[option_index].name, "version") == 0)
 123  			{
 124  				showVersion(argv[0]);
 125  				exit(EXIT_SUCCESS);
 126  			}
 127  			break;
 128  			case '?':
 129  			break;
 130  			default:
 131  			abort();
 132  		}
 133  	}
 134  
 135  	pidInit = getInitProcess();
 136  
 137  	if (strcmp(argv[1], "shutdown") == 0)
 138  	{
 139  		if (pidInit == 0)
 140  		{
 141  			fprintf(stderr, "Darling container is not running\n");
 142  			return 1;
 143  		}
 144  
 145  		// TODO: when we have a working launchd,
 146  		// this is where we ask it to shut down nicely
 147  
 148  		char path_buf[128];
 149  		FILE* file;
 150  		pid_t launchd_pid;
 151  		snprintf(path_buf, sizeof(path_buf), "/proc/%d/task/%d/children", pidInit, pidInit);
 152  		file = fopen(path_buf, "r");
 153  		if (!file || fscanf(file, "%d", &launchd_pid) != 1) {
 154  			fprintf(stderr, "Failed to shutdown Darling container\n");
 155  			if (file) {
 156  				fclose(file);
 157  			}
 158  			return 1;
 159  		}
 160  		fclose(file);
 161  
 162  		kill(launchd_pid, SIGKILL);
 163  		kill(pidInit, SIGKILL);
 164  		return 0;
 165  	}
 166  
 167  	// If prefix's init is not running, start it up
 168  	if (pidInit == 0)
 169  	{
 170  		char socketPath[4096];
 171  		
 172  		snprintf(socketPath, sizeof(socketPath), "%s"  SHELLSPAWN_SOCKPATH, prefix);
 173  		
 174  		unlink(socketPath);
 175  		
 176  		setupWorkdir();
 177  		pidInit = spawnInitProcess();
 178  		putInitPid(pidInit);
 179  		
 180  		// Wait until shellspawn starts
 181  		for (int i = 0; i < 15; i++)
 182  		{
 183  			if (access(socketPath, F_OK) == 0)
 184  				break;
 185  			sleep(1);
 186  		}
 187  	}
 188  
 189  #if USE_LINUX_4_11_HACK
 190  	joinNamespace(pidInit, CLONE_NEWNS, "mnt");
 191  #endif
 192  
 193  	seteuid(g_originalUid);
 194  
 195  	if (strcmp(argv[1], "shell") == 0)
 196  	{
 197  		// Spawn the shell
 198  		if (argc > 2)
 199  			spawnShell((const char**) &argv[2]);
 200  		else
 201  			spawnShell(NULL);
 202  	}
 203  	else
 204  	{
 205  		bool doExec = strcmp(argv[1], "exec") == 0;
 206  		int argvIndex = doExec ? 2 : 1;
 207  
 208  		if (doExec && argc <= 2)
 209  		{
 210  			printf("'exec' subcommand requires a binary to execute.\n");
 211  			return 1;
 212  		}
 213  
 214  		char *fullPath;
 215  		char *path = realpath(argv[argvIndex], NULL);
 216  
 217  		if (path == NULL)
 218  		{
 219  			printf("'%s' is not a supported command or a file.\n", argv[argvIndex]);
 220  			return 1;
 221  		}
 222  
 223  		fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1);
 224  		strcpy(fullPath, SYSTEM_ROOT);
 225  		strcat(fullPath, path);
 226  		free(path);
 227  
 228  		argv[argvIndex] = fullPath;
 229  
 230  		if (doExec)
 231  			spawnBinary(argv[argvIndex], (const char**) &argv[argvIndex]);
 232  		else
 233  			spawnShell((const char**) &argv[argvIndex]);
 234  	}
 235  
 236  	return 0;
 237  }
 238  
 239  void joinNamespace(pid_t pid, int type, const char* typeName)
 240  {
 241  	int fdNS;
 242  	char pathNS[4096];
 243  	
 244  	snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/%s", pid, typeName);
 245  
 246  	fdNS = open(pathNS, O_RDONLY);
 247  
 248  	if (fdNS < 0)
 249  	{
 250  		fprintf(stderr, "Cannot open %s namespace file: %s\n", typeName, strerror(errno));
 251  		exit(1);
 252  	}
 253  
 254  	// Calling setns() with a PID namespace doesn't move our process into it,
 255  	// but our child process will be spawned inside the namespace
 256  	if (setns(fdNS, type) != 0)
 257  	{
 258  		fprintf(stderr, "Cannot join %s namespace: %s\n", typeName, strerror(errno));
 259  		exit(1);
 260  	}
 261  	close(fdNS);
 262  }
 263  
 264  static void pushShellspawnCommandData(int sockfd, shellspawn_cmd_type_t type, const void* data, size_t data_length)
 265  {
 266  	struct shellspawn_cmd* cmd;
 267  	size_t length;
 268  
 269  	length = sizeof(*cmd) + data_length;
 270  
 271  	cmd = (struct shellspawn_cmd*) malloc(length);
 272  	cmd->cmd = type;
 273  	cmd->data_length = data_length;
 274  
 275  	if (data != NULL)
 276  		memcpy(cmd->data, data, data_length);
 277  
 278  	if (write(sockfd, cmd, length) != length)
 279  	{
 280  		fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
 281  		exit(EXIT_FAILURE);
 282  	}
 283  
 284  	free(cmd);
 285  }
 286  
 287  static void pushShellspawnCommand(int sockfd, shellspawn_cmd_type_t type, const char* value)
 288  {
 289  	if (!value)
 290  		pushShellspawnCommandData(sockfd, type, NULL, 0);
 291  	else
 292  		pushShellspawnCommandData(sockfd, type, value, strlen(value) + 1);
 293  }
 294  
 295  static void pushShellspawnCommandFDs(int sockfd, shellspawn_cmd_type_t type, const int fds[3])
 296  {
 297  	struct shellspawn_cmd cmd;
 298  	char cmsgbuf[CMSG_SPACE(sizeof(int) * 3)];
 299  	struct msghdr msg;
 300  	struct iovec iov;
 301  	struct cmsghdr *cmptr;
 302  
 303  	cmd.cmd = type;
 304  	cmd.data_length = 0;
 305  
 306  	iov.iov_base = &cmd;
 307  
 308  	memset(&msg, 0, sizeof(msg));
 309  	msg.msg_control = cmsgbuf;
 310  	msg.msg_controllen = sizeof(cmsgbuf);
 311  
 312  	iov.iov_base = &cmd;
 313  	iov.iov_len = sizeof(cmd);
 314  	msg.msg_iov = &iov;
 315  	msg.msg_iovlen = 1;
 316  
 317  	cmptr = CMSG_FIRSTHDR(&msg);
 318  	cmptr->cmsg_len = CMSG_LEN(sizeof(int) * 3);
 319  	cmptr->cmsg_level = SOL_SOCKET;
 320  	cmptr->cmsg_type = SCM_RIGHTS;
 321  	memcpy(CMSG_DATA(cmptr), fds, sizeof(fds[0])*3);
 322  
 323  	if (sendmsg(sockfd, &msg, 0) < 0)
 324  	{
 325  		fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno));
 326  		exit(EXIT_FAILURE);
 327  	}
 328  }
 329  
 330  static int _shSockfd = -1;
 331  static struct termios orig_termios;
 332  static int pty_master;
 333  static void signalHandler(int signo)
 334  {
 335  	// printf("Received signal %d\n", signo);
 336  
 337  	// Forward window size changes
 338  	if (signo == SIGWINCH && pty_master != -1)
 339  	{
 340  		struct winsize win;
 341  
 342  		ioctl(0, TIOCGWINSZ, &win);
 343  		ioctl(pty_master, TIOCSWINSZ, &win);
 344  	}
 345  	
 346  	// Foreground process loopkup in shellspawn doesn't work
 347  	// if we're not running in TTY mode, so shellspawn falls back
 348  	// to forwarding signals to the Bash subprocess.
 349  	// 
 350  	// Hence we translate SIGINT to SIGTERM for user convenience,
 351  	// because Bash will not terminate on SIGINT.
 352  	if (pty_master == -1 && signo == SIGINT)
 353  		signo = SIGTERM;
 354  
 355  	pushShellspawnCommandData(_shSockfd, SHELLSPAWN_SIGNAL, &signo, sizeof(signo));
 356  }
 357  
 358  static void shellLoop(int sockfd, int master)
 359  {
 360  	struct sigaction sa;
 361  	struct pollfd pfds[3];
 362  	const int fdcount = (master != -1) ? 3 : 1; // do we do pty proxying?
 363  
 364  	// Vars for signal handler
 365  	_shSockfd = sockfd;
 366  	pty_master = master;
 367  
 368  	memset(&sa, 0, sizeof(sa));
 369  	sa.sa_handler = signalHandler;
 370  	sigfillset(&sa.sa_mask);
 371  
 372  	for (int i = 1; i < 32; i++)
 373  		sigaction(i, &sa, NULL);
 374  
 375  	pfds[2].fd = master;
 376  	pfds[2].events = POLLIN;
 377  	pfds[2].revents = 0;
 378  	pfds[1].fd = STDIN_FILENO;
 379  	pfds[1].events = POLLIN;
 380  	pfds[1].revents = 0;
 381  	pfds[0].fd = sockfd;
 382  	pfds[0].events = POLLIN;
 383  	pfds[0].revents = 0;
 384  
 385  	if (master != -1)
 386  		fcntl(master, F_SETFL, O_NONBLOCK);
 387  	//fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
 388  	fcntl(sockfd, F_SETFL, O_NONBLOCK);
 389  
 390  	while (1)
 391  	{
 392  		char buf[4096];
 393  
 394  		if (poll(pfds, fdcount, -1) < 0)
 395  		{
 396  			if (errno != EINTR)
 397  			{
 398  				perror("poll");
 399  				break;
 400  			}
 401  		}
 402  
 403  		if (pfds[2].revents & POLLIN)
 404  		{
 405  			int rd;
 406  			do
 407  			{
 408  				rd = read(master, buf, sizeof(buf));
 409  				if (rd > 0)
 410  					write(STDOUT_FILENO, buf, rd);
 411  			}
 412  			while (rd == sizeof(buf));
 413  		}
 414  
 415  		if (pfds[1].revents & POLLIN)
 416  		{
 417  			int rd = 0;
 418  			do
 419  			{
 420  				if (ioctl(STDIN_FILENO, FIONREAD, &rd) < 0) {
 421  					perror("ioctl");
 422  					exit(1);
 423  				}
 424  				if (rd > sizeof(buf)) {
 425  					rd = sizeof(buf);
 426  				}
 427  				rd = read(STDIN_FILENO, buf, rd);
 428  				if (rd > 0)
 429  					write(master, buf, rd);
 430  				else {
 431  					perror("read");
 432  					exit(1);
 433  				}
 434  			}
 435  			while (rd == sizeof(buf));
 436  		}
 437  
 438  		if (pfds[0].revents & (POLLHUP | POLLIN))
 439  		{
 440  			int exitStatus;
 441  			
 442  			if (read(sockfd, &exitStatus, sizeof(int)) == sizeof(int))
 443  				exit(exitStatus);
 444  			else
 445  				exit(1);
 446  		}
 447  	}
 448  }
 449  
 450  static void restoreTermios(void)
 451  {
 452  	tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
 453  }
 454  
 455  // Glibc openpty() fails for me on Debian, because grantpty() fails to chown() the pty node with error code EPERM.
 456  // This is a more lenient version of openpty() that just works.
 457  static int openpty_darling(int* amaster, int* aslave, char* name_unused, const struct termios* tos, const struct winsize* wsz)
 458  {
 459  	const char* slave_name;
 460  
 461  	*amaster = posix_openpt(O_RDWR);
 462  	if (*amaster == -1)
 463  		return -1;
 464  
 465  	grantpt(*amaster);
 466  	if (unlockpt(*amaster) < 0)
 467  		return -1;
 468  
 469  	slave_name = ptsname(*amaster);
 470  	*aslave = open(slave_name, O_RDWR | O_NOCTTY);
 471  	if (*aslave == -1)
 472  		return -1;
 473  
 474  	if (tos != NULL)
 475  		tcsetattr(*amaster, TCSANOW, tos);
 476  	if (wsz != NULL)
 477  		ioctl(*amaster, TIOCSWINSZ, wsz);
 478  
 479  	return 0;
 480  }
 481  
 482  static void setupPtys(int fds[3], int* master)
 483  {
 484  	struct winsize win;
 485  	struct termios termios;
 486  	bool tty = true;
 487  
 488  	if (tcgetattr(STDIN_FILENO, &termios) < 0)
 489  		tty = false;
 490  
 491  	if (openpty_darling(master, &fds[0], NULL, &termios, NULL) < 0)
 492  	{
 493  		perror("openpty");
 494  		exit(1);
 495  	}
 496  	fds[2] = fds[1] = fds[0];
 497  
 498  	if (tty)
 499  	{
 500  		orig_termios = termios;
 501  
 502  		ioctl(0, TIOCGWINSZ, &win);
 503  
 504  		termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
 505  		termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
 506  				INPCK | ISTRIP | IXON | PARMRK);
 507  		termios.c_oflag &= ~OPOST;
 508  		termios.c_cc[VMIN] = 1;
 509  		termios.c_cc[VTIME] = 0;
 510  
 511  		if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) < 0)
 512  		{
 513  			perror("tcsetattr");
 514  			exit(1);
 515  		}
 516  		ioctl(*master, TIOCSWINSZ, &win);
 517  
 518  		atexit(restoreTermios);
 519  	}
 520  }
 521  
 522  // Replace each quote character (') with the sequence '\''
 523  // (copying the result to dest, if non-null)
 524  // and return the length of the result.
 525  static size_t escapeQuotes(char *dest, const char *src)
 526  {
 527  	size_t len = 0;
 528  
 529  	for (; *src != 0; src++)
 530  	{
 531  		if (*src == '\'')
 532  		{
 533  			if (dest)
 534  				memcpy(&dest[len], "'\\''", 4);
 535  			len += 4;
 536  		}
 537  		else
 538  		{
 539  			if (dest)
 540  				dest[len] = *src;
 541  			len++;
 542  		}
 543  	}
 544  	if (dest)
 545  		dest[len] = 0;
 546  
 547  	return len;
 548  }
 549  
 550  int connectToShellspawn(void)
 551  {
 552  	struct sockaddr_un addr;
 553  	int sockfd;
 554  
 555  	// Connect to the shellspawn daemon in the container
 556  	addr.sun_family = AF_UNIX;
 557  #if USE_LINUX_4_11_HACK
 558  	addr.sun_path[0] = '\0';
 559  	
 560  	strcpy(addr.sun_path, prefix);
 561  	strcat(addr.sun_path, SHELLSPAWN_SOCKPATH);
 562  #else
 563  	snprintf(addr.sun_path, sizeof(addr.sun_path), "%s"  SHELLSPAWN_SOCKPATH, prefix);
 564  #endif
 565  
 566  	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
 567  	if (sockfd == -1)
 568  	{
 569  		fprintf(stderr, "Error creating a unix domain socket: %s\n", strerror(errno));
 570  		exit(1);
 571  	}
 572  
 573  	if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) == -1)
 574  	{
 575  		fprintf(stderr, "Error connecting to shellspawn in the container (%s): %s\n", addr.sun_path, strerror(errno));
 576  		exit(1);
 577  	}
 578  
 579  	return sockfd;
 580  }
 581  
 582  void setupShellspawnEnv(int sockfd)
 583  {
 584  	static const char* skip_vars[] = {
 585  		"PATH",
 586  		"TMPDIR",
 587  		"HOME",
 588  	};
 589  
 590  	char buffer2[4096];
 591  
 592  	// Push environment variables
 593  	pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, 
 594  		"PATH=/usr/bin:"
 595  		"/bin:"
 596  		"/usr/sbin:"
 597  		"/sbin:"
 598  		"/usr/local/bin");
 599  	pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, "TMPDIR=/private/tmp");
 600  
 601  	const char* login = NULL;
 602  	struct passwd* pw = getpwuid(geteuid());
 603  
 604  	if (pw != NULL)
 605  		login = pw->pw_name;
 606  
 607  	if (!login)
 608  		login = getlogin();
 609  	if (!login)
 610  	{
 611  		fprintf(stderr, "Cannot determine your user name\n");
 612  		exit(1);
 613  	}
 614  
 615  	snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", login);
 616  	pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2);
 617  
 618  	for (char** var_ptr = environ; *var_ptr != NULL; ++var_ptr) {
 619  		const char* var = *var_ptr;
 620  		const char* equal = strchr(var, '=');
 621  		size_t name_len = (equal != NULL) ? (size_t)(equal - var) : strlen(var);
 622  		bool skip_it = false;
 623  
 624  		for (size_t i = 0; i < sizeof(skip_vars) / sizeof(*skip_vars); ++i) {
 625  			if (strlen(skip_vars[i]) == name_len && strncmp(var, skip_vars[i], name_len) == 0) {
 626  				skip_it = true;
 627  				break;
 628  			}
 629  		}
 630  
 631  		if (skip_it) {
 632  			continue;
 633  		}
 634  
 635  		pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, var);
 636  	}
 637  }
 638  
 639  void setupWorkingDir(int sockfd)
 640  {
 641  	char buffer2[4096];
 642  	snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", g_workingDirectory);
 643  	pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2);
 644  }
 645  
 646  void setupIDs(int sockfd)
 647  {
 648  	int ids[2] = { g_originalUid, g_originalGid };
 649  	pushShellspawnCommandData(sockfd, SHELLSPAWN_SETUIDGID, ids, sizeof(ids));
 650  }
 651  
 652  void setupFDs(int fds[3], int* master)
 653  {
 654  	*master = -1;
 655  
 656  	if (isatty(STDIN_FILENO))
 657  		setupPtys(fds, master);
 658  	else
 659  		fds[0] = dup(STDIN_FILENO); // dup() because we close() after spawning
 660  	
 661  	if (*master == -1 || !isatty(STDOUT_FILENO))
 662  		fds[1] = STDOUT_FILENO;
 663  	if (*master == -1 || !isatty(STDERR_FILENO))
 664  		fds[2] = STDERR_FILENO;
 665  }
 666  
 667  void spawnGo(int sockfd, int fds[3], int master)
 668  {
 669  	pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds);
 670  	close(fds[0]);
 671  
 672  	shellLoop(sockfd, master);
 673  	
 674  	if (master != -1)
 675  		close(master);
 676  	close(sockfd);
 677  }
 678  
 679  void spawnShell(const char** argv)
 680  {
 681  	size_t total_len = 0;
 682  	int count;
 683  	int sockfd;
 684  	char* buffer;
 685  	int fds[3], master;
 686  
 687  	if (argv != NULL)
 688  	{
 689  		for (count = 0; argv[count] != NULL; count++)
 690  			total_len += escapeQuotes(NULL, argv[count]);
 691  
 692  		buffer = malloc(total_len + count*3);
 693  
 694  		char *to = buffer;
 695  		for (int i = 0; argv[i] != NULL; i++)
 696  		{
 697  			if (to != buffer)
 698  				to = stpcpy(to, " ");
 699  			to = stpcpy(to, "'");
 700  			to += escapeQuotes(to, argv[i]);
 701  			to = stpcpy(to, "'");
 702  		}
 703  	}
 704  	else
 705  		buffer = NULL;
 706  
 707  	sockfd = connectToShellspawn();
 708  
 709  	setupShellspawnEnv(sockfd);
 710  
 711  	// Push shell arguments
 712  	if (buffer != NULL)
 713  	{
 714  		pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c");
 715  		pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer);
 716  
 717  		free(buffer);
 718  	}
 719  
 720  	setupWorkingDir(sockfd);
 721  	setupIDs(sockfd);
 722  	setupFDs(fds, &master);
 723  	spawnGo(sockfd, fds, master);
 724  }
 725  
 726  void spawnBinary(const char* binary, const char** argv)
 727  {
 728  	int fds[3], master;
 729  	int sockfd;
 730  
 731  	sockfd = connectToShellspawn();
 732  	setupShellspawnEnv(sockfd);
 733  
 734  	pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary);
 735  
 736  	for (; *argv != NULL; ++argv)
 737  		pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, *argv);
 738  
 739  	setupWorkingDir(sockfd);
 740  	setupIDs(sockfd);
 741  	setupFDs(fds, &master);
 742  	spawnGo(sockfd, fds, master);
 743  }
 744  
 745  void showHelp(const char* argv0)
 746  {
 747  	fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n");
 748  	fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n\n");
 749  
 750  	fprintf(stderr, "Usage:\n");
 751  	fprintf(stderr, "\t%s <program-path> [arguments...]\n", argv0);
 752  	fprintf(stderr, "\t%s shell [arguments...]\n", argv0);
 753  	fprintf(stderr, "\t%s exec <program-path> [arguments...]\n", argv0);
 754  	fprintf(stderr, "\t%s shutdown\n", argv0);
 755  	fprintf(stderr, "\n");
 756  	fprintf(stderr, "Environment variables:\n"
 757  		"DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n");
 758  }
 759  
 760  void showVersion(const char* argv0) {
 761  	fprintf(stderr, "%s " GIT_BRANCH " @ " GIT_COMMIT_HASH "\n", argv0);
 762  	fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n");
 763  }
 764  
 765  void missingSetuidRoot(void)
 766  {
 767  	char path[4096];
 768  	int len;
 769  
 770  	len = readlink("/proc/self/exe", path, sizeof(path)-1);
 771  	if (len < 0)
 772  		strcpy(path, "darling");
 773  	else
 774  		path[len] = '\0';
 775  
 776  	fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path);
 777  	fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n");
 778  }
 779  
 780  pid_t spawnInitProcess(void)
 781  {
 782  	pid_t pid;
 783  	int pipefd[2];
 784  	char buffer[1];
 785  
 786  	if (pipe(pipefd) == -1)
 787  	{
 788  		fprintf(stderr, "Cannot create a pipe for synchronization: %s\n", strerror(errno));
 789  		exit(1);
 790  	}
 791  
 792  	if (unshare(CLONE_NEWUTS | CLONE_NEWIPC) != 0)
 793  	{
 794  		fprintf(stderr, "Cannot unshare UTS and IPC namespaces to create darling-init: %s\n", strerror(errno));
 795  		exit(1);
 796  	}
 797  
 798  	pid = fork();
 799  
 800  	if (pid < 0)
 801  	{
 802  		fprintf(stderr, "Cannot fork() to create darling-init: %s\n", strerror(errno));
 803  		exit(1);
 804  	}
 805  
 806  	if (pid == 0)
 807  	{
 808  		// The child
 809  
 810  		char uid_str[21];
 811  		char gid_str[21];
 812  		char pipefd_str[21];
 813  
 814  		snprintf(uid_str, sizeof(uid_str), "%d", g_originalUid);
 815  		snprintf(gid_str, sizeof(gid_str), "%d", g_originalGid);
 816  		snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]);
 817  
 818  		close(pipefd[0]);
 819  
 820  		execl(INSTALL_PREFIX "/bin/darlingserver", "darlingserver", prefix, uid_str, gid_str, pipefd_str, g_fixPermissions ? "1" : "0", NULL);
 821  
 822  		fprintf(stderr, "Failed to start darlingserver\n");
 823  		exit(1);
 824  	}
 825  
 826  	// Wait for the child to drop UID/GIDs and unshare stuff
 827  	close(pipefd[1]);
 828  	read(pipefd[0], buffer, 1);
 829  	close(pipefd[0]);
 830  
 831  	/*
 832  	snprintf(idmap, sizeof(idmap), "/proc/%d/uid_map", pid);
 833  
 834  	file = fopen(idmap, "w");
 835  	if (file != NULL)
 836  	{
 837  		fprintf(file, "0 %d 1\n", g_originalUid); // all users map to our user on the outside
 838  		fclose(file);
 839  	}
 840  	else
 841  	{
 842  		fprintf(stderr, "Cannot set uid_map for the init process: %s\n", strerror(errno));
 843  	}
 844  
 845  	snprintf(idmap, sizeof(idmap), "/proc/%d/gid_map", pid);
 846  
 847  	file = fopen(idmap, "w");
 848  	if (file != NULL)
 849  	{
 850  		fprintf(file, "0 %d 1\n", g_originalGid); // all groups map to our group on the outside
 851  		fclose(file);
 852  	}
 853  	else
 854  	{
 855  		fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno));
 856  	}
 857  	*/
 858  
 859  	// Here's where we resume the child
 860  	// if we enable user namespaces
 861  
 862  	return pid;
 863  }
 864  
 865  void putInitPid(pid_t pidInit)
 866  {
 867  	const char pidFile[] = "/.init.pid";
 868  	char* pidPath;
 869  	FILE *fp;
 870  
 871  	pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile));
 872  	strcpy(pidPath, prefix);
 873  	strcat(pidPath, pidFile);
 874  
 875  	seteuid(g_originalUid);
 876  	setegid(g_originalGid);
 877  
 878  	fp = fopen(pidPath, "w");
 879  
 880  	seteuid(0);
 881  	setegid(0);
 882  
 883  	if (fp == NULL)
 884  	{
 885  		fprintf(stderr, "Cannot write out PID of the init process: %s\n", strerror(errno));
 886  		return;
 887  	}
 888  	fprintf(fp, "%d", (int) pidInit);
 889  	fclose(fp);
 890  }
 891  
 892  char* defaultPrefixPath(void)
 893  {
 894  	const char defaultPath[] = "/.darling";
 895  	const char* home = getenv("HOME");
 896  	char* buf;
 897  
 898  	if (!home)
 899  	{
 900  		fprintf(stderr, "Cannot detect your home directory!\n");
 901  		return NULL;
 902  	}
 903  
 904  	buf = (char*) malloc(strlen(home) + sizeof(defaultPath));
 905  	strcpy(buf, home);
 906  	strcat(buf, defaultPath);
 907  
 908  	return buf;
 909  }
 910  
 911  void createDir(const char* path)
 912  {
 913  	struct stat st;
 914  
 915  	if (stat(path, &st) == 0)
 916  	{
 917  		if (!S_ISDIR(st.st_mode))
 918  		{
 919  			fprintf(stderr, "%s already exists and is a file. Remove the file.\n", path);
 920  			exit(1);
 921  		}
 922  	}
 923  	else
 924  	{
 925  		if (errno == ENOENT)
 926  		{
 927  			if (mkdir(path, 0755) != 0)
 928  			{
 929  				fprintf(stderr, "Cannot create %s: %s\n", path, strerror(errno));
 930  				exit(1);
 931  			}
 932  		}
 933  		else
 934  		{
 935  			fprintf(stderr, "Cannot access %s: %s\n", path, strerror(errno));
 936  			exit(1);
 937  		}
 938  	}
 939  }
 940  
 941  void setupWorkdir()
 942  {
 943  	char* workdir;
 944  	const char suffix[] = ".workdir";
 945  	size_t len;
 946  
 947  	len = strlen(prefix);
 948  	workdir = (char*) alloca(len + sizeof(suffix));
 949  	strcpy(workdir, prefix);
 950  
 951  	// Remove trailing /
 952  	while (workdir[len-1] == '/')
 953  		len--;
 954  	workdir[len] = '\0';
 955  
 956  	strcat(workdir, suffix);
 957  
 958  	createDir(workdir);
 959  }
 960  
 961  int checkPrefixDir()
 962  {
 963  	struct stat st;
 964  
 965  	if (stat(prefix, &st) == 0)
 966  	{
 967  		if (!S_ISDIR(st.st_mode))
 968  		{
 969  			fprintf(stderr, "%s is a file. Remove the file.\n", prefix);
 970  			exit(1);
 971  		}
 972  		return 1; // OK
 973  	}
 974  	if (errno == ENOENT)
 975  		return 0; // not found
 976  	fprintf(stderr, "Cannot access %s: %s\n", prefix, strerror(errno));
 977  	exit(1);
 978  }
 979  
 980  void setupPrefix()
 981  {
 982  	char path[4096];
 983  	size_t plen;
 984  	FILE* file;
 985  	struct passwd* passwd_entry;
 986  	
 987  	const char* dirs[] = {
 988  		"/Volumes",
 989  		"/Applications",
 990  		"/usr",
 991  		"/usr/local",
 992  		"/usr/local/share",
 993  		"/private",
 994  		"/private/var",
 995  		"/private/var/log",
 996  		"/private/var/db",
 997  		"/private/etc",
 998  		"/var",
 999  		"/var/run",
1000  		"/var/tmp",
1001  		"/var/log"
1002  	};
1003  
1004  	fprintf(stderr, "Setting up a new Darling prefix at %s\n", prefix);
1005  
1006  	seteuid(g_originalUid);
1007  	setegid(g_originalGid);
1008  
1009  	createDir(prefix);
1010  	strcpy(path, prefix);
1011  	strcat(path, "/");
1012  	plen = strlen(path);
1013  
1014  	for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
1015  	{
1016  		path[plen] = '\0';
1017  		strcat(path, dirs[i]);
1018  		createDir(path);
1019  	}
1020  
1021  	// create passwd, master.passwd, and group
1022  
1023  	passwd_entry = getpwuid(g_originalUid);
1024  	if (!passwd_entry) {
1025  		fprintf(stderr, "Failed to find Linux /etc/passwd entry for current user\n");
1026  		exit(1);
1027  	}
1028  
1029  	path[plen] = '\0';
1030  	strcat(path, "/private/etc/passwd");
1031  	file = fopen(path, "w");
1032  	if (!file) {
1033  		fprintf(stderr, "Failed to open /private/etc/passwd within the prefix\n");
1034  		exit(1);
1035  	}
1036  
1037  	fprintf(file,
1038  		"root:*:0:0:System Administrator:/var/root:/bin/sh\n"
1039  		"%s:*:%d:%d:Darling User:/Users/%s:/bin/bash\n",
1040  		passwd_entry->pw_name,
1041  		passwd_entry->pw_uid,
1042  		passwd_entry->pw_gid,
1043  		passwd_entry->pw_name
1044  	);
1045  	fclose(file);
1046  
1047  	path[plen] = '\0';
1048  	strcat(path, "/private/etc/master.passwd");
1049  	file = fopen(path, "w");
1050  	if (!file) {
1051  		fprintf(stderr, "Failed to open /private/etc/master.passwd within the prefix\n");
1052  		exit(1);
1053  	}
1054  
1055  	fprintf(file,
1056  		"root:*:0:0::0:0:System Administrator:/var/root:/bin/sh\n"
1057  		"%s:*:%d:%d::0:0:Darling User:/Users/%s:/bin/bash\n",
1058  		passwd_entry->pw_name,
1059  		passwd_entry->pw_uid,
1060  		passwd_entry->pw_gid,
1061  		passwd_entry->pw_name
1062  	);
1063  	fclose(file);
1064  
1065  	path[plen] = '\0';
1066  	strcat(path, "/private/etc/group");
1067  	file = fopen(path, "w");
1068  	if (!file) {
1069  		fprintf(stderr, "Failed to open /private/etc/group within the prefix\n");
1070  		exit(1);
1071  	}
1072  
1073  	fprintf(file,
1074  		"wheel:*:0:root,%s\n"
1075  		"%s:*:%d:%s\n",
1076  		passwd_entry->pw_name,
1077  		passwd_entry->pw_name,
1078  		passwd_entry->pw_gid,
1079  		passwd_entry->pw_name
1080  	);
1081  	fclose(file);
1082  	
1083  	seteuid(0);
1084  	setegid(0);
1085  }
1086  
1087  pid_t getInitProcess()
1088  {
1089  	const char pidFile[] = "/.init.pid";
1090  	char* pidPath;
1091  	pid_t pid;
1092  	int pid_i;
1093  	FILE *fp;
1094  	char procBuf[100];
1095  	char *exeBuf, *statusBuf;
1096  	int uidMatch = 0, gidMatch = 0;
1097  
1098  	pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile));
1099  	strcpy(pidPath, prefix);
1100  	strcat(pidPath, pidFile);
1101  
1102  	fp = fopen(pidPath, "r");
1103  	if (fp == NULL)
1104  		return 0;
1105  
1106  	if (fscanf(fp, "%d", &pid_i) != 1)
1107  	{
1108  		fclose(fp);
1109  		unlink(pidPath);
1110  		return 0;
1111  	}
1112  	fclose(fp);
1113  	pid = (pid_t) pid_i;
1114  
1115  	// Does the process exist?
1116  	if (kill(pid, 0) == -1)
1117  	{
1118  		unlink(pidPath);
1119  		return 0;
1120  	}
1121  
1122  	// Is it actually an init process?
1123  	snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid);
1124  	fp = fopen(procBuf, "r");
1125  	if (fp == NULL)
1126  	{
1127  		unlink(pidPath);
1128  		return 0;
1129  	}
1130  
1131  	if (fscanf(fp, "%ms", &exeBuf) != 1)
1132  	{
1133  		fclose(fp);
1134  		unlink(pidPath);
1135  		return 0;
1136  	}
1137  	fclose(fp);
1138  
1139  	if (strcmp(exeBuf, "darlingserver") != 0)
1140  	{
1141  		unlink(pidPath);
1142  		return 0;
1143  	}
1144  	free(exeBuf);
1145  
1146  	// Is it owned by the current user?
1147  	if (g_originalUid != 0)
1148  	{
1149  		snprintf(procBuf, sizeof(procBuf), "/proc/%d/status", pid);
1150  		fp = fopen(procBuf, "r");
1151  		if (fp == NULL)
1152  		{
1153  			unlink(pidPath);
1154  			return 0;
1155  		}
1156  
1157  		while (1)
1158  		{
1159  			statusBuf = NULL;
1160  			size_t len;
1161  			if (getline(&statusBuf, &len, fp) == -1)
1162  				break;
1163  			int rid, eid, sid, fid;
1164  			if (sscanf(statusBuf, "Uid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4)
1165  			{
1166  				uidMatch = 1;
1167  				uidMatch &= rid == g_originalUid;
1168  				uidMatch &= eid == g_originalUid;
1169  				uidMatch &= sid == g_originalUid;
1170  				uidMatch &= fid == g_originalUid;
1171  			}
1172  			if (sscanf(statusBuf, "Gid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4)
1173  			{
1174  				gidMatch = 1;
1175  				gidMatch &= rid == g_originalGid;
1176  				gidMatch &= eid == g_originalGid;
1177  				gidMatch &= sid == g_originalGid;
1178  				gidMatch &= fid == g_originalGid;
1179  			}
1180  			free(statusBuf);
1181  		}
1182  		fclose(fp);
1183  
1184  		if (!uidMatch || !gidMatch)
1185  		{
1186  			unlink(pidPath);
1187  			return 0;
1188  		}
1189  	}
1190  
1191  	return pid;
1192  }
1193  
1194  void checkPrefixOwner()
1195  {
1196  	struct stat st;
1197  
1198  	if (stat(prefix, &st) == 0)
1199  	{
1200  		if (g_originalUid != 0 && st.st_uid != g_originalUid)
1201  		{
1202  			fprintf(stderr, "You do not own the prefix directory.\n");
1203  			exit(1);
1204  		}
1205  	}
1206  	else if (errno == EACCES)
1207  	{
1208  		fprintf(stderr, "You do not own the prefix directory.\n");
1209  		exit(1);
1210  	}
1211  }