/* * Copyright (c) 2025 Rick Macklem * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include static struct option longopts[] = { { "nofollow", no_argument, NULL, 'h' }, { "-", no_argument, NULL, '-' }, { NULL, 0, NULL, 0} }; static void usage(void) { (void)fprintf(stderr, "usage: runat [-h/--nofollow] [--] " "\n"); exit(1); } int main(int argc, char *argv[]) { int ch, file_fd, flags, i, longindex, nameddir_fd, outsiz; char *buf; long named_enabled; size_t pos, siz; bool done_args; flags = O_RDONLY | O_CLOEXEC | O_PATH; done_args = false; while (!done_args && (ch = getopt_long(argc, argv, "h-", longopts, &longindex)) != -1) switch (ch) { case 'h': flags |= O_NOFOLLOW; break; case '-': done_args = true; break; default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); named_enabled = pathconf(argv[0], _PC_NAMEDATTR_ENABLED); if (named_enabled <= 0) errx(1, "Named attributes not enabled for %s", argv[0]); /* Generate the command string for "sh". */ siz = 0; for (i = 1; i < argc; i++) siz += strlen(argv[i]) + 1; buf = malloc(siz); if (buf == NULL) errx(1, "Cannot malloc"); pos = 0; for (i = 1; i < argc; i++) { outsiz = snprintf(&buf[pos], siz, "%s ", argv[i]); if (outsiz <= 0) errx(1, "snprintf failed: returned %d", outsiz); if ((size_t)outsiz > siz) errx(1, "Arguments too large"); pos += outsiz; siz -= outsiz; } buf[pos - 1] = '\0'; file_fd = open(argv[0], flags, 0); if (file_fd < 0) err(1, "Cannot open %s", argv[0]); nameddir_fd = openat(file_fd, ".", O_RDONLY | O_CLOEXEC | O_NAMEDATTR, 0); if (nameddir_fd < 0) err(1, "Cannot open named attribute directory " "for %s", argv[0]); if (fchdir(nameddir_fd) < 0) err(1, "Cannot fchdir to named attribute dir"); execl(_PATH_BSHELL, "sh", "-c", buf, NULL); err(1, "Could not exec %s", _PATH_BSHELL); }