xref: /freebsd/usr.bin/runat/runat.c (revision 511de5b1430ea974b3dc6fcb2af28d2e10b2b25e)
1 /*
2  * Copyright (c) 2025 Rick Macklem
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/param.h>
8 #include <sys/wait.h>
9 #include <err.h>
10 #include <fcntl.h>
11 #include <paths.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 static void
usage(void)19 usage(void)
20 {
21 	(void)fprintf(stderr, "usage: runat <file> "
22 	    "<shell command>\n");
23 	exit(1);
24 }
25 
26 int
main(int argc,char * argv[])27 main(int argc, char *argv[])
28 {
29 	int i, file_fd, nameddir_fd, outsiz;
30 	char *buf;
31 	long named_enabled;
32 	size_t pos, siz;
33 
34 	if (argc <= 2)
35 		usage();
36 	argv++;
37 	argc--;
38 	if (argc < 2)
39 		usage();
40 
41 	named_enabled = pathconf(argv[0], _PC_NAMEDATTR_ENABLED);
42 	if (named_enabled <= 0)
43 		errx(1, "Named attributes not enabled for %s", argv[0]);
44 
45 	/* Generate the command string for "sh". */
46 	siz = 0;
47 	for (i = 1; i < argc; i++)
48 		siz += strlen(argv[i]) + 1;
49 	buf = malloc(siz);
50 	if (buf == NULL)
51 		errx(1, "Cannot malloc");
52 	pos = 0;
53 	for (i = 1; i < argc; i++) {
54 		outsiz = snprintf(&buf[pos], siz, "%s ", argv[i]);
55 		if (outsiz <= 0)
56 			errx(1, "snprintf failed: returned %d", outsiz);
57 		if ((size_t)outsiz > siz)
58 			errx(1, "Arguments too large");
59 		pos += outsiz;
60 		siz -= outsiz;
61 	}
62 	buf[pos - 1] = '\0';
63 
64 	file_fd = open(argv[0], O_RDONLY | O_CLOEXEC, 0);
65 	if (file_fd < 0)
66 		err(1, "Cannot open %s", argv[0]);
67 	nameddir_fd = openat(file_fd, ".", O_RDONLY | O_CLOEXEC | O_NAMEDATTR,
68 	    0);
69 	if (nameddir_fd < 0)
70 		err(1, "Cannot open named attribute directory "
71 		    "for %s", argv[0]);
72 
73 	if (fchdir(nameddir_fd) < 0)
74 		err(1, "Cannot fchdir to named attribute dir");
75 
76 	execl(_PATH_BSHELL, "sh", "-c", buf, NULL);
77 	err(1, "Could not exec %s", _PATH_BSHELL);
78 }
79