#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <note.h>
#include <sys/execx.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include "test_common.h"
int extra_debug = 0;
struct utsname un;
static void
forkit(char *msg, const char *expect, void (*postfn)(void))
{
int fd;
FILE *f;
pid_t pid;
char *ptr = NULL, *p;
size_t cap = 0;
int wstat;
int rv;
test_t t;
char fname[32];
(void) strcpy(fname, "/tmp/testXXXXXX");
t = test_start(msg);
fd = mkstemp(fname);
if (fd < 0) {
test_failed(t, "mkstemp failed: %s", strerror(errno));
return;
}
(void) unlink(fname);
pid = fork();
switch (pid) {
case -1:
test_failed(t, "fork failed: %s", strerror(errno));
return;
case 0:
if (dup2(fd, 1) < 0) {
test_failed(t, "dup2 failed: %s", strerror(errno));
exit(9);
}
postfn();
exit(0);
default:
break;
}
f = fdopen(fd, "r");
if (f == NULL) {
(void) close(fd);
test_failed(t, "fdopen failed: %s", strerror(errno));
(void) wait(NULL);
return;
}
if (waitpid(pid, &wstat, 0) < 0) {
test_failed(t, "wait failed: %s", strerror(errno));
(void) fclose(f);
return;
}
if (!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) {
test_failed(t, "child failed: %#x", wstat);
(void) fclose(f);
return;
}
(void) lseek(fd, 0, SEEK_SET);
if ((rv = getline(&ptr, &cap, f)) < 1) {
test_failed(t, "child gave no data: %d", rv);
(void) fclose(f);
return;
}
(void) fclose(f);
if ((p = strchr(ptr, '\n')) != NULL)
*p = '\0';
if (extra_debug)
printf("Child output: [%s]\n", ptr);
if (strcmp(ptr, expect) != 0) {
test_failed(t, "[%s] != [%s]", ptr, expect);
return;
}
(void) free(ptr);
test_passed(t);
}
static void
case_badf(void)
{
int fd = -1;
int rv;
char *args[] = { "uname", NULL };
char *env[] = { NULL };
rv = fexecve(fd, args, env);
if (rv != -1) {
(void) printf("rv is not -1\n");
(void) exit(0);
}
if (errno != EBADF) {
(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
(void) exit(0);
}
(void) printf("GOOD\n");
(void) exit(0);
}
static void
case_bad_highf(void)
{
int fd = 55;
int rv;
char *args[] = { "uname", NULL };
char *env[] = { NULL };
closefrom(3);
rv = fexecve(fd, args, env);
if (rv != -1) {
(void) printf("rv is not -1\n");
(void) exit(0);
}
if (errno != EBADF) {
(void) printf("err %d(%s) != EBADF\n", errno, strerror(errno));
(void) exit(0);
}
(void) printf("GOOD\n");
(void) exit(0);
}
static void
case_notexec(void)
{
int fd;
int rv;
char *args[] = { "uname", NULL };
char *env[] = { NULL };
fd = open("/usr/bin/uname", O_RDONLY);
rv = fexecve(fd, args, env);
if (rv != -1) {
(void) printf("rv is not -1\n");
(void) exit(0);
}
(void) printf("FAILURE\n");
(void) exit(0);
}
static void
case_cloexec(void)
{
int fd;
int rv;
char *args[] = { "ls", "-C", "/proc/self/fd", NULL };
char *env[] = { NULL };
closefrom(3);
fd = open("/usr/bin/ls", O_RDONLY | O_EXEC | O_CLOEXEC);
rv = fexecve(fd, args, env);
if (rv != -1) {
(void) printf("rv is not -1\n");
(void) exit(0);
}
(void) printf("FAILURE\n");
(void) exit(0);
}
static void
case_uname(void)
{
int fd;
char *args[] = { "uname", NULL };
char *env[] = { NULL };
fd = open("/usr/bin/uname", O_EXEC);
if (fd < 0) {
(void) printf("failed to open /usr/bin/uname: %s",
strerror(errno));
(void) exit(0);
}
(void) fexecve(fd, args, env);
(void) printf("EXEC FAILED: %s\n", strerror(errno));
(void) exit(0);
}
static void
case_uname_r(void)
{
int fd;
char *args[] = { "uname", "-r", NULL };
char *env[] = { NULL };
fd = open("/usr/bin/uname", O_EXEC);
if (fd < 0) {
(void) printf("failed to open /usr/bin/uname: %s",
strerror(errno));
(void) exit(0);
}
(void) fexecve(fd, args, env);
(void) printf("EXEC FAILED: %s\n", strerror(errno));
(void) exit(0);
}
static void
case_execvex_bad_flags(void)
{
int fd = -1;
int rv;
char *args[] = { "uname", NULL };
char *env[] = { NULL };
rv = execvex(fd, args, env, 0x1005);
if (rv != -1) {
(void) printf("rv is not -1\n");
(void) exit(0);
}
if (errno != EINVAL) {
(void) printf("err %d(%s) != EINVAL\n", errno, strerror(errno));
(void) exit(0);
}
(void) printf("GOOD\n");
(void) exit(0);
}
static void
test_fexecve_badf(void)
{
forkit("fexecve (bad FD)", "GOOD", case_badf);
}
static void
test_fexecve_bad_highf(void)
{
forkit("fexecve (bad FD)", "GOOD", case_bad_highf);
}
static void
test_fexecve_notexec(void)
{
forkit("fexecve (not O_EXEC)", un.sysname, case_notexec);
}
static void
test_fexecve_cloexec(void)
{
forkit("fexecve (O_CLOEXEC)", "0 1 2 3", case_cloexec);
}
static void
test_fexecve_uname(void)
{
forkit("fexecve (uname)", un.sysname, case_uname);
}
static void
test_fexecve_uname_r(void)
{
forkit("fexecve (uname)", un.release, case_uname_r);
}
static void
test_execvex_bad_flags(void)
{
forkit("execvex (bad flags)", "GOOD", case_execvex_bad_flags);
}
int
main(int argc, char **argv)
{
int optc;
(void) uname(&un);
while ((optc = getopt(argc, argv, "dfD")) != EOF) {
switch (optc) {
case 'd':
test_set_debug();
break;
case 'f':
test_set_force();
break;
case 'D':
test_set_debug();
extra_debug++;
break;
default:
(void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]);
exit(1);
}
}
test_fexecve_badf();
test_fexecve_bad_highf();
test_fexecve_notexec();
test_fexecve_cloexec();
test_fexecve_uname();
test_fexecve_uname_r();
test_execvex_bad_flags();
exit(0);
}