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 <getopt.h>
12 #include <paths.h>
13 #include <signal.h>
14 #include <stdbool.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 static struct option longopts[] = {
21 { "nofollow", no_argument, NULL, 'h' },
22 { "-", no_argument, NULL, '-' },
23 { NULL, 0, NULL, 0}
24 };
25
26 static void
usage(void)27 usage(void)
28 {
29 (void)fprintf(stderr, "usage: runat [-h/--nofollow] [--] <file> "
30 "<shell command>\n");
31 exit(1);
32 }
33
34 int
main(int argc,char * argv[])35 main(int argc, char *argv[])
36 {
37 int ch, file_fd, flags, i, longindex, nameddir_fd, outsiz;
38 char *buf;
39 long named_enabled;
40 size_t pos, siz;
41 bool done_args;
42
43 flags = O_RDONLY | O_CLOEXEC | O_PATH;
44 done_args = false;
45 while (!done_args && (ch = getopt_long(argc, argv, "h-", longopts,
46 &longindex)) != -1)
47 switch (ch) {
48 case 'h':
49 flags |= O_NOFOLLOW;
50 break;
51 case '-':
52 done_args = true;
53 break;
54 default:
55 usage();
56 }
57 argv += optind;
58 argc -= optind;
59 if (argc < 2)
60 usage();
61
62 named_enabled = pathconf(argv[0], _PC_NAMEDATTR_ENABLED);
63 if (named_enabled <= 0)
64 errx(1, "Named attributes not enabled for %s", argv[0]);
65
66 /* Generate the command string for "sh". */
67 siz = 0;
68 for (i = 1; i < argc; i++)
69 siz += strlen(argv[i]) + 1;
70 buf = malloc(siz);
71 if (buf == NULL)
72 errx(1, "Cannot malloc");
73 pos = 0;
74 for (i = 1; i < argc; i++) {
75 outsiz = snprintf(&buf[pos], siz, "%s ", argv[i]);
76 if (outsiz <= 0)
77 errx(1, "snprintf failed: returned %d", outsiz);
78 if ((size_t)outsiz > siz)
79 errx(1, "Arguments too large");
80 pos += outsiz;
81 siz -= outsiz;
82 }
83 buf[pos - 1] = '\0';
84
85 file_fd = open(argv[0], flags, 0);
86 if (file_fd < 0)
87 err(1, "Cannot open %s", argv[0]);
88 nameddir_fd = openat(file_fd, ".", O_RDONLY | O_CLOEXEC | O_NAMEDATTR,
89 0);
90 if (nameddir_fd < 0)
91 err(1, "Cannot open named attribute directory "
92 "for %s", argv[0]);
93
94 if (fchdir(nameddir_fd) < 0)
95 err(1, "Cannot fchdir to named attribute dir");
96
97 execl(_PATH_BSHELL, "sh", "-c", buf, NULL);
98 err(1, "Could not exec %s", _PATH_BSHELL);
99 }
100