diff --git a/.gdbinit b/.gdbinit index 8c72fb990a..7280992d15 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,14 +1,3 @@ -# some generic settings -set output-radix 0x10 -set pagination off -set confirm off -set verbose off - -# make extra commands available -python import os, sys -python sys.path.append(os.path.join(os.getcwd(), 'sys')) -python import debug - # favorite set of breakpoints break kernel_init break halt diff --git a/.gdbinit-common b/.gdbinit-common new file mode 100644 index 0000000000..d828d1d1ce --- /dev/null +++ b/.gdbinit-common @@ -0,0 +1,10 @@ +# some generic settings +set output-radix 0x10 +set pagination off +set confirm off +set verbose off + +# make extra commands available +python import os, sys +python sys.path.append(os.path.join(os.getcwd(), 'sys')) +python import debug diff --git a/.gdbinit-kftrace b/.gdbinit-kftrace new file mode 100644 index 0000000000..ff72e5e08e --- /dev/null +++ b/.gdbinit-kftrace @@ -0,0 +1,29 @@ +# Command to easily append KFT events to specified file +define dump-kft + printf "Appending %d entries to file `dump.kft`\n", kft_used + append memory dump.kft kft_event_list kft_event_list+kft_used +end + +set $kft_flush_count = 0 + +break kft_flush +commands + silent + printf "kft_flush(%d): ", $kft_flush_count++ + dump-kft + continue +end + +break ktest_success +commands + printf "ktest_success: " + dump-kft +end + +break halt +commands + printf "halt: " + dump-kft +end + +continue diff --git a/.gdbinit-test b/.gdbinit-test index 95aeade0b8..5452c58180 100644 --- a/.gdbinit-test +++ b/.gdbinit-test @@ -1,15 +1,3 @@ -# some generic settings -set output-radix 0x10 -set pagination off -set confirm off -set verbose off -handle SIGINT stop - -# make extra commands available -python import os, sys -python sys.path.append(os.path.join(os.getcwd(), 'sys')) -python import debug - define post-mortem add-symbol-file bin/utest/utest.uelf 0x400000 echo \n*** REGISTERS ***\n\n diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 655be7bf42..a6c4693c68 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -29,104 +29,53 @@ jobs: - uses: actions/checkout@v3 - run: ./verify-pycodestyle.sh - build_mips: - name: Build MIPS + build: + name: Build Mimiker + strategy: + fail-fast: false + matrix: + arch: [mips, aarch64, riscv32, riscv64] + build: [kasan, kcsan] + exclude: + - arch: riscv32 + build: kcsan + - arch: riscv64 + build: kcsan runs-on: self-hosted needs: [verify_c_style, verify_py_style] steps: - uses: actions/checkout@v3 with: submodules: recursive - - run: make BOARD=malta KASAN=1 LOCKDEP=1 + - run: ./action.sh build ${{ matrix.arch }} ${{ matrix.build }} - uses: actions/upload-artifact@v3 with: - name: "mips kernel+ramdisk" + name: "${{ matrix.arch }} ${{ matrix.build }} build" path: | - sys/mimiker.elf - sys/mimiker.elf.map bin/*/*.uelf - usr.bin/*/*.uelf initrd.cpio - - build_aarch64: - name: Build AArch64 - runs-on: self-hosted - needs: [verify_c_style, verify_py_style] - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - run: make BOARD=rpi3 KASAN=1 LOCKDEP=1 - - uses: actions/upload-artifact@v3 - with: - name: "aarch64 kernel+ramdisk" - path: | - sys/mimiker.elf - sys/mimiker.img.gz - sys/mimiker.elf.map - bin/*/*.uelf - usr.bin/*/*.uelf sys/dts/*.dtb - initrd.cpio - - build_riscv32: - name: Build RISC-V 32-bit - runs-on: self-hosted - needs: [verify_c_style, verify_py_style] - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - run: make BOARD=litex-riscv KASAN=1 LOCKDEP=1 - - build_riscv64: - name: Build RISC-V 64-bit - runs-on: self-hosted - needs: [verify_c_style, verify_py_style] - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - run: make BOARD=sifive_u KASAN=1 LOCKDEP=1 - - uses: actions/upload-artifact@v3 - with: - name: "riscv64 kernel+ramdisk" - path: | sys/mimiker.elf sys/mimiker.elf.map - bin/*/*.uelf + sys/mimiker.img.gz usr.bin/*/*.uelf - initrd.cpio - - kernel_tests_mips: - name: Tests MIPS - runs-on: self-hosted - needs: build_mips - steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - with: - name: "mips kernel+ramdisk" - - run: ./run_tests.py --board malta --timeout=100 --times=50 - - kernel_tests_aarch64: - name: Tests AArch64 - runs-on: self-hosted - needs: build_aarch64 - steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - with: - name: "aarch64 kernel+ramdisk" - - run: ./run_tests.py --board rpi3 --timeout=60 --times=50 - kernel_tests_riscv64: - name: Tests RISC-V 64-bit + tests: + name: Run Tests + strategy: + fail-fast: false + matrix: + arch: [mips, aarch64, riscv64] + build: [kasan, kcsan] + suite: [kernel, user] + exclude: + - arch: riscv64 + build: kcsan runs-on: self-hosted - needs: build_riscv64 + needs: build steps: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: - name: "riscv64 kernel+ramdisk" - - run: ./run_tests.py --board sifive_u --timeout=100 --times=50 + name: "${{ matrix.arch }} ${{ matrix.build }} build" + - run: ./action.sh tests ${{ matrix.arch }} ${{ matrix.build }} ${{ matrix.suite }} diff --git a/.gitignore b/.gitignore index a5817f6060..1c96ddc975 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ mimiker-env/ # contrib/*/build/ + +# kft dump +dump.kft diff --git a/.gitmodules b/.gitmodules index 62c6b308ee..3581501714 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,6 +8,9 @@ [submodule "contrib/atto/incurses"] path = contrib/atto/incurses url = https://github.com/infinnovation-dev/incurses.git +[submodule "contrib/ubase/ubase"] + path = contrib/ubase/ubase + url = git://git.suckless.org/ubase [submodule "contrib/sbase/sbase"] path = contrib/sbase/sbase url = git://git.suckless.org/sbase diff --git a/action.sh b/action.sh new file mode 100755 index 0000000000..fc35ea781a --- /dev/null +++ b/action.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +cmd=$1 +arch=$2 +build=$3 + +shift 3 + +case $arch in + mips) board=malta ;; + aarch64) board=rpi3 ;; + riscv32) board=litex-riscv ;; + riscv64) board=sifive_u ;; + *) exit 1 ;; +esac + +case $build in + kasan) ksan=KASAN ;; + kcsan) ksan=KCSAN ;; + *) exit 1 ;; +esac + +case $cmd in + build) + make BOARD=$board $ksan=1 LOCKDEP=1 ;; + tests) + case $build in + kasan) + ./run_tests.py --board $board --timeout=100 --times=50 --suite=$1 ;; + kcsan) + ./run_tests.py --board $board --timeout=100 --times=50 --suite=$1 \ + --parallel=5 + # do not report it as failed because we have no people working on + # fixing concurrency issues + exit 0 ;; + *) + exit 1 ;; + esac ;; + *) + exit 1 ;; +esac diff --git a/bin/ksh/Makefile b/bin/ksh/Makefile index 63bccce7ce..652e6d31ad 100644 --- a/bin/ksh/Makefile +++ b/bin/ksh/Makefile @@ -14,6 +14,10 @@ clean-here: $(RM) siglist.out siglist.out.tmp $(RM) emacs.out emacs.out.tmp +install-here: + @echo "[SYMLINK] sh -> $(PROGRAM)" + ln -sf $(PROGRAM) $(SYSROOT)/$(BINDIR)/sh + # two steps to prevent the creation of a bogus siglist.out siglist.out: config.h sh.h siglist.in siglist.sh ./siglist.sh "$(CC) -E $(CPPFLAGS) $(DEFS) -I. -I$(CURDIR)" \ diff --git a/bin/utest/Makefile b/bin/utest/Makefile index 77b5f78091..12577f97d3 100644 --- a/bin/utest/Makefile +++ b/bin/utest/Makefile @@ -31,15 +31,19 @@ SOURCES = \ vfs.c \ wait.c \ vm_map.c \ - vm_map_prot.c + vm_map_prot.c \ PROGRAM = utest +CPPFLAGS += -I. EXTRAFILES = $(shell find extra -type f) INSTALL-FILES = $(EXTRAFILES:extra/%=$(SYSROOT)/%) include $(TOPDIR)/build/build.prog.mk +# Force a user not to use following symbols +LDFLAGS += --wrap=sprintf --wrap=printf --wrap=wait + $(SYSROOT)/%: extra/% @echo "[INSTALL] $(DIR)$< -> /$*" $(INSTALL) -m 644 $< $@ diff --git a/bin/utest/access.c b/bin/utest/access.c index 944387c10a..e31ff8db58 100644 --- a/bin/utest/access.c +++ b/bin/utest/access.c @@ -5,11 +5,11 @@ #include #include -TEST_ADD(access_basic) { +TEST_ADD(access_basic, 0) { /* After implementing credentials test should be extended. */ - syscall_ok(access("/bin/mandelbrot", R_OK)); - syscall_ok(access("/bin/mandelbrot", 0)); - syscall_ok(access("/bin/mandelbrot", R_OK | W_OK | X_OK)); + xaccess("/bin/mandelbrot", R_OK); + xaccess("/bin/mandelbrot", 0); + xaccess("/bin/mandelbrot", R_OK | W_OK | X_OK); syscall_fail(access("/tests/ascii", X_OK), EACCES); syscall_fail(access("/bin/mandelbrot", (R_OK | W_OK | X_OK) + 1), EINVAL); syscall_fail(access("/dont/exist", X_OK), ENOENT); diff --git a/bin/utest/cred.c b/bin/utest/cred.c index d07e1813d7..d2d425db7c 100644 --- a/bin/utest/cred.c +++ b/bin/utest/cred.c @@ -5,94 +5,94 @@ #include #include -TEST_ADD(get_set_uid) { +TEST_ADD(get_set_uid, 0) { uid_t ruid, euid, suid; /* check if nothing fail if we put wrong addresses */ syscall_fail(getresuid((void *)1, (void *)1, (void *)1), EFAULT); - syscall_ok(getresuid(&ruid, &euid, &suid)); + xgetresuid(&ruid, &euid, &suid); /* assume we are running tests as root */ assert(ruid == 0 && euid == 0 && suid == 0); /* as root we chan change id to ony other */ ruid = 1, euid = 2, suid = 3; - syscall_ok(setresuid(ruid, euid, suid)); + xsetresuid(ruid, euid, suid); - syscall_ok(getresuid(&ruid, &euid, &suid)); + xgetresuid(&ruid, &euid, &suid); assert(ruid == 1 && euid == 2 && suid == 3); /* we can only change to value that is one of real, effective or saved */ ruid = -1, euid = 3, suid = -1; - syscall_ok(setresuid(ruid, euid, suid)); + xsetresuid(ruid, euid, suid); - syscall_ok(getresuid(&ruid, &euid, &suid)); + xgetresuid(&ruid, &euid, &suid); assert(ruid == 1 && euid == 3 && suid == 3); /* we cannnot change to value that is not one of real, effective or saved */ ruid = -1, euid = 2, suid = -1; syscall_fail(setresuid(ruid, euid, suid), EPERM); - syscall_ok(getresuid(&ruid, &euid, &suid)); + xgetresuid(&ruid, &euid, &suid); assert(ruid == 1 && euid == 3 && suid == 3); return 0; } -TEST_ADD(get_set_gid) { +TEST_ADD(get_set_gid, 0) { gid_t rgid, egid, sgid; /* check if nothing fail if we put wrong addresses */ syscall_fail(getresgid((void *)1, (void *)1, (void *)1), EFAULT); - syscall_ok(getresgid(&rgid, &egid, &sgid)); + xgetresgid(&rgid, &egid, &sgid); /* assume we are running tests as root */ assert(rgid == 0 && egid == 0 && sgid == 0); /* as root we chan change id to ony other */ rgid = 1, egid = 2, sgid = 3; - syscall_ok(setresgid(rgid, egid, sgid)); + xsetresgid(rgid, egid, sgid); - syscall_ok(getresgid(&rgid, &egid, &sgid)); + xgetresgid(&rgid, &egid, &sgid); assert(rgid == 1 && egid == 2 && sgid == 3); /* dropping privileges */ - syscall_ok(setresuid(1, 1, 1)); + xsetresuid(1, 1, 1); /* we can only change to value that is one of real, effective or saved */ rgid = -1, egid = 3, sgid = -1; - syscall_ok(setresgid(rgid, egid, sgid)); + xsetresgid(rgid, egid, sgid); - syscall_ok(getresgid(&rgid, &egid, &sgid)); + xgetresgid(&rgid, &egid, &sgid); assert(rgid == 1 && egid == 3 && sgid == 3); /* we cannnot change to value that is not one of real, effective or saved */ rgid = -1, egid = 2, sgid = -1; syscall_fail(setresgid(rgid, egid, sgid), EPERM); - syscall_ok(getresgid(&rgid, &egid, &sgid)); + xgetresgid(&rgid, &egid, &sgid); assert(rgid == 1 && egid == 3 && sgid == 3); return 0; } -TEST_ADD(get_set_groups) { +TEST_ADD(get_set_groups, 0) { gid_t rgrp[NGROUPS_MAX], gidset[NGROUPS_MAX] = {0, 1, 2, 3, 4, 5}; const int ngroups = 6; uid_t euid; /* check if we are a root at start */ - syscall_ok(getresuid(NULL, &euid, NULL)); + xgetresuid(NULL, &euid, NULL); assert(euid == 0); - syscall_ok(getgroups(0, NULL)); + xgetgroups(0, NULL); /* setting too many groups */ syscall_fail(setgroups(NGROUPS_MAX + 2, gidset), EINVAL); /* setting groups (without fail now) */ - syscall_ok(setgroups(ngroups, gidset)); + xsetgroups(ngroups, gidset); assert(getgroups(NGROUPS_MAX, rgrp) == ngroups); for (int i = 0; i < ngroups; ++i) assert(rgrp[i] == gidset[i]); @@ -101,15 +101,15 @@ TEST_ADD(get_set_groups) { syscall_fail(getgroups(ngroups - 1, rgrp), EINVAL); /* dropping all supplementary groups */ - syscall_ok(setgroups(0, NULL)); + xsetgroups(0, NULL); assert(getgroups(NGROUPS_MAX, rgrp) == 0); syscall_fail(setgroups(-ngroups, gidset), EINVAL); /* setting for further tests */ - syscall_ok(setgroups(ngroups, gidset)); + xsetgroups(ngroups, gidset); /* dropping privileges */ - syscall_ok(setresuid(1, 1, 1)); + xsetresuid(1, 1, 1); /* dropping shouldn't affect supplementary groups */ assert(getgroups(NGROUPS_MAX, rgrp) == ngroups); diff --git a/bin/utest/exceptions.c b/bin/utest/exceptions.c index 6977537f4b..acae4dc465 100644 --- a/bin/utest/exceptions.c +++ b/bin/utest/exceptions.c @@ -10,7 +10,7 @@ #ifdef __mips__ -TEST_ADD(exc_cop_unusable) { +TEST_ADD(exc_cop_unusable, 0) { siginfo_t si; EXPECT_SIGNAL(SIGILL, &si) { int value; @@ -23,7 +23,7 @@ TEST_ADD(exc_cop_unusable) { return 0; } -TEST_ADD(exc_reserved_instruction) { +TEST_ADD(exc_reserved_instruction, 0) { /* The choice of reserved opcode was done based on "Table A.2 MIPS32 Encoding * of the Opcode Field" from "MIPS® Architecture For Programmers Volume II-A: * The MIPS32® Instruction Set" */ @@ -38,7 +38,7 @@ TEST_ADD(exc_reserved_instruction) { return 0; } -TEST_ADD(exc_integer_overflow) { +TEST_ADD(exc_integer_overflow, 0) { siginfo_t si; EXPECT_SIGNAL(SIGFPE, &si) { int d = __INT_MAX__; @@ -51,7 +51,7 @@ TEST_ADD(exc_integer_overflow) { return 0; } -TEST_ADD(exc_unaligned_access) { +TEST_ADD(exc_unaligned_access, 0) { siginfo_t si; EXPECT_SIGNAL(SIGBUS, &si) { int a[2]; @@ -65,7 +65,7 @@ TEST_ADD(exc_unaligned_access) { return 0; } -TEST_ADD(syscall_in_bds) { +TEST_ADD(syscall_in_bds, TF_DISABLED) { unsigned control = 1; char *text = "write executed\n"; @@ -91,7 +91,7 @@ TEST_ADD(syscall_in_bds) { #ifdef __aarch64__ -TEST_ADD(exc_unknown_instruction) { +TEST_ADD(exc_unknown_instruction, 0) { siginfo_t si; EXPECT_SIGNAL(SIGILL, &si) { start: @@ -103,7 +103,7 @@ TEST_ADD(exc_unknown_instruction) { return 0; } -TEST_ADD(exc_msr_instruction) { +TEST_ADD(exc_msr_instruction, 0) { siginfo_t si; EXPECT_SIGNAL(SIGILL, &si) { start: @@ -115,7 +115,7 @@ TEST_ADD(exc_msr_instruction) { return 0; } -TEST_ADD(exc_mrs_instruction) { +TEST_ADD(exc_mrs_instruction, 0) { siginfo_t si; EXPECT_SIGNAL(SIGILL, &si) { long x; @@ -128,7 +128,7 @@ TEST_ADD(exc_mrs_instruction) { return 0; } -TEST_ADD(exc_brk) { +TEST_ADD(exc_brk, 0) { siginfo_t si; EXPECT_SIGNAL(SIGTRAP, &si) { asm volatile("brk 0"); diff --git a/bin/utest/extra/root/.keep b/bin/utest/extra/root/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/utest/fd.c b/bin/utest/fd.c index 557893260e..ddd4fa78e6 100644 --- a/bin/utest/fd.c +++ b/bin/utest/fd.c @@ -20,7 +20,7 @@ static int n; #define FD_OFFSET 3 /* Just the basic, correct operations on a single /dev/null */ -TEST_ADD(fd_devnull) { +TEST_ADD(fd_devnull, 0) { assert_open_ok(0, "/dev/null", 0, O_RDWR); assert_read_ok(0, buf, 100); assert_write_ok(0, str, strlen(str)); @@ -30,7 +30,7 @@ TEST_ADD(fd_devnull) { /* Opens and closes multiple descriptors, checks if descriptor numbers are correctly reused */ -TEST_ADD(fd_multidesc) { +TEST_ADD(fd_multidesc, 0) { assert_open_ok(0, "/dev/null", 0, O_RDWR); assert_open_ok(1, "/dev/null", 0, O_RDWR); assert_open_ok(2, "/dev/null", 0, O_RDWR); @@ -51,7 +51,7 @@ TEST_ADD(fd_multidesc) { } /* Tests whether READ/WRITE flags are checked correctly */ -TEST_ADD(fd_readwrite) { +TEST_ADD(fd_readwrite, 0) { assert_open_ok(0, "/dev/null", 0, O_RDONLY); assert_open_ok(1, "/dev/null", 0, O_WRONLY); assert_open_ok(2, "/dev/null", 0, O_RDWR); @@ -70,7 +70,7 @@ TEST_ADD(fd_readwrite) { return 0; } -TEST_ADD(fd_read) { +TEST_ADD(fd_read, 0) { /* Read all at once */ const char *contents = "This is the content of file \"fd_test_file\" in directory \"/tests\"!"; @@ -99,7 +99,7 @@ TEST_ADD(fd_read) { } /* Try passing invalid pointers as arguments to open,read,write. */ -TEST_ADD(fd_copy) { +TEST_ADD(fd_copy, 0) { /* /dev/null does not copy any data, so passing an invalid pointer will not * cause any errors - thus we use /dev/zero for this test. However, /dev/zero * might also skip copying data, and in that case this test would also fail - @@ -123,7 +123,7 @@ TEST_ADD(fd_copy) { } /* Tries accessing some invalid descriptor numbers */ -TEST_ADD(fd_bad_desc) { +TEST_ADD(fd_bad_desc, 0) { assert_write_fail(0, buf, 100, EBADF); assert_write_fail(42, buf, 100, EBADF); assert_write_fail(-10, buf, 100, EBADF); @@ -136,7 +136,7 @@ TEST_ADD(fd_bad_desc) { return 0; } -TEST_ADD(fd_open_path) { +TEST_ADD(fd_open_path, 0) { assert_open_fail("/etc/shadow", 0, O_RDONLY, ENOENT); assert_open_fail("123456", 0, O_RDONLY, ENOENT); assert_open_fail("", 0, O_RDONLY, ENOENT); @@ -152,8 +152,8 @@ TEST_ADD(fd_open_path) { return 0; } -TEST_ADD(fd_dup) { - int x = open("/tests/dup_test_file", O_RDONLY, 0); +TEST_ADD(fd_dup, 0) { + int x = xopen("/tests/dup_test_file", O_RDONLY, 0); int y = dup(0); dup2(x, 0); char word[100]; @@ -189,7 +189,7 @@ static void _init_iovec(char *buf, struct iovec *iov, size_t *lens, int nlens) { _init_iovec(buf, iov, (size_t[]){__VA_ARGS__}, \ sizeof((size_t[]){__VA_ARGS__}) / sizeof(size_t)) -TEST_ADD(fd_readv) { +TEST_ADD(fd_readv, 0) { /* Read all at once */ const char *contents = "This is the content of file \"fd_test_file\" in directory \"/tests\"!"; @@ -238,7 +238,7 @@ TEST_ADD(fd_readv) { return 0; } -TEST_ADD(fd_writev) { +TEST_ADD(fd_writev, 0) { struct iovec iov[10]; /* Fill buf with some data. */ @@ -268,22 +268,13 @@ TEST_ADD(fd_writev) { #undef FD_OFFSET #define FD_OFFSET 0 -TEST_ADD(fd_pipe) { +TEST_ADD(fd_pipe, 0) { int fd[2]; - assert_pipe_ok(fd); + xpipe(fd); - pid_t pid = fork(); - assert(pid >= 0); + pid_t pid = xfork(); - if (pid > 0) { - /* parent */ - assert_close_ok(fd[0]); - assert_write_ok(fd[1], str, strlen(str)); - assert_close_ok(fd[1]); - - int status; - waitpid(-1, &status, 0); - } else { + if (pid == 0) { /* child */ assert_close_ok(fd[1]); assert_read_equal(fd[0], buf, str); @@ -291,10 +282,16 @@ TEST_ADD(fd_pipe) { exit(0); } + assert_close_ok(fd[0]); + assert_write_ok(fd[1], str, strlen(str)); + assert_close_ok(fd[1]); + + wait_child_finished(pid); + return 0; } -TEST_ADD(fd_all) { +TEST_ADD(fd_all, 0) { /* Call all fd-related tests one by one to see how they impact the process * file descriptor table. */ test_fd_read(); diff --git a/bin/utest/fork.c b/bin/utest/fork.c index a27ea88da8..c821f2f3de 100644 --- a/bin/utest/fork.c +++ b/bin/utest/fork.c @@ -7,57 +7,58 @@ #include #include -TEST_ADD(fork_wait) { - int n = fork(); - if (n == 0) { - printf("This is child, my pid is %d!\n", getpid()); +TEST_ADD(fork_wait, 0) { + pid_t pid = xfork(); + if (pid == 0) { + debug("This is child, my pid is %d!", getpid()); exit(42); - } else { - printf("This is parent, my pid is %d, I was told child is %d!\n", getpid(), - n); - int status, exitcode; - int p = wait(&status); - assert(WIFEXITED(status)); - exitcode = WEXITSTATUS(status); - printf("Child exit status is %d, exit code %d.\n", status, exitcode); - assert(exitcode == 42); - assert(p == n); } + + debug("This is parent, my pid is %d, I was told child is %d!", getpid(), pid); + wait_child_exited(pid, 42); return 0; } static volatile int done = 0; static void sigchld_handler(int signo) { - printf("SIGCHLD handler!\n"); + debug("SIGCHLD handler!"); int n = 0; while ((n = waitpid(-1, NULL, WNOHANG)) > 0) { - printf("Reaped a child.\n"); + debug("Reaped a child."); done = 1; } } -TEST_ADD(fork_signal) { - signal(SIGCHLD, sigchld_handler); - int n = fork(); +TEST_ADD(fork_signal, 0) { + xsignal(SIGCHLD, sigchld_handler); + + sigset_t mask, saved; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + xsigprocmask(SIG_BLOCK, &mask, &saved); + + pid_t n = xfork(); if (n == 0) exit(0); /* Wait for the child to get reaped by signal handler. */ while (!done) - sched_yield(); - signal(SIGCHLD, SIG_DFL); + sigsuspend(&saved); + + xsigprocmask(SIG_UNBLOCK, &mask, NULL); + xsignal(SIGCHLD, SIG_DFL); return 0; } -TEST_ADD(fork_sigchld_ignored) { +TEST_ADD(fork_sigchld_ignored, 0) { /* Please auto-reap my children. */ - signal(SIGCHLD, SIG_IGN); - int n = fork(); + xsignal(SIGCHLD, SIG_IGN); + pid_t n = xfork(); if (n == 0) exit(0); - /* wait() should fail, since the child reaps itself. */ - assert(wait(NULL) == -1); + /* waitpid() should fail, since the child reaps itself. */ + syscall_fail(waitpid(-1, NULL, 0), ECHILD); return 0; } diff --git a/bin/utest/fpu_ctx.c b/bin/utest/fpu_ctx.c index f8570d6e79..db3af12ea7 100644 --- a/bin/utest/fpu_ctx.c +++ b/bin/utest/fpu_ctx.c @@ -122,27 +122,27 @@ static int check_fpu_ctx(void *value) { return 0; } -TEST_ADD(fpu_gpr_preservation) { +TEST_ADD(fpu_gpr_preservation, TF_DISABLED) { int seed = 0xbeefface; for (int i = 0; i < P_PROCESSES; i++) - utest_spawn(check_fpu_ctx, (void *)seed + i); + spawn(check_fpu_ctx, (void *)seed + i); for (int i = 0; i < PROCESSES; i++) - utest_child_exited(EXIT_SUCCESS); + wait_child_finished(0); return 0; } -TEST_ADD(fpu_cpy_ctx_on_fork) { +TEST_ADD(fpu_cpy_ctx_on_fork, TF_DISABLED) { void *value = (void *)0xbeefface; MTC1_all_gpr(value); - utest_spawn(MTC1_all_gpr, (void *)0xc0de); - utest_spawn(check_fpu_all_gpr, (void *)value); + spawn(MTC1_all_gpr, (void *)0xc0de); + spawn(check_fpu_all_gpr, (void *)value); for (int i = 0; i < 2; i++) - utest_child_exited(EXIT_SUCCESS); + wait_child_finished(0); return 0; } @@ -157,12 +157,12 @@ static int check_fcsr(void *arg) { return 0; } -TEST_ADD(fpu_fcsr) { +TEST_ADD(fpu_fcsr, TF_DISABLED) { for (int i = 0; i < PROCESSES; i++) - utest_spawn(check_fcsr, (void *)i); + spawn(check_fcsr, (void *)i); for (int i = 0; i < PROCESSES; i++) - utest_child_exited(EXIT_SUCCESS); + wait_child_finished(0); return 0; } @@ -174,16 +174,16 @@ static void signal_handler_usr2(int signo) { static void signal_handler_usr1(int signo) { MTC1_all_gpr((void *)1337); - signal(SIGUSR2, signal_handler_usr2); + xsignal(SIGUSR2, signal_handler_usr2); raise(SIGUSR2); check_fpu_all_gpr((void *)1337); } -TEST_ADD(fpu_ctx_signals) { +TEST_ADD(fpu_ctx_signals, TF_DISABLED) { MTC1_all_gpr((void *)0xc0de); - signal(SIGUSR1, signal_handler_usr1); + xsignal(SIGUSR1, signal_handler_usr1); raise(SIGUSR1); check_fpu_all_gpr((void *)0xc0de); diff --git a/bin/utest/getcwd.c b/bin/utest/getcwd.c index f40e9da862..90be4099ac 100644 --- a/bin/utest/getcwd.c +++ b/bin/utest/getcwd.c @@ -5,7 +5,9 @@ #include #include -TEST_ADD(getcwd) { +TEST_ADD(getcwd, 0) { + xchdir("/"); + { /* Working directory is set to root if not changed */ char buffer[256]; diff --git a/bin/utest/lseek.c b/bin/utest/lseek.c index 69bd5e3320..ffb2fdb191 100644 --- a/bin/utest/lseek.c +++ b/bin/utest/lseek.c @@ -29,58 +29,58 @@ static void check_lseek(int fd, off_t offset, int whence, off_t expect) { assert(readchar(fd) == pos(expect)); } -TEST_ADD(lseek_basic) { +TEST_ADD(lseek_basic, 0) { int fd; - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); check_lseek(fd, 0, SEEK_SET, 0); check_lseek(fd, 42, SEEK_SET, 42); check_lseek(fd, end - 1, SEEK_SET, end - 1); - close(fd); + xclose(fd); - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); check_lseek(fd, -1, SEEK_END, end - 1); check_lseek(fd, -20, SEEK_END, end - 20); check_lseek(fd, -end, SEEK_END, 0); - close(fd); + xclose(fd); - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); check_lseek(fd, 20, SEEK_CUR, 20); check_lseek(fd, -1, SEEK_CUR, 20); check_lseek(fd, end - 22, SEEK_CUR, end - 1); check_lseek(fd, -end, SEEK_CUR, 0); - close(fd); + xclose(fd); return 0; } -TEST_ADD(lseek_errors) { +TEST_ADD(lseek_errors, 0) { int fd; - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); assert(lseek(fd, end, SEEK_SET) == end); syscall_fail(lseek(fd, end + 1, SEEK_SET), EINVAL); syscall_fail(lseek(fd, -1, SEEK_SET), EINVAL); - close(fd); + xclose(fd); - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); assert(lseek(fd, -end, SEEK_END) == 0); syscall_fail(lseek(fd, -end - 1, SEEK_END), EINVAL); syscall_fail(lseek(fd, 1, SEEK_END), EINVAL); - close(fd); + xclose(fd); - fd = open(testfile, 0, O_RDONLY); + fd = xopen(testfile, 0, O_RDONLY); assert(lseek(fd, 47, SEEK_CUR) == 47); syscall_fail(lseek(fd, -48, SEEK_CUR), EINVAL); syscall_fail(lseek(fd, 49, SEEK_CUR), EINVAL); - close(fd); + xclose(fd); /* Now let's check some weird cases. */ syscall_fail(lseek(666, 10, SEEK_CUR), EBADF); - fd = open("/dev/cons", 0, O_RDONLY); + fd = xopen("/dev/cons", 0, O_RDONLY); syscall_fail(lseek(fd, 10, SEEK_CUR), ESPIPE); - close(fd); + xclose(fd); return 0; } diff --git a/bin/utest/main.c b/bin/utest/main.c index 5d66b411a1..00836a8cff 100644 --- a/bin/utest/main.c +++ b/bin/utest/main.c @@ -1,11 +1,58 @@ #include "utest.h" +#include #include +#include #include +#include +#include #include +#include SET_DECLARE(tests, test_entry_t); +static int utest_repeat = 1; +static int utest_seed = 0; + +typedef struct job { + test_entry_t *te; + pid_t pid; + int status; +} job_t; + +static job_t *jobs = NULL; +static int njobmax = 1; +static sigset_t sigchld_mask; + +static void sigchld_handler(__unused int sig) { + int old_errno = errno; + + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + int found = 0; + + for (int j = 0; j < njobmax; j++) { + job_t *job = &jobs[j]; + + if (job->pid == pid) { + job->status = status; + found = 1; + break; + } + } + + if (!found) { + fprintf(stderr, "utest[%d]: reaped somebody's else child (pid=%d)!\n", + getpid(), pid); + exit(EXIT_FAILURE); + } + } + + errno = old_errno; +} + static test_entry_t *find_test(const char *name) { test_entry_t **ptr; SET_FOREACH (ptr, tests) { @@ -22,20 +69,217 @@ static timeval_t timestamp(void) { return (timeval_t){.tv_sec = ts.tv_sec, .tv_usec = ts.tv_nsec / 1000}; } +static int running(void) { + int pending = 0; + + for (int i = 0; i < njobmax; i++) { + job_t *job = &jobs[i]; + if (job->te == NULL) + continue; + if (job->status < 0) { + pending++; + continue; + } + if (WIFEXITED(job->status)) { + int code = WEXITSTATUS(job->status); + if (code) { + fprintf(stderr, "Test '%s' exited with %d code!\n", job->te->name, + code); + exit(EXIT_FAILURE); + } + } else if (WIFSIGNALED(job->status)) { + fprintf(stderr, "Test '%s' was terminated by %s!\n", job->te->name, + sys_signame[WTERMSIG(job->status)]); + } else { + fprintf(stderr, "Test '%s' exited with invalid status %d!\n", + job->te->name, job->status); + exit(EXIT_FAILURE); + } + job->te = NULL; + job->status = 0; + job->pid = 0; + } + + return pending; +} + +#define TMPDIR "/tmp" + +char testdir[128] = TMPDIR; + +static void testdir_cleanup(void) { + xrmdir(testdir); +} + +static void run_test(sigset_t *mask, test_entry_t *te) { + timeval_t tv = timestamp(); + const char *name = te->name; + + fprintf(stderr, "[%d.%06d] Begin '%s' test.\n", (int)tv.tv_sec, tv.tv_usec, + name); + + pid_t pid = xfork(); + if (pid == 0) { + xsignal(SIGCHLD, SIG_DFL); + setsid(); + setpgid(0, 0); + xsigprocmask(SIG_SETMASK, mask, NULL); + + if (te->flags & TF_TMPDIR) { + snprintf(testdir, sizeof(testdir), TMPDIR "/%s.%d", te->name, getpid()); + xmkdir(testdir, 0); + atexit(testdir_cleanup); + } + + xchdir(testdir); + + if (te->flags & TF_DEBUG) + __verbose = 1; + + exit(te->func()); + } + + for (int i = 0; i < njobmax; i++) { + job_t *job = &jobs[i]; + if (job->te == NULL) { + job->te = te; + job->pid = pid; + job->status = -1; + return; + } + } + + abort(); +} + +static inline int test_disabled(test_entry_t *te) { + return te->flags < 0; +} + +static int test_compare(const void *a_, const void *b_) { + const test_entry_t *a = *(test_entry_t **)a_; + const test_entry_t *b = *(test_entry_t **)b_; + return strcmp(a->name, b->name); +} + +static void select_tests(char *test_str) { + if (!strcmp(test_str, "all")) + return; + + /* Disable all tests and later enable them one by one. */ + test_entry_t **ptr; + SET_FOREACH (ptr, tests) { + (*ptr)->flags |= TF_DISABLED; + } + + char *brkt; + for (char *name = strtok_r(test_str, ",", &brkt); name; + name = strtok_r(NULL, ",", &brkt)) { + test_entry_t *te = find_test(name); + if (!te) { + fprintf(stderr, "Test '%s' not found!", name); + exit(EXIT_FAILURE); + } + te->flags &= ~TF_DISABLED; + } +} + +/* Count the number of tests that may be run in an arbitrary order. */ +static test_entry_t **collect_tests(int *countp) { + int n = 2; + int i = 0; + + test_entry_t **vector = malloc(sizeof(test_entry_t *) * n); + + test_entry_t **ptr; + SET_FOREACH (ptr, tests) { + test_entry_t *te = *ptr; + if (test_disabled(te)) { + fprintf(stderr, "Test '%s' skipped.\n", te->name); + continue; + } + + for (int r = 0; r < utest_repeat; r++) { + if (i == n - 1) { + n *= 2; + vector = realloc(vector, sizeof(test_entry_t *) * n); + } + vector[i++] = *ptr; + } + } + + vector[i] = NULL; + *countp = i; + + /* Sort tests alphabetically by name, so that shuffling may be deterministic + * and not affected by build/link order. */ + qsort(vector, i, sizeof(test_entry_t *), test_compare); + return vector; +} + int main(int argc, char **argv) { if (argc < 2) { - printf("Not enough arguments provided to utest.\n"); + fprintf(stderr, "Not enough arguments provided to utest.\n"); return 1; } - char *test_name = argv[1]; - timeval_t tv = timestamp(); - printf("[%d.%06d] Begin '%s' test.\n", (int)tv.tv_sec, tv.tv_usec, test_name); + if (getpid() == 1) { + fprintf(stderr, "Don't run me as init process!\n"); + return 1; + } + + const char *seed_str = getenv("seed"); + if (seed_str) + utest_seed = strtoul(seed_str, NULL, 10); + + const char *repeat_str = getenv("repeat"); + if (repeat_str) + utest_repeat = strtoul(repeat_str, NULL, 10); + + const char *parallel_str = getenv("parallel"); + if (parallel_str) + njobmax = strtoul(parallel_str, NULL, 10); + + fprintf(stderr, "utest[%d]: seed=%u repeat=%u parallel=%d test=%s\n", + getpid(), utest_seed, utest_repeat, njobmax, argv[1]); + + select_tests(argv[1]); + + int ntests; + test_entry_t **tests = collect_tests(&ntests); + + if (utest_seed != 0) { + /* Initialize LCG with seed.*/ + unsigned seed = utest_seed; + /* Yates-Fisher shuffle. */ + for (int i = 0; i <= ntests - 2; i++) { + int j = i + rand_r(&seed) % (ntests - i); + test_entry_t *swap = tests[i]; + tests[i] = tests[j]; + tests[j] = swap; + } + } + + jobs = calloc(sizeof(job_t), njobmax); + + sigset_t mask; + + xsignal(SIGCHLD, sigchld_handler); + sigemptyset(&sigchld_mask); + sigaddset(&sigchld_mask, SIGCHLD); + xsigprocmask(SIG_BLOCK, &sigchld_mask, &mask); + + for (int i = 0; i < ntests; i++) { + run_test(&mask, tests[i]); + + while (running() == njobmax) + sigsuspend(&mask); + } + + while (running() > 0) + sigsuspend(&mask); - test_entry_t *te = find_test(test_name); - if (te) - return te->func(); + xsigprocmask(SIG_SETMASK, &mask, NULL); - printf("No user test \"%s\" available.\n", test_name); - return 1; + return 0; } diff --git a/bin/utest/misbehave.c b/bin/utest/misbehave.c index 68d4baec2c..8e2b0ee5fc 100644 --- a/bin/utest/misbehave.c +++ b/bin/utest/misbehave.c @@ -4,7 +4,7 @@ #include #include -TEST_ADD(misbehave) { +TEST_ADD(misbehave, 0) { const char str[] = "Hello world from a user program!\n"; /* XXX: Currently kernel does not sigsegv offending programs, but in future it @@ -26,7 +26,7 @@ TEST_ADD(misbehave) { #ifdef __mips__ -TEST_ADD(exc_sigsys) { +TEST_ADD(exc_sigsys, 0) { int retval = 0; int error = 0; int sysnum = 9999; /* large enough to be never implemented */ diff --git a/bin/utest/mmap.c b/bin/utest/mmap.c index dbd9d5cb57..87480c3aa3 100644 --- a/bin/utest/mmap.c +++ b/bin/utest/mmap.c @@ -2,6 +2,7 @@ #include "util.h" #include +#include #include #include #include @@ -57,24 +58,24 @@ static void munmap_good(void) { /* mmap & munmap one page */ addr = mmap_anon_prw(NULL, 0x1000); - syscall_ok(munmap(addr, 0x1000)); + xmunmap(addr, 0x1000); /* munmapping again is no-op */ - syscall_ok(munmap(addr, 0x1000)); + xmunmap(addr, 0x1000); /* more pages */ addr = mmap_anon_prw(NULL, 0x3000); assert(addr != MAP_FAILED); - syscall_ok(munmap(addr + 0x1000, 0x1000)); - syscall_ok(munmap(addr, 0x1000)); - syscall_ok(munmap(addr + 0x2000, 0x1000)); + xmunmap(addr + 0x1000, 0x1000); + xmunmap(addr, 0x1000); + xmunmap(addr + 0x2000, 0x1000); } -TEST_ADD(munmap_sigsegv) { +TEST_ADD(munmap_sigsegv, 0) { void *addr = mmap_anon_prw(NULL, 0x4000); - syscall_ok(munmap(addr, 0x4000)); + xmunmap(addr, 0x4000); siginfo_t si; EXPECT_SIGNAL(SIGSEGV, &si) { @@ -87,7 +88,7 @@ TEST_ADD(munmap_sigsegv) { return 0; } -TEST_ADD(mmap) { +TEST_ADD(mmap, 0) { mmap_no_hint(); mmap_with_hint(); mmap_bad(); @@ -95,7 +96,7 @@ TEST_ADD(mmap) { return 0; } -TEST_ADD(munmap) { +TEST_ADD(munmap, 0) { void *addr; int child; @@ -104,29 +105,74 @@ TEST_ADD(munmap) { assert(addr != MAP_FAILED); /* write data to parts which will remain mapped after unmap */ - sprintf(addr, "first"); - sprintf(addr + 0x2000, "second"); + strcpy(addr, "first"); + strcpy(addr + 0x2000, "second"); - syscall_ok(munmap(addr + 0x1000, 0x1000)); + xmunmap(addr + 0x1000, 0x1000); /* Now we have to fork to trigger pagefault on both parts of mapped memory. */ - child = fork(); + child = xfork(); if (child == 0) { string_eq(addr, "first"); string_eq(addr + 0x2000, "second"); exit(0); } - wait_for_child_exit(child, 0); + wait_child_finished(child); - syscall_ok(munmap(addr, 0x1000)); - syscall_ok(munmap(addr + 0x2000, 0x1000)); + xmunmap(addr, 0x1000); + xmunmap(addr + 0x2000, 0x1000); + return 0; +} + +TEST_ADD(mmap_private, 0) { + size_t pgsz = getpagesize(); + + signal_setup(SIGUSR1); + + /* mmap & munmap one page */ + char *addr = mmap_anon_prw(NULL, pgsz); + assert(addr != (char *)MAP_FAILED); + + pid_t pid = xfork(); + + strcpy(addr, "parent"); + + if (pid == 0) { + /* child */ + debug("Child read: '%s'", addr); + /* Check and modify. */ + string_eq(addr, "parent"); + strcpy(addr, "child"); + debug("Child written: '%s'", addr); + + /* Wait for parent to check and modify its memory. */ + xkill(getppid(), SIGUSR1); + wait_for_signal(SIGUSR1); + + debug("Child read again: '%s'", addr); + string_eq(addr, "child"); + exit(0); + } + + /* Wait for child to check and modify its memory. */ + wait_for_signal(SIGUSR1); + + debug("Parent read: '%s'", addr); + /* Check and modify. */ + string_eq(addr, "parent"); + strcpy(addr, "parent again"); + + /* Resume child. */ + xkill(pid, SIGUSR1); + + wait_child_finished(pid); return 0; } #define NPAGES 8 -TEST_ADD(mmap_prot_none) { +TEST_ADD(mmap_prot_none, 0) { size_t pgsz = getpagesize(); size_t size = pgsz * NPAGES; volatile void *addr = mmap_anon_priv(NULL, size, PROT_NONE); @@ -145,7 +191,7 @@ TEST_ADD(mmap_prot_none) { return 0; } -TEST_ADD(mmap_prot_read) { +TEST_ADD(mmap_prot_read, 0) { size_t pgsz = getpagesize(); size_t size = pgsz * NPAGES; volatile void *addr = mmap_anon_priv(NULL, size, PROT_READ); @@ -170,12 +216,12 @@ TEST_ADD(mmap_prot_read) { return 0; } -TEST_ADD(mmap_fixed) { +TEST_ADD(mmap_fixed, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, 3 * pgsz, PROT_READ | PROT_WRITE); void *new; - syscall_ok(munmap(addr + pgsz, pgsz)); + xmunmap(addr + pgsz, pgsz); new = mmap_anon_priv_flags(addr + pgsz, pgsz, PROT_READ, MAP_FIXED); assert(new == addr + pgsz); @@ -183,7 +229,7 @@ TEST_ADD(mmap_fixed) { return 0; } -TEST_ADD(mmap_fixed_excl) { +TEST_ADD(mmap_fixed_excl, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, 2 * pgsz, PROT_READ); @@ -201,13 +247,13 @@ TEST_ADD(mmap_fixed_excl) { return 0; } -TEST_ADD(mmap_fixed_replace) { +TEST_ADD(mmap_fixed_replace, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, 2 * pgsz, PROT_READ | PROT_WRITE); void *new; - sprintf(addr, "first"); - sprintf(addr + pgsz, "second"); + strcpy(addr, "first"); + strcpy(addr + pgsz, "second"); new = mmap_anon_priv_flags(addr, pgsz, PROT_READ, MAP_FIXED); assert(new == addr); @@ -226,23 +272,21 @@ static void *prepare_rw_layout(size_t pgsz) { if (addr == NULL) return NULL; - sprintf(addr, "first"); - sprintf(addr + pgsz, "second"); - sprintf(addr + 3 * pgsz, "fourth"); - sprintf(addr + 5 * pgsz, "sixth"); - sprintf(addr + 6 * pgsz, "seventh"); + strcpy(addr, "first"); + strcpy(addr + pgsz, "second"); + strcpy(addr + 3 * pgsz, "fourth"); + strcpy(addr + 5 * pgsz, "sixth"); + strcpy(addr + 6 * pgsz, "seventh"); return addr; } /* This test unmaps entries from prepared layout (second, fourth, sixth) */ -TEST_ADD(munmap_many_1) { +TEST_ADD(munmap_many_1, 0) { size_t pgsz = getpagesize(); void *addr = prepare_rw_layout(pgsz); - int res; - res = munmap(addr + pgsz, 5 * pgsz); - syscall_ok(res); + xmunmap(addr + pgsz, 5 * pgsz); siginfo_t si; EXPECT_SIGNAL(SIGSEGV, &si) { @@ -270,13 +314,11 @@ TEST_ADD(munmap_many_1) { } /* This test unmaps all entries from prepared layout */ -TEST_ADD(munmap_many_2) { +TEST_ADD(munmap_many_2, 0) { size_t pgsz = getpagesize(); void *addr = prepare_rw_layout(pgsz); - int res; - res = munmap(addr, 7 * pgsz); - syscall_ok(res); + xmunmap(addr, 7 * pgsz); siginfo_t si; EXPECT_SIGNAL(SIGSEGV, &si) { @@ -313,7 +355,7 @@ TEST_ADD(munmap_many_2) { } /* This test unmaps entries from prepared layout (second, fourth, sixth) */ -TEST_ADD(mmap_fixed_replace_many_1) { +TEST_ADD(mmap_fixed_replace_many_1, 0) { size_t pgsz = getpagesize(); void *addr = prepare_rw_layout(pgsz); void *new; @@ -332,7 +374,7 @@ TEST_ADD(mmap_fixed_replace_many_1) { } /* This test unmaps all entries from prepared layout */ -TEST_ADD(mmap_fixed_replace_many_2) { +TEST_ADD(mmap_fixed_replace_many_2, 0) { size_t pgsz = getpagesize(); void *addr = prepare_rw_layout(pgsz); void *new; diff --git a/bin/utest/mprotect.c b/bin/utest/mprotect.c index 4a99c8b91e..6a71474ccc 100644 --- a/bin/utest/mprotect.c +++ b/bin/utest/mprotect.c @@ -12,30 +12,24 @@ #include #define check_write_ok(addr) \ - { *((char *)(addr)) = 'x'; } + { *((volatile char *)(addr)) = 'x'; } #define check_write_err(si, addr) \ { \ EXPECT_SIGNAL(SIGSEGV, &(si)) { \ - *((char *)(addr)) = 'x'; \ + *((volatile char *)(addr)) = 'x'; \ } \ CLEANUP_SIGNAL(); \ CHECK_SIGSEGV(&(si), (addr), SEGV_ACCERR); \ } #define check_read_ok(addr) \ - { \ - char v = *((char *)(addr)); \ - if (v) \ - printf("\n"); \ - } + { *((volatile char *)(addr)); } #define check_read_err(si, addr) \ { \ EXPECT_SIGNAL(SIGSEGV, &(si)) { \ - char v = *((char *)(addr)); \ - if (v) \ - printf("\n"); \ + *((volatile char *)(addr)); \ } \ CLEANUP_SIGNAL(); \ CHECK_SIGSEGV(&(si), (addr), SEGV_ACCERR); \ @@ -82,7 +76,7 @@ static void memcpy_fun(void *addr) { memcpy(addr, func_inc, len); } -TEST_ADD(mprotect_fail) { +TEST_ADD(mprotect_fail, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, pgsz, PROT_NONE); siginfo_t si; @@ -105,19 +99,19 @@ TEST_ADD(mprotect_fail) { /* len must be nonzero */ syscall_fail(mprotect(addr, 0, PROT_READ), EINVAL); - syscall_ok(munmap(addr, pgsz)); + xmunmap(addr, pgsz); return 0; } -TEST_ADD(mprotect1) { +TEST_ADD(mprotect1, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, pgsz, PROT_NONE); siginfo_t si; check_none_prot(si, addr); - syscall_ok(mprotect(addr, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC)); + xmprotect(addr, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC); /* Copy function to address that won't be overritten by checks. */ void *fun_addr = addr + 4; @@ -127,25 +121,25 @@ TEST_ADD(mprotect1) { check_write_ok(addr); check_exec_ok(fun_addr); - syscall_ok(mprotect(addr, pgsz, PROT_READ | PROT_WRITE)); + xmprotect(addr, pgsz, PROT_READ | PROT_WRITE); check_read_ok(addr); check_write_ok(addr); check_exec_err(si, fun_addr); - syscall_ok(mprotect(addr, pgsz, PROT_READ | PROT_EXEC)); + xmprotect(addr, pgsz, PROT_READ | PROT_EXEC); check_read_ok(addr); check_write_err(si, addr); check_exec_ok(fun_addr); - syscall_ok(mprotect(addr, pgsz, PROT_READ)); + xmprotect(addr, pgsz, PROT_READ); check_read_ok(addr); check_write_err(si, addr); check_exec_err(si, fun_addr); - syscall_ok(mprotect(addr, pgsz, PROT_NONE)); + xmprotect(addr, pgsz, PROT_NONE); check_none_prot(si, addr); @@ -171,16 +165,16 @@ static void *prepare_none_layout(size_t pgsz) { /* Change only some entries from prepared layout and check if protection * was changed correctly. */ -TEST_ADD(mprotect2) { +TEST_ADD(mprotect2, 0) { size_t pgsz = getpagesize(); void *addr = prepare_none_layout(pgsz); siginfo_t si; syscall_fail(mprotect(addr + pgsz, 5 * pgsz, PROT_READ | PROT_WRITE), ENOMEM); - syscall_ok(mprotect(addr + pgsz, pgsz, PROT_READ | PROT_WRITE)); - syscall_ok(mprotect(addr + 3 * pgsz, pgsz, PROT_READ | PROT_WRITE)); - syscall_ok(mprotect(addr + 5 * pgsz, pgsz, PROT_READ | PROT_WRITE)); + xmprotect(addr + pgsz, pgsz, PROT_READ | PROT_WRITE); + xmprotect(addr + 3 * pgsz, pgsz, PROT_READ | PROT_WRITE); + xmprotect(addr + 5 * pgsz, pgsz, PROT_READ | PROT_WRITE); check_none_prot(si, addr); @@ -204,8 +198,8 @@ TEST_ADD(mprotect2) { syscall_fail(mprotect(addr + pgsz, 3 * pgsz, PROT_READ), ENOMEM); - syscall_ok(mprotect(addr + pgsz, pgsz, PROT_READ)); - syscall_ok(mprotect(addr + 3 * pgsz, pgsz, PROT_READ)); + xmprotect(addr + pgsz, pgsz, PROT_READ); + xmprotect(addr + 3 * pgsz, pgsz, PROT_READ); check_none_prot(si, addr); @@ -224,8 +218,7 @@ TEST_ADD(mprotect2) { check_none_prot(si, addr + 6 * pgsz); /* TODO: change to READ | WRITE */ - syscall_ok( - mprotect(addr + 5 * pgsz, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC)); + xmprotect(addr + 5 * pgsz, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC); check_none_prot(si, addr); @@ -243,8 +236,8 @@ TEST_ADD(mprotect2) { check_none_prot(si, addr + 6 * pgsz); - syscall_ok(mprotect(addr + pgsz, pgsz, PROT_READ | PROT_WRITE)); - syscall_ok(mprotect(addr + 3 * pgsz, pgsz, PROT_READ | PROT_WRITE)); + xmprotect(addr + pgsz, pgsz, PROT_READ | PROT_WRITE); + xmprotect(addr + 3 * pgsz, pgsz, PROT_READ | PROT_WRITE); check_none_prot(si, addr); @@ -264,9 +257,9 @@ TEST_ADD(mprotect2) { syscall_fail(mprotect(addr + pgsz, 5 * pgsz, PROT_NONE), ENOMEM); - syscall_ok(mprotect(addr + pgsz, pgsz, PROT_NONE)); - syscall_ok(mprotect(addr + 3 * pgsz, pgsz, PROT_NONE)); - syscall_ok(mprotect(addr + 5 * pgsz, pgsz, PROT_NONE)); + xmprotect(addr + pgsz, pgsz, PROT_NONE); + xmprotect(addr + 3 * pgsz, pgsz, PROT_NONE); + xmprotect(addr + 5 * pgsz, pgsz, PROT_NONE); check_none_prot(si, addr); check_none_prot(si, addr + pgsz); @@ -280,7 +273,7 @@ TEST_ADD(mprotect2) { /* Change protection for page inside given memory range. This will trigger * vm_map_entry splitting. */ -TEST_ADD(mprotect3) { +TEST_ADD(mprotect3, 0) { size_t pgsz = getpagesize(); void *addr = mmap_anon_priv(NULL, 4 * pgsz, PROT_NONE); siginfo_t si; @@ -290,7 +283,7 @@ TEST_ADD(mprotect3) { check_none_prot(si, addr + 2 * pgsz); check_none_prot(si, addr + 3 * pgsz); - syscall_ok(mprotect(addr + pgsz, 2 * pgsz, PROT_READ | PROT_WRITE)); + xmprotect(addr + pgsz, 2 * pgsz, PROT_READ | PROT_WRITE); check_none_prot(si, addr); @@ -304,7 +297,7 @@ TEST_ADD(mprotect3) { check_none_prot(si, addr + 3 * pgsz); - syscall_ok(mprotect(addr + pgsz, pgsz, PROT_READ)); + xmprotect(addr + pgsz, pgsz, PROT_READ); check_none_prot(si, addr); @@ -318,7 +311,7 @@ TEST_ADD(mprotect3) { check_none_prot(si, addr + 3 * pgsz); - syscall_ok(mprotect(addr + pgsz, 2 * pgsz, PROT_NONE)); + xmprotect(addr + pgsz, 2 * pgsz, PROT_NONE); check_none_prot(si, addr); check_none_prot(si, addr + pgsz); diff --git a/bin/utest/pgrp.c b/bin/utest/pgrp.c index 2aab678f69..64cb245291 100644 --- a/bin/utest/pgrp.c +++ b/bin/utest/pgrp.c @@ -10,10 +10,10 @@ #include #include -TEST_ADD(setpgid) { +TEST_ADD(setpgid, 0) { pgid_t parent_pgid = getpgid(0); - pid_t children_pid = fork(); + pid_t children_pid = xfork(); if (children_pid == 0) { /* Process inherits group of its parent. */ assert(getpgid(0) == parent_pgid); @@ -24,7 +24,7 @@ TEST_ADD(setpgid) { exit(0); } - wait_for_child_exit(children_pid, 0); + wait_child_finished(children_pid); /* It is forbidden to move the process to non-existing group. */ assert(setpgid(0, children_pid)); @@ -41,66 +41,71 @@ static void sa_handler(int signo) { sig_delivered = 1; } -TEST_ADD(setpgid_leader) { +TEST_ADD(setpgid_leader, 0) { signal_setup(SIGUSR1); - pid_t cpid = fork(); + pid_t cpid = xfork(); if (cpid == 0) { /* Become session leader. */ assert(setsid() == getpid()); /* Can't change pgrp of session leader. */ assert(setpgid(0, 0)); + xkill(getppid(), SIGUSR1); wait_for_signal(SIGUSR1); return 0; } /* Wait until child becomes session leader. */ - while (getsid(cpid) != cpid) - sched_yield(); + wait_for_signal(SIGUSR1); + assert(getsid(cpid) == cpid); /* Can't change pgrp of session leader. */ assert(setpgid(cpid, getpgid(0))); - kill(cpid, SIGUSR1); + xkill(cpid, SIGUSR1); - wait_for_child_exit(cpid, 0); + wait_child_finished(cpid); return 0; } -TEST_ADD(setpgid_child) { - signal_setup(SIGUSR1); +TEST_ADD(setpgid_child, 0) { + signal_setup(SIGUSR1); /* child 1 */ + signal_setup(SIGUSR2); /* child 2 */ - pid_t cpid1 = fork(); + pid_t cpid1 = xfork(); if (cpid1 == 0) { /* Become session leader. */ assert(setsid() == getpid()); + /* Signal readiness to parent. */ + xkill(getppid(), SIGUSR1); + /* Wait until our parent gives a signal to exit. */ wait_for_signal(SIGUSR1); return 0; } - pid_t cpid2 = fork(); + pid_t cpid2 = xfork(); if (cpid2 == 0) { /* Signal readiness to parent. */ - kill(getppid(), SIGUSR1); + xkill(getppid(), SIGUSR2); /* A child should not be able to change its parent's * process group. */ assert(setpgid(getppid(), 0)); /* Wait until our parent gives a signal to exit. */ - wait_for_signal(SIGUSR1); + wait_for_signal(SIGUSR2); return 0; } /* Wait for child 1 to become session leader. */ - while (getsid(cpid1) != cpid1) - sched_yield(); + wait_for_signal(SIGUSR1); + assert(getsid(cpid1) == cpid1); /* Wait until child 2 is ready. */ - wait_for_signal(SIGUSR1); + wait_for_signal(SIGUSR2); /* Move child 2 into its own process group. */ assert(!setpgid(cpid2, cpid2)); @@ -114,11 +119,11 @@ TEST_ADD(setpgid_child) { assert(setpgid(cpid2, cpid1)); assert(getpgid(cpid2) == getpgid(0)); - kill(cpid1, SIGUSR1); - kill(cpid2, SIGUSR1); + xkill(cpid1, SIGUSR1); + xkill(cpid2, SIGUSR2); - wait_for_child_exit(cpid1, 0); - wait_for_child_exit(cpid2, 0); + wait_child_finished(cpid1); + wait_child_finished(cpid2); return 0; } @@ -134,20 +139,20 @@ static void kill_tests_setup(void) { } /* In this test child process sends signal to its parent. */ -TEST_ADD(kill) { +TEST_ADD(kill, 0) { kill_tests_setup(); pgid_t parent_pid = getpid(); - pid_t pid = fork(); + pid_t pid = xfork(); if (pid == 0) { - kill(parent_pid, SIGUSR1); + xkill(parent_pid, SIGUSR1); /* Signal is not delivered to all processes in the group. */ assert(!sig_delivered); exit(0); } - wait_for_child_exit(pid, 0); + wait_child_finished(pid); /* Signal is delivered to appropriate process. */ assert(sig_delivered); @@ -157,34 +162,34 @@ TEST_ADD(kill) { /* In this tests there are two processes marked with: a, b. * Processes a and b are in the same process group. * Process b sends signal to its own process group containing a and b. */ -TEST_ADD(killpg_same_group) { +TEST_ADD(killpg_same_group, 0) { kill_tests_setup(); - pid_t pid_a = fork(); + pid_t pid_a = xfork(); if (pid_a == 0) { setpgid(0, 0); pid_t pgid_a = getpgid(0); - pid_t pid_b = fork(); + pid_t pid_b = xfork(); if (pid_b == 0) { - assert(!killpg(pgid_a, SIGUSR1)); + xkillpg(pgid_a, SIGUSR1); /* Process b should receive signal from process b. */ assert(sig_delivered); exit(0); // process b } - wait_for_child_exit(pid_b, 0); + wait_child_finished(pid_b); /* Process a should receive signal from process b. */ assert(sig_delivered); exit(0); // process a } - wait_for_child_exit(pid_a, 0); + wait_child_finished(pid_a); /* Invalid argument. */ - assert(killpg(1, SIGUSR1)); + syscall_fail(killpg(1, SIGUSR1), ESRCH); /* Invalid argument (negative number). */ - assert(killpg(-1, SIGUSR1)); + syscall_fail(killpg(-1, SIGUSR1), ESRCH); return 0; } @@ -193,56 +198,55 @@ TEST_ADD(killpg_same_group) { * Processes a and b are in the same process group. * Process c is in different process group than a and b. * Process c sends signal to the process group containing a and b. */ -TEST_ADD(killpg_other_group) { +TEST_ADD(killpg_other_group, 0) { kill_tests_setup(); - pid_t pid_a = fork(); + pid_t pid_a = xfork(); if (pid_a == 0) { setpgid(0, 0); pid_t pgid_a = getpgid(0); - pid_t pid_b = fork(); + pid_t pid_b = xfork(); if (pid_b == 0) { - pid_t pid_c = fork(); + pid_t pid_c = xfork(); if (pid_c == 0) { setpgid(0, 0); - assert(!killpg(pgid_a, SIGUSR1)); + xkillpg(pgid_a, SIGUSR1); /* Process c should not receive signal from process c. */ assert(!sig_delivered); exit(0); // process c } - wait_for_child_exit(pid_c, 0); + wait_child_finished(pid_c); /* Process b should receive signal from process c. */ assert(sig_delivered); exit(0); // process b } - wait_for_child_exit(pid_b, 0); + wait_child_finished(pid_b); /* Process a should receive signal from process c. */ assert(sig_delivered); exit(0); // process a } - wait_for_child_exit(pid_a, 0); + wait_child_finished(pid_a); /* It is forbidden to send signal to non-existing group. */ - assert(killpg(pid_a, SIGUSR1)); + syscall_fail(killpg(pid_a, SIGUSR1), ESRCH); return 0; } -TEST_ADD(pgrp_orphan) { +TEST_ADD(pgrp_orphan, 0) { signal_setup(SIGHUP); int ppid = getpid(); - pid_t cpid = fork(); - int status; + pid_t cpid = xfork(); if (cpid == 0) { cpid = getpid(); assert(setsid() == cpid); - pid_t gcpid = fork(); + pid_t gcpid = xfork(); if (gcpid == 0) { gcpid = getpid(); @@ -250,14 +254,13 @@ TEST_ADD(pgrp_orphan) { raise(SIGSTOP); wait_for_signal(SIGHUP); - kill(ppid, SIGHUP); + xkill(ppid, SIGHUP); return 0; } /* Wait for the grandchild to stop, then orphan its process group. */ - printf("Child: waiting for the grandchild to stop...\n"); - assert(waitpid(gcpid, &status, WUNTRACED) == gcpid); - assert(WIFSTOPPED(status)); + debug("Child: waiting for the grandchild to stop..."); + wait_child_stopped(gcpid); /* When we exit, init will become the grandchild's parent. * Since init is in a different session, and the grandchild will * be the only member of its own process group, the grandchild's @@ -267,11 +270,11 @@ TEST_ADD(pgrp_orphan) { } /* Reap the child. */ - printf("Parent: waiting for the child to exit...\n"); - wait_for_child_exit(cpid, 0); + debug("Parent: waiting for the child to exit..."); + wait_child_finished(cpid); /* Wait for a signal from the grandchild. */ - printf("Parent: waiting for a signal from the grandchild...\n"); + debug("Parent: waiting for a signal from the grandchild..."); wait_for_signal(SIGHUP); /* We're exiting without reaping the grandchild. @@ -279,13 +282,11 @@ TEST_ADD(pgrp_orphan) { return 0; } -static volatile pid_t parent_sid; - -TEST_ADD(session_basic) { +TEST_ADD(session_basic, 0) { signal_setup(SIGUSR1); - parent_sid = getsid(getpid()); + pid_t parent_sid = getsid(getpid()); assert(parent_sid != -1); - pid_t cpid = fork(); + pid_t cpid = xfork(); if (cpid == 0) { cpid = getpid(); pid_t ppid = getppid(); @@ -294,6 +295,7 @@ TEST_ADD(session_basic) { assert(getsid(ppid) == parent_sid); /* Create a session. This should always succeed. */ assert(setsid() == cpid); + xkill(ppid, SIGUSR1); assert(getsid(0) == cpid); assert(getsid(ppid) == parent_sid); /* Creating a session when we're already a leader should fail. */ @@ -305,18 +307,15 @@ TEST_ADD(session_basic) { return 0; } - pid_t child_sid; - while ((child_sid = getsid(cpid)) != cpid) { - assert(child_sid == parent_sid); - sched_yield(); - } + wait_for_signal(SIGUSR1); + assert(getsid(cpid) == cpid); - kill(cpid, SIGUSR1); - wait_for_child_exit(cpid, 0); + xkill(cpid, SIGUSR1); + wait_child_finished(cpid); return 0; } -TEST_ADD(session_login_name) { +TEST_ADD(session_login_name, 0) { const char *name = "foo"; /* Assume login name is not set. */ assert(getlogin() == NULL); diff --git a/bin/utest/pipe.c b/bin/utest/pipe.c index 0fa0147205..c032b0710f 100644 --- a/bin/utest/pipe.c +++ b/bin/utest/pipe.c @@ -20,41 +20,38 @@ static void sigpipe_handler(int signo) { } } -TEST_ADD(pipe_parent_signaled) { +TEST_ADD(pipe_parent_signaled, 0) { int pipe_fd[2]; signal_delivered = 0; - signal(SIGPIPE, sigpipe_handler); + xsignal(SIGPIPE, sigpipe_handler); /* creating pipe */ int pipe2_ret = pipe2(pipe_fd, 0); assert(pipe2_ret == 0); /* forking */ - pid_t child_pid = fork(); - assert(child_pid >= 0); + pid_t child_pid = xfork(); if (child_pid == 0) { /* child */ - close(pipe_fd[1]); /* closing write end of pipe */ - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ exit(EXIT_SUCCESS); } /* parent */ - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ /* Sync with end of child execution */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); + wait_child_finished(child_pid); /* This is supposed to trigger SIGPIPE and return EPIPE */ - ssize_t write_ret = write(pipe_fd[1], "hello world\n", 12); - assert(write_ret == -1); - assert(errno == EPIPE); + syscall_fail(write(pipe_fd[1], "hello world\n", 12), EPIPE); assert(signal_delivered); return 0; } -TEST_ADD(pipe_child_signaled) { +TEST_ADD(pipe_child_signaled, 0) { int pipe_fd[2]; signal_delivered = 0; @@ -66,46 +63,34 @@ TEST_ADD(pipe_child_signaled) { assert(pipe2_ret == 0); /* forking */ - pid_t child_pid = fork(); - assert(child_pid >= 0); + pid_t child_pid = xfork(); if (child_pid == 0) { /* child */ - signal(SIGPIPE, sigpipe_handler); + xsignal(SIGPIPE, sigpipe_handler); - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ wait_for_signal(SIGUSR1); /* now we know that other end is closed */ /* This is supposed to trigger SIGPIPE and return EPIPE */ - ssize_t write_ret = write(pipe_fd[1], "hello world\n", 12); - assert(write_ret == -1); - assert(errno == EPIPE); + syscall_fail(write(pipe_fd[1], "hello world\n", 12), EPIPE); assert(signal_delivered); exit(EXIT_SUCCESS); } /* parent */ - close(pipe_fd[1]); /* closing write end of pipe */ - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ /* send SIGUSR1 informing that parent closed both ends of pipe */ - kill(child_pid, SIGUSR1); + xkill(child_pid, SIGUSR1); - /* I really want child to finish, not just change it's state. - * so i don't use wait_for_child_exit - */ - int wstatus = 1; - do { - ssize_t waitpid_ret = waitpid(child_pid, &wstatus, 0); - assert(waitpid_ret == child_pid); - } while (!WIFEXITED(wstatus)); - - assert(WEXITSTATUS(wstatus) == EXIT_SUCCESS); + wait_child_finished(child_pid); return 0; } -TEST_ADD(pipe_blocking_flag_manipulation) { +TEST_ADD(pipe_blocking_flag_manipulation, 0) { int pipe_fd[2]; /* creating pipe */ @@ -136,13 +121,13 @@ TEST_ADD(pipe_blocking_flag_manipulation) { is_flag_not_set = fcntl(pipe_fd[1], F_GETFL) & O_NONBLOCK; assert(!is_flag_not_set); - close(pipe_fd[0]); - close(pipe_fd[1]); + xclose(pipe_fd[0]); + xclose(pipe_fd[1]); return 0; } -TEST_ADD(pipe_write_interruptible_sleep) { +TEST_ADD(pipe_write_interruptible_sleep, 0) { int pipe_fd[2]; pid_t child_pid; @@ -151,11 +136,10 @@ TEST_ADD(pipe_write_interruptible_sleep) { assert(pipe2_ret == 0); /* forking */ - child_pid = fork(); - assert(child_pid >= 0); + child_pid = xfork(); if (child_pid == 0) { /* child */ - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ struct sigaction sa = { .sa_handler = sigpipe_handler, @@ -170,27 +154,27 @@ TEST_ADD(pipe_write_interruptible_sleep) { for (int i = 0; i < page_size; i++) { data[i] = (i + '0') % CHAR_MAX; } - int bytes_wrote = 0; ualarm(5000, 5000); /* 5 ms, and after that every 5 ms */ + int bytes_wrote = 0; while (bytes_wrote >= 0) { bytes_wrote = write(pipe_fd[1], &data, sizeof(data)); } assert(bytes_wrote == -1); assert(errno == EINTR); - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ free(data); exit(EXIT_SUCCESS); } - close(pipe_fd[1]); /* closing write end of pipe */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ + wait_child_finished(child_pid); + xclose(pipe_fd[0]); /* closing read end of pipe */ return 0; } -TEST_ADD(pipe_write_errno_eagain) { +TEST_ADD(pipe_write_errno_eagain, 0) { int pipe_fd[2]; pid_t child_pid; int bytes_wrote = 0; @@ -200,11 +184,10 @@ TEST_ADD(pipe_write_errno_eagain) { assert(pipe2_ret == 0); /* forking */ - child_pid = fork(); - assert(child_pid >= 0); + child_pid = xfork(); if (child_pid == 0) { - close(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ int page_size = getpagesize(); /* prepare varying data */ @@ -221,32 +204,29 @@ TEST_ADD(pipe_write_errno_eagain) { assert(bytes_wrote == -1); assert(errno == EAGAIN); - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ free(data); exit(EXIT_SUCCESS); } - close(pipe_fd[1]); /* closing write end of pipe */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); - close(pipe_fd[0]); + xclose(pipe_fd[1]); /* closing write end of pipe */ + wait_child_finished(child_pid); + xclose(pipe_fd[0]); return 0; } -TEST_ADD(pipe_read_interruptible_sleep) { +TEST_ADD(pipe_read_interruptible_sleep, 0) { int pipe_fd[2]; - pid_t child_pid; - int bytes_wrote; /* creating pipe */ int pipe2_ret = pipe2(pipe_fd, 0); assert(pipe2_ret == 0); /* forking */ - child_pid = fork(); - assert(child_pid >= 0); + pid_t child_pid = xfork(); if (child_pid == 0) { /* child */ - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ struct sigaction sa = { .sa_handler = sigpipe_handler, @@ -258,82 +238,67 @@ TEST_ADD(pipe_read_interruptible_sleep) { char buf; ualarm(5000, 5000); /* 5 ms, and after that every 5 ms */ - bytes_wrote = read(pipe_fd[0], &buf, 1); - - assert(bytes_wrote == -1); - assert(errno == EINTR); + syscall_fail(read(pipe_fd[0], &buf, 1), EINTR); - close(pipe_fd[0]); + xclose(pipe_fd[0]); exit(EXIT_SUCCESS); } - close(pipe_fd[0]); /* closing read end of pipe */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ + wait_child_finished(child_pid); + xclose(pipe_fd[1]); /* closing write end of pipe */ return 0; } -TEST_ADD(pipe_read_errno_eagain) { +TEST_ADD(pipe_read_errno_eagain, 0) { int pipe_fd[2]; - pid_t child_pid; - int bytes_wrote; /* creating pipe */ int pipe2_ret = pipe2(pipe_fd, O_NONBLOCK); assert(pipe2_ret == 0); /* forking */ - child_pid = fork(); - assert(child_pid >= 0); + pid_t child_pid = xfork(); if (child_pid == 0) { /* child */ - - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ char buf; - bytes_wrote = read(pipe_fd[0], &buf, 1); - assert(errno == EAGAIN); - assert(bytes_wrote == -1); - close(pipe_fd[0]); + syscall_fail(read(pipe_fd[0], &buf, 1), EAGAIN); + xclose(pipe_fd[0]); exit(EXIT_SUCCESS); } - close(pipe_fd[0]); /* closing read end of pipe */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[0]); /* closing read end of pipe */ + wait_child_finished(child_pid); + xclose(pipe_fd[1]); /* closing write end of pipe */ return 0; } -TEST_ADD(pipe_read_return_zero) { +TEST_ADD(pipe_read_return_zero, 0) { int pipe_fd[2]; - pid_t child_pid; - int bytes_wrote; /* creating pipe */ int pipe2_ret = pipe2(pipe_fd, 0); assert(pipe2_ret == 0); /* forking */ - child_pid = fork(); - assert(child_pid >= 0); + pid_t child_pid = xfork(); if (child_pid == 0) { /* child */ - close(pipe_fd[1]); /* closing write end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ char buf; - bytes_wrote = read(pipe_fd[0], &buf, 1); - - assert(bytes_wrote == 0); - assert(errno == 0); + assert(xread(pipe_fd[0], &buf, 1) == 0); - close(pipe_fd[0]); + xclose(pipe_fd[0]); exit(EXIT_SUCCESS); } - close(pipe_fd[0]); /* closing read end of pipe */ - close(pipe_fd[1]); /* closing write end of pipe */ - wait_for_child_exit(child_pid, EXIT_SUCCESS); + xclose(pipe_fd[0]); /* closing read end of pipe */ + xclose(pipe_fd[1]); /* closing write end of pipe */ + wait_child_finished(child_pid); return 0; } diff --git a/bin/utest/procstat.c b/bin/utest/procstat.c index 2ffe1f3438..001811b618 100644 --- a/bin/utest/procstat.c +++ b/bin/utest/procstat.c @@ -2,7 +2,7 @@ #include -TEST_ADD(procstat) { +TEST_ADD(procstat, 0) { int euid, pid, ppid, pgrp, session, got; char state; #define PROC_COMM_MAX 128 diff --git a/bin/utest/pty.c b/bin/utest/pty.c index 1cb70b179e..cb96eca150 100644 --- a/bin/utest/pty.c +++ b/bin/utest/pty.c @@ -12,7 +12,7 @@ static const char *test_str = "hello"; -TEST_ADD(pty_simple) { +TEST_ADD(pty_simple, 0) { int master_fd, slave_fd; open_pty(&master_fd, &slave_fd); @@ -39,7 +39,7 @@ TEST_ADD(pty_simple) { assert(c == test_str[i]); } - close(slave_fd); + xclose(slave_fd); /* Slave device isn't opened: master reads should report EOF. */ assert(read(master_fd, &c, 1) == 0); @@ -47,7 +47,7 @@ TEST_ADD(pty_simple) { /* Write something to the master, and then open the slave. * The slave should see the data written. */ assert(write(master_fd, test_str, len) == len); - slave_fd = open(ptsname(master_fd), O_NOCTTY | O_RDWR); + slave_fd = xopen(ptsname(master_fd), O_NOCTTY | O_RDWR); for (int i = 0; i < len; i++) { assert(read(slave_fd, &c, 1) == 1); assert(c == test_str[i]); @@ -55,12 +55,12 @@ TEST_ADD(pty_simple) { /* Close the master device: reads on the slave device should report EOF, and * writes should return an ENXIO error. */ - close(master_fd); + xclose(master_fd); assert(read(slave_fd, &c, 1) == 0); assert(write(slave_fd, test_str, len) == -1); assert(errno == ENXIO); - close(slave_fd); + xclose(slave_fd); return 0; } diff --git a/bin/utest/sbrk.c b/bin/utest/sbrk.c index 5780270f60..941cadd9f1 100644 --- a/bin/utest/sbrk.c +++ b/bin/utest/sbrk.c @@ -64,7 +64,7 @@ static void sbrk_bad(void) { assert(b2 == b1); } -TEST_ADD(sbrk_sigsegv) { +TEST_ADD(sbrk_sigsegv, 0) { /* Make sure memory just above sbrk has just been used and freed */ void *unaligned = sbrk(0); /* Align to page size */ @@ -84,7 +84,7 @@ TEST_ADD(sbrk_sigsegv) { return 0; } -TEST_ADD(sbrk) { +TEST_ADD(sbrk, 0) { sbrk_orig = sbrk(0); assert(sbrk_orig != NULL); diff --git a/bin/utest/setjmp.c b/bin/utest/setjmp.c index 878b13eb98..6e230f1bf3 100644 --- a/bin/utest/setjmp.c +++ b/bin/utest/setjmp.c @@ -15,7 +15,7 @@ noreturn static void do_longjmp(int count) { assert(0); /* Shouldn't reach here. */ } -TEST_ADD(setjmp) { +TEST_ADD(setjmp, 0) { unsigned int local_var = LOCAL_VALUE; volatile int count = 0; diff --git a/bin/utest/sigaction.c b/bin/utest/sigaction.c index 69ef42001b..547d1ff4fb 100644 --- a/bin/utest/sigaction.c +++ b/bin/utest/sigaction.c @@ -12,7 +12,7 @@ noreturn static void sigint_handler(int signo) { assert(0); /* Shouldn't reach here. */ } -TEST_ADD(sigaction_with_setjmp) { +TEST_ADD(sigaction_with_setjmp, 0) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); @@ -33,7 +33,7 @@ static void sigusr1_handler(int signo) { sigusr1_handled = 1; } -TEST_ADD(sigaction_handler_returns) { +TEST_ADD(sigaction_handler_returns, 0) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/bin/utest/signal.c b/bin/utest/signal.c index a72e0e1404..d2ec0e1d87 100644 --- a/bin/utest/signal.c +++ b/bin/utest/signal.c @@ -6,66 +6,62 @@ #include #include #include +#include /* ======= signal_basic ======= */ static volatile int sigusr1_handled = 0; static void sigusr1_handler(int signo) { - printf("sigusr1 handled!\n"); + debug("sigusr1 handled!"); sigusr1_handled = 1; } static void sigint_handler(int signo) { - printf("sigint handled!\n"); + debug("sigint handled!"); raise(SIGUSR1); /* Recursive signals! */ } -TEST_ADD(signal_basic) { - signal(SIGINT, sigint_handler); - signal(SIGUSR1, sigusr1_handler); +TEST_ADD(signal_basic, 0) { + xsignal(SIGINT, sigint_handler); + xsignal(SIGUSR1, sigusr1_handler); raise(SIGINT); assert(sigusr1_handled); /* Restore original behavior. */ - signal(SIGINT, SIG_DFL); - signal(SIGUSR1, SIG_DFL); + xsignal(SIGINT, SIG_DFL); + xsignal(SIGUSR1, SIG_DFL); return 0; } /* ======= signal_send ======= */ static void sigusr2_handler(int signo) { - printf("Child process handles sigusr2.\n"); + debug("Child process handles sigusr2."); raise(SIGABRT); /* Terminate self. */ } /* Test sending a signal to a different thread. */ -TEST_ADD(signal_send) { +TEST_ADD(signal_send, 0) { /* The child should inherit signal handler configuration. */ - signal(SIGUSR2, sigusr2_handler); - int pid = fork(); + xsignal(SIGUSR2, sigusr2_handler); + pid_t pid = xfork(); if (pid == 0) { - printf("This is child (mypid = %d)\n", getpid()); - /* Wait for signal. */ - while (1) - sched_yield(); + debug("This is child (mypid = %d)", getpid()); + pause(); /* Wait for signal. */ + exit(0); } - printf("This is parent (childpid = %d, mypid = %d)\n", pid, getpid()); - kill(pid, SIGUSR2); - int status; - printf("Waiting for child...\n"); - wait(&status); - assert(WIFSIGNALED(status)); - assert(WTERMSIG(status) == SIGABRT); - printf("Child was stopped by SIGABRT.\n"); + debug("This is parent (childpid = %d, mypid = %d)", pid, getpid()); + xkill(pid, SIGUSR2); + wait_child_terminated(pid, SIGABRT); + debug("Child was terminated by SIGABRT."); return 0; } /* ======= signal_abort ======= */ /* This test shall be considered success if the process gets terminated with SIGABRT */ -TEST_ADD(signal_abort) { +TEST_ADD(signal_abort, 0) { siginfo_t si; EXPECT_SIGNAL(SIGABRT, &si) { raise(SIGABRT); @@ -76,7 +72,7 @@ TEST_ADD(signal_abort) { } /* ======= signal_segfault ======= */ -TEST_ADD(signal_segfault) { +TEST_ADD(signal_segfault, 0) { volatile struct { int x; } *ptr = 0x0; siginfo_t si; @@ -96,31 +92,29 @@ static void sigcont_handler(int signo) { static volatile int ppid; static void signal_parent(int signo) { - kill(ppid, SIGCONT); + xkill(ppid, SIGCONT); } -TEST_ADD(signal_stop) { +TEST_ADD(signal_stop, 0) { ppid = getpid(); - signal(SIGUSR1, SIG_IGN); - signal(SIGCONT, sigcont_handler); - int pid = fork(); + xsignal(SIGUSR1, SIG_IGN); + xsignal(SIGCONT, sigcont_handler); + pid_t pid = xfork(); if (pid == 0) { - signal(SIGUSR1, signal_parent); + xsignal(SIGUSR1, signal_parent); /* The child keeps sending SIGUSR1 to the parent. */ while (!sigcont_handled) - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); return 0; } - int status; - signal(SIGUSR1, sigusr1_handler); + xsignal(SIGUSR1, sigusr1_handler); /* Wait for the child to start sending signals */ while (!sigusr1_handled) sched_yield(); - kill(pid, SIGSTOP); + xkill(pid, SIGSTOP); /* Wait for the child to stop. */ - assert(waitpid(pid, &status, WUNTRACED) == pid); - assert(WIFSTOPPED(status)); + wait_child_stopped(pid); /* Now we shouldn't be getting any signals from the child. */ sigusr1_handled = 0; /* Yield a couple times to make sure that if the child was runnable, @@ -132,33 +126,30 @@ TEST_ADD(signal_stop) { * continued (with SIGKILL and SIGCONT being the only exceptions). * Send SIGUSR1 to the stopped child. If the handler runs, it will * send us SIGCONT. */ - kill(pid, SIGUSR1); + xkill(pid, SIGUSR1); for (int i = 0; i < 3; i++) sched_yield(); assert(!sigcont_handled); /* Now continue the child process. */ - kill(pid, SIGCONT); + xkill(pid, SIGCONT); /* The child's SIGUSR1 handler should now run, and so our SIGCONT handler * should run too. */ while (!sigcont_handled) sched_yield(); /* The child process should exit normally. */ - printf("Waiting for child...\n"); - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + wait_child_finished(pid); return 0; } /* ======= signal_cont_masked ======= */ -TEST_ADD(signal_cont_masked) { +TEST_ADD(signal_cont_masked, 0) { ppid = getpid(); - signal(SIGCONT, sigcont_handler); - int pid = fork(); + xsignal(SIGCONT, sigcont_handler); + pid_t pid = xfork(); if (pid == 0) { /* Block SIGCONT. */ sigset_t mask, old; - __sigemptyset(&mask); + sigemptyset(&mask); sigaddset(&mask, SIGCONT); assert(sigprocmask(SIG_BLOCK, &mask, &old) == 0); /* Even though SIGCONT is blocked, it should wake us up, but it @@ -171,26 +162,21 @@ TEST_ADD(signal_cont_masked) { return 0; } - int status; /* Wait for the child to stop. */ - assert(waitpid(pid, &status, WUNTRACED) == pid); - assert(WIFSTOPPED(status)); - - kill(pid, SIGCONT); - printf("Waiting for child...\n"); - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + wait_child_stopped(pid); + xkill(pid, SIGCONT); + wait_child_finished(pid); + return 0; } /* ======= signal_mask ======= */ -TEST_ADD(signal_mask) { +TEST_ADD(signal_mask, 0) { ppid = getpid(); - signal(SIGUSR1, signal_parent); - signal(SIGCONT, sigcont_handler); + xsignal(SIGUSR1, signal_parent); + xsignal(SIGCONT, sigcont_handler); - int pid = fork(); + pid_t pid = xfork(); if (pid == 0) { while (!sigcont_handled) sched_yield(); @@ -198,7 +184,7 @@ TEST_ADD(signal_mask) { } /* Check that the signal bounces properly. */ - kill(pid, SIGUSR1); + xkill(pid, SIGUSR1); while (!sigcont_handled) sched_yield(); @@ -210,14 +196,14 @@ TEST_ADD(signal_mask) { * The delivery of the signal should be delayed until we unblock it. */ assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); sigcont_handled = 0; - kill(pid, SIGUSR1); + xkill(pid, SIGUSR1); /* Wait until we get a signal from the child. */ sigset_t set; do { sched_yield(); sigpending(&set); - } while (!__sigismember(&set, SIGCONT)); + } while (!sigismember(&set, SIGCONT)); assert(!sigcont_handled); @@ -225,89 +211,82 @@ TEST_ADD(signal_mask) { assert(sigprocmask(SIG_UNBLOCK, &mask, NULL) == 0); assert(sigcont_handled); - kill(pid, SIGCONT); - int status; - printf("Waiting for child...\n"); - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + xkill(pid, SIGCONT); + + wait_child_finished(pid); return 0; } /* ======= signal_mask_nonmaskable ======= */ -TEST_ADD(signal_mask_nonmaskable) { +TEST_ADD(signal_mask_nonmaskable, 0) { sigset_t set, old; - __sigemptyset(&set); - __sigaddset(&set, SIGSTOP); - __sigaddset(&set, SIGKILL); - __sigaddset(&set, SIGUSR1); + sigemptyset(&set); + sigaddset(&set, SIGSTOP); + sigaddset(&set, SIGKILL); + sigaddset(&set, SIGUSR1); /* The call should succeed, but SIGKILL and SIGSTOP shouldn't be blocked. */ - assert(sigprocmask(SIG_BLOCK, &set, &old) == 0); - assert(sigprocmask(SIG_BLOCK, NULL, &set) == 0); - __sigaddset(&old, SIGUSR1); - assert(__sigsetequal(&set, &old)); + xsigprocmask(SIG_BLOCK, &set, &old); + xsigprocmask(SIG_BLOCK, NULL, &set); + sigaddset(&old, SIGUSR1); + assert(sigsetequal(&set, &old)); return 0; } /* ======= signal_sigsuspend ======= */ -TEST_ADD(signal_sigsuspend) { +TEST_ADD(signal_sigsuspend, 0) { pid_t ppid = getpid(); - signal(SIGCONT, sigcont_handler); - signal(SIGUSR1, sigusr1_handler); + xsignal(SIGCONT, sigcont_handler); + xsignal(SIGUSR1, sigusr1_handler); sigset_t set, old; - __sigemptyset(&set); - __sigaddset(&set, SIGCONT); - __sigaddset(&set, SIGUSR1); - assert(sigprocmask(SIG_BLOCK, &set, &old) == 0); - __sigaddset(&old, SIGCONT); - pid_t cpid = fork(); + sigemptyset(&set); + sigaddset(&set, SIGCONT); + sigaddset(&set, SIGUSR1); + xsigprocmask(SIG_BLOCK, &set, &old); + sigaddset(&old, SIGCONT); + pid_t cpid = xfork(); if (cpid == 0) { for (int i = 0; i < 10; i++) { - kill(ppid, SIGCONT); + xkill(ppid, SIGCONT); sched_yield(); } - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); return 0; } /* Go to sleep with SIGCONT blocked and SIGUSR1 unblocked. */ - printf("Calling sigsuspend()...\n"); + debug("Calling sigsuspend()..."); sigset_t current; - sigprocmask(SIG_BLOCK, NULL, ¤t); - assert(__sigismember(¤t, SIGUSR1)); - assert(!__sigismember(&old, SIGUSR1)); + xsigprocmask(SIG_BLOCK, NULL, ¤t); + assert(sigismember(¤t, SIGUSR1)); + assert(!sigismember(&old, SIGUSR1)); sigsuspend(&old); /* Check if mask is set back after waking up */ - sigprocmask(SIG_BLOCK, NULL, &set); - assert(__sigsetequal(&set, ¤t)); + xsigprocmask(SIG_BLOCK, NULL, &set); + assert(sigsetequal(&set, ¤t)); /* SIGUSR1 should have woken us up, but SIGCONT should still be pending. */ assert(sigusr1_handled); assert(!sigcont_handled); - __sigemptyset(&set); - __sigaddset(&set, SIGCONT); - assert(sigprocmask(SIG_UNBLOCK, &set, NULL) == 0); + sigemptyset(&set); + sigaddset(&set, SIGCONT); + xsigprocmask(SIG_UNBLOCK, &set, NULL); assert(sigcont_handled); - int status; - printf("Waiting for child...\n"); - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + wait_child_finished(cpid); return 0; } /* ======= signal_sigsuspend_stop ======= */ -TEST_ADD(signal_sigsuspend_stop) { +TEST_ADD(signal_sigsuspend_stop, 0) { pid_t ppid = getpid(); - signal(SIGUSR1, sigusr1_handler); + xsignal(SIGUSR1, sigusr1_handler); sigset_t set, old; - __sigemptyset(&set); - __sigaddset(&set, SIGUSR1); + sigemptyset(&set); + sigaddset(&set, SIGUSR1); assert(sigprocmask(SIG_BLOCK, &set, &old) == 0); - pid_t cpid = fork(); + pid_t cpid = xfork(); if (cpid == 0) { sigsuspend(&old); assert(sigusr1_handled); - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); return 0; } /* Wait for the child to call sigsuspend(). @@ -316,13 +295,11 @@ TEST_ADD(signal_sigsuspend_stop) { sched_yield(); /* Stop the child. */ - kill(cpid, SIGSTOP); - int status; - assert(waitpid(cpid, &status, WUNTRACED) == cpid); - assert(WIFSTOPPED(status)); + xkill(cpid, SIGSTOP); + wait_child_stopped(cpid); /* Continue the child. This should not interrupt the child's sigsuspend(). */ - kill(cpid, SIGCONT); + xkill(cpid, SIGCONT); /* Give the child a chance to run if it has been resumed * (which it shouldn't). */ for (int i = 0; i < 3; i++) @@ -331,13 +308,12 @@ TEST_ADD(signal_sigsuspend_stop) { assert(!sigusr1_handled); /* Stop the child again. */ - kill(cpid, SIGSTOP); - assert(waitpid(cpid, &status, WUNTRACED) == cpid); - assert(WIFSTOPPED(status)); + xkill(cpid, SIGSTOP); + wait_child_stopped(cpid); /* Send SIGUSR1 to the child. Since it's stopped, it should not interrupt * the sigsuspend() yet. */ - kill(cpid, SIGUSR1); + xkill(cpid, SIGUSR1); /* Give the child a chance to run if it has been resumed * (which it shouldn't). */ for (int i = 0; i < 3; i++) @@ -347,15 +323,13 @@ TEST_ADD(signal_sigsuspend_stop) { /* Continue the child. Now the SIGUSR1 we sent earlier should interrupt * the sigsuspend() call. */ - kill(cpid, SIGCONT); + xkill(cpid, SIGCONT); /* Wait for the child to send us SIGUSR1. */ sigsuspend(&old); /* Reap the child. */ - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + wait_child_finished(cpid); return 0; } @@ -366,7 +340,7 @@ static pid_t cpid; static void yield_handler(int signo) { /* Give the child process the signal to send us SIGUSR1 */ - kill(cpid, SIGUSR1); + xkill(cpid, SIGUSR1); while (!sigcont_handled) sched_yield(); handler_ran = 1; @@ -374,27 +348,27 @@ static void yield_handler(int signo) { handler_success = 1; } -TEST_ADD(signal_handler_mask) { +TEST_ADD(signal_handler_mask, 0) { pid_t ppid = getpid(); struct sigaction sa = {.sa_handler = yield_handler, .sa_flags = 0}; /* Block SIGUSR1 when executing handler for SIGUSR2. */ - __sigemptyset(&sa.sa_mask); - __sigaddset(&sa.sa_mask, SIGUSR1); + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGUSR1); assert(sigaction(SIGUSR2, &sa, NULL) == 0); - signal(SIGUSR1, sigusr1_handler); - signal(SIGCONT, sigcont_handler); + xsignal(SIGUSR1, sigusr1_handler); + xsignal(SIGCONT, sigcont_handler); - pid_t cpid = fork(); + pid_t cpid = xfork(); if (cpid == 0) { - kill(ppid, SIGUSR2); + xkill(ppid, SIGUSR2); /* Wait for the parent to enter the signal handler. */ while (!sigusr1_handled) sched_yield(); /* Now SIGUSR1 should be blocked in the parent. */ for (int i = 0; i < 3; i++) - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); /* Sending SIGCONT should allow yield_handler() to run to completion. */ - kill(ppid, SIGCONT); + xkill(ppid, SIGCONT); return 0; } @@ -404,10 +378,6 @@ TEST_ADD(signal_handler_mask) { assert(handler_success); assert(sigusr1_handled); - int status; - printf("Waiting for child...\n"); - wait(&status); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + wait_child_finished(cpid); return 0; } diff --git a/bin/utest/stat.c b/bin/utest/stat.c index 5a77f48f37..1e2295a5ef 100644 --- a/bin/utest/stat.c +++ b/bin/utest/stat.c @@ -6,10 +6,10 @@ #include #include -TEST_ADD(stat) { +TEST_ADD(stat, 0) { struct stat sb; - syscall_ok(stat("/tests/ascii", &sb)); + xstat("/tests/ascii", &sb); assert(sb.st_size == 95); assert(S_ISREG(sb.st_mode)); @@ -18,17 +18,17 @@ TEST_ADD(stat) { return 0; } -TEST_ADD(fstat) { +TEST_ADD(fstat, 0) { struct stat sb; int fd; - fd = open("/tests", 0, O_RDONLY); - syscall_ok(fstat(fd, &sb)); + fd = xopen("/tests", 0, O_RDONLY); + xfstat(fd, &sb); assert(S_ISDIR(sb.st_mode)); syscall_fail(fstat(fd, NULL), EFAULT); syscall_fail(fstat(666, &sb), EBADF); - close(fd); + xclose(fd); return 0; } diff --git a/bin/utest/time.c b/bin/utest/time.c index 5c64017827..6121aa60b9 100644 --- a/bin/utest/time.c +++ b/bin/utest/time.c @@ -7,7 +7,7 @@ #include #include -TEST_ADD(gettimeofday) { +TEST_ADD(gettimeofday, 0) { timeval_t time1, time2; const int64_t start_of_century = 94684800, end_of_century = 4102444799; @@ -28,7 +28,7 @@ TEST_ADD(gettimeofday) { return 0; } -TEST_ADD(nanosleep) { +TEST_ADD(nanosleep, TF_DISABLED) { /* Requested and remaining time */ timespec_t rqt, rmt; timeval_t time1, time2, diff; @@ -61,21 +61,21 @@ TEST_ADD(nanosleep) { diff.tv_sec = rqt.tv_sec; diff.tv_usec = rqt.tv_nsec / 1000; - syscall_ok(gettimeofday(&time1, NULL)); + xgettimeofday(&time1, NULL); while ((ret = nanosleep(&rqt, &rmt)) == EINTR) rqt = rmt; assert(ret == 0); - syscall_ok(gettimeofday(&time2, NULL)); + xgettimeofday(&time2, NULL); - printf("time1: %d.%06d, time2: %d.%06d\n", (int)time1.tv_sec, time1.tv_usec, - (int)time2.tv_sec, time2.tv_usec); + debug("time1: %d.%06d, time2: %d.%06d", (int)time1.tv_sec, time1.tv_usec, + (int)time2.tv_sec, time2.tv_usec); timeradd(&time1, &diff, &time1); assert(timercmp(&time1, &time2, <=)); } return 0; } -TEST_ADD(itimer) { +TEST_ADD(itimer, 0) { signal_setup(SIGALRM); struct itimerval it, it2; memset(&it, 0, sizeof(it)); diff --git a/bin/utest/tty.c b/bin/utest/tty.c index cbdaa4040e..67264c13a9 100644 --- a/bin/utest/tty.c +++ b/bin/utest/tty.c @@ -12,7 +12,7 @@ #include #include -TEST_ADD(tty_canon) { +TEST_ADD(tty_canon, 0) { int master_fd, slave_fd; open_pty(&master_fd, &slave_fd); @@ -82,7 +82,7 @@ TEST_ADD(tty_canon) { return 0; } -TEST_ADD(tty_echo) { +TEST_ADD(tty_echo, 0) { int master_fd, slave_fd; open_pty(&master_fd, &slave_fd); @@ -129,7 +129,7 @@ TEST_ADD(tty_echo) { return 0; } -TEST_ADD(tty_signals) { +TEST_ADD(tty_signals, 0) { signal_setup(SIGUSR1); int master_fd, slave_fd; open_pty(&master_fd, &slave_fd); @@ -142,7 +142,7 @@ TEST_ADD(tty_signals) { assert(tcsetattr(slave_fd, TCSANOW, &t) == 0); pid_t ppid = getpid(); - pid_t cpid = fork(); + pid_t cpid = xfork(); if (cpid == 0) { signal_setup(SIGINT); signal_setup(SIGQUIT); @@ -153,14 +153,14 @@ TEST_ADD(tty_signals) { * becomes the controlling terminal for its session, and the child is in the * foreground process group. */ assert(setsid() == cpid); - assert(close(slave_fd) == 0); - assert((slave_fd = open(ptsname(master_fd), 0)) >= 0); - assert(close(master_fd) == 0); + xclose(slave_fd); + assert((slave_fd = xopen(ptsname(master_fd), 0)) >= 0); + xclose(master_fd); /* We should be in the foreground process group now. */ assert(tcgetpgrp(slave_fd) == cpid); /* We're ready to take the signals now. */ - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); wait_for_signal(SIGINT); /* Check if the "foo" was discarded. */ @@ -168,10 +168,10 @@ TEST_ADD(tty_signals) { assert(read(slave_fd, &c, 1) == 1); assert(c == 'x'); - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); wait_for_signal(SIGQUIT); - kill(ppid, SIGUSR1); + xkill(ppid, SIGUSR1); wait_for_signal(SIGTSTP); return 0; @@ -209,7 +209,7 @@ TEST_ADD(tty_signals) { assert(vsusp != _POSIX_VDISABLE); assert(write(master_fd, &vsusp, 1) == 1); - wait_for_child_exit(cpid, 0); + wait_child_finished(cpid); return 0; } diff --git a/bin/utest/utest.c b/bin/utest/utest.c index 68b6404cf9..cd16f7a096 100644 --- a/bin/utest/utest.c +++ b/bin/utest/utest.c @@ -7,45 +7,82 @@ #include #include -noreturn void __die(const char *file, int line, const char *func, - const char *fmt, ...) { +static void print_msg(const char *file, int line, const char *fmt, va_list ap) { char buf[1024]; int len = 0, res; char *basename = strrchr(file, '/'); file = basename ? basename + 1 : file; - res = snprintf_ss(buf, sizeof(buf), "[%s:%d] %s: ", file, line, func); + res = snprintf_ss(buf, sizeof(buf), "[%s:%d] ", file, line); if (res < 0) exit(EXIT_FAILURE); len += res; - va_list ap; - va_start(ap, fmt); res = vsnprintf_ss(buf + len, sizeof(buf) - len, fmt, ap); if (res < 0) exit(EXIT_FAILURE); - va_end(ap); len += res; + buf[len++] = '\n'; (void)write(STDERR_FILENO, buf, (size_t)len); +} + +int __verbose = 0; + +void __msg(const char *file, int line, const char *fmt, ...) { + if (!__verbose) + return; + + va_list ap; + va_start(ap, fmt); + print_msg(file, line, fmt, ap); + va_end(ap); +} + +noreturn void __die(const char *file, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + print_msg(file, line, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); } -int utest_spawn(proc_func_t func, void *arg) { - int pid; - switch ((pid = fork())) { - case -1: - exit(EXIT_FAILURE); - case 0: - exit(func(arg)); - default: - return pid; - } +pid_t spawn(proc_func_t func, void *arg) { + int pid = xfork(); + if (pid == 0) + exit(func(arg)); + return pid; } -void utest_child_exited(int exitcode) { +pid_t wait_child_exited(pid_t pid, int exitcode) { int status; - wait(&status); + pid_t res = xwaitpid(pid, &status, 0); + if (pid > 0) + assert(res == pid); assert(WIFEXITED(status)); assert(WEXITSTATUS(status) == exitcode); + return res; +} + +pid_t wait_child_terminated(pid_t pid, int signo) { + int status; + pid_t res = xwaitpid(pid, &status, 0); + if (pid > 0) + assert(res == pid); + assert(WIFSIGNALED(status)); + assert(WTERMSIG(status) == signo); + return res; +} + +void wait_child_stopped(pid_t pid) { + int status; + assert(xwaitpid(pid, &status, WUNTRACED) == pid); + assert(WIFSTOPPED(status)); +} + +void wait_child_continued(pid_t pid) { + int status; + assert(xwaitpid(pid, &status, WCONTINUED) == pid); + assert(WIFCONTINUED(status)); } diff --git a/bin/utest/utest.h b/bin/utest/utest.h index d315979031..614cf62b2e 100644 --- a/bin/utest/utest.h +++ b/bin/utest/utest.h @@ -1,45 +1,55 @@ -#ifndef __UTEST_H__ -#define __UTEST_H__ +#pragma once #include +#include +#include #include +/* + * Test definition + */ + typedef int (*test_func_t)(void); +typedef enum test_flags { + TF_DISABLED = INT32_MIN, /* test will return success without being executed */ + TF_DEBUG = 1, /* display debug messages to stderr */ + TF_TMPDIR = 2, /* create temp di rand run the test there */ +} test_flags_t; + typedef struct test_entry { const char *name; test_func_t func; + test_flags_t flags; } test_entry_t; -#define TEST_ADD(name) \ - int test_##name(void); \ - test_entry_t name##_test = {#name, test_##name}; \ - SET_ENTRY(tests, name##_test); \ - int test_##name(void) +#define TEST_ADD(NAME, FLAGS) \ + int test_##NAME(void); \ + test_entry_t NAME##_test = { \ + .name = #NAME, .func = test_##NAME, .flags = FLAGS}; \ + SET_ENTRY(tests, NAME##_test); \ + int test_##NAME(void) -typedef int (*proc_func_t)(void *); +/* + * Diagnostic messages + */ + +extern int __verbose; -noreturn void __die(const char *file, int line, const char *func, - const char *fmt, ...); -int utest_spawn(proc_func_t func, void *arg); -void utest_child_exited(int exitcode); +noreturn void __die(const char *file, int line, const char *fmt, ...); +void __msg(const char *file, int line, const char *fmt, ...); -#define die(...) __die(__FILE__, __LINE__, __func__, __VA_ARGS__) +#define die(...) __die(__FILE__, __LINE__, __VA_ARGS__) +#define debug(...) __msg(__FILE__, __LINE__, __VA_ARGS__) + +/* + * Assertion definition and various checks + */ #define assert(e) \ ({ \ if (!(e)) \ - die("assertion '%s' failed!\n", #e); \ - }) - -/* Test if system call returned with no error. */ -#define syscall_ok(e) \ - ({ \ - errno = 0; \ - long __r = (long)(e); \ - if (__r != 0) \ - die("call '%s' unexpectedly returned %ld (errno = %d)!\n", #e, __r, \ - errno); \ + die("assertion '%s' failed!", #e); \ }) /* Test if system call returned with specific error. */ @@ -48,20 +58,99 @@ void utest_child_exited(int exitcode); errno = 0; \ long __r = (long)(e); \ if ((__r != -1) || (errno != (err))) \ - die("call '%s' unexpectedly returned %ld (errno = %d)\n", #e, __r, \ - errno); \ + die("call '%s' unexpectedly returned %ld (errno = %d)", #e, __r, errno); \ }) #define string_eq(s1, s2) \ ({ \ if (strcmp((s1), (s2))) \ - die("strings were expected to match!\n"); \ + die("strings were expected to match!"); \ }) #define string_ne(s1, s2) \ ({ \ if (!strcmp((s1), (s2))) \ - die("strings were not expected to match!\n"); \ + die("strings were not expected to match!"); \ + }) + +/* + * Miscellanous helper functions + */ + +typedef int (*proc_func_t)(void *); + +pid_t spawn(proc_func_t func, void *arg); + +/* waitpid wrapper that checks if child has exited with given exit code */ +pid_t wait_child_exited(pid_t pid, int exitcode); +#define wait_child_finished(pid) wait_child_exited(pid, 0) + +/* waitpid wrapper that checks if child has been signaled with given signal */ +pid_t wait_child_terminated(pid_t pid, int signo); + +void wait_child_stopped(pid_t pid); +void wait_child_continued(pid_t pid); + +/* + * VFS test related definitions + */ + +extern char testdir[]; + +/* + * libc function wrappers that call die(...) on error + */ + +#include +#include + +#define NOFAIL_NR(_CALL, ...) \ + ({ \ + int _res##__LINE__ = _CALL(__VA_ARGS__); \ + if (_res##__LINE__ == -1) \ + __die(__FILE__, __LINE__, #_CALL ": %s", strerror(errno)); \ + }) + +#define NOFAIL(_CALL, _TYPE, ...) \ + ({ \ + _TYPE _res##__LINE__ = _CALL(__VA_ARGS__); \ + if (_res##__LINE__ == (_TYPE)-1) \ + __die(__FILE__, __LINE__, #_CALL ": %s", strerror(errno)); \ + _res##__LINE__; \ }) -#endif /* __UTEST_H__ */ +#define xaccess(...) NOFAIL_NR(access, __VA_ARGS__) +#define xchdir(...) NOFAIL_NR(chdir, __VA_ARGS__) +#define xchmod(...) NOFAIL_NR(chmod, __VA_ARGS__) +#define xclose(...) NOFAIL_NR(close, __VA_ARGS__) +#define xfork(...) NOFAIL(fork, pid_t, __VA_ARGS__) +#define xfstat(...) NOFAIL_NR(fstat, __VA_ARGS__) +#define xgetgroups(...) NOFAIL(getgroups, int, __VA_ARGS__) +#define xgetresuid(...) NOFAIL_NR(getresuid, __VA_ARGS__) +#define xgetresgid(...) NOFAIL_NR(getresgid, __VA_ARGS__) +#define xgettimeofday(...) NOFAIL_NR(gettimeofday, __VA_ARGS__) +#define xkill(...) NOFAIL_NR(kill, __VA_ARGS__) +#define xkillpg(...) NOFAIL_NR(killpg, __VA_ARGS__) +#define xlchmod(...) NOFAIL_NR(lchmod, __VA_ARGS__) +#define xlstat(...) NOFAIL_NR(lstat, __VA_ARGS__) +#define xlink(...) NOFAIL_NR(link, __VA_ARGS__) +#define xmkdir(...) NOFAIL_NR(mkdir, __VA_ARGS__) +#define xmmap(...) NOFAIL(mmap, void *, __VA_ARGS__) +#define xmprotect(...) NOFAIL_NR(mprotect, __VA_ARGS__) +#define xmunmap(...) NOFAIL_NR(munmap, __VA_ARGS__) +#define xopen(...) NOFAIL(open, int, __VA_ARGS__) +#define xpipe(...) NOFAIL_NR(pipe, __VA_ARGS__) +#define xread(...) NOFAIL(read, ssize_t, __VA_ARGS__) +#define xreadlink(...) NOFAIL(readlink, ssize_t, __VA_ARGS__) +#define xrmdir(...) NOFAIL_NR(rmdir, __VA_ARGS__) +#define xsetgroups(...) NOFAIL_NR(setgroups, __VA_ARGS__) +#define xsetresuid(...) NOFAIL_NR(setresuid, __VA_ARGS__) +#define xsetresgid(...) NOFAIL_NR(setresgid, __VA_ARGS__) +#define xsigaction(...) NOFAIL_NR(sigaction, __VA_ARGS__) +#define xsignal(...) NOFAIL(signal, sig_t, __VA_ARGS__) +#define xsigprocmask(...) NOFAIL_NR(sigprocmask, __VA_ARGS__) +#define xstat(...) NOFAIL_NR(stat, __VA_ARGS__) +#define xsymlink(...) NOFAIL_NR(symlink, __VA_ARGS__) +#define xunlink(...) NOFAIL_NR(unlink, __VA_ARGS__) +#define xwaitpid(...) NOFAIL(waitpid, pid_t, __VA_ARGS__) +#define xwrite(...) NOFAIL(write, ssize_t, __VA_ARGS__) diff --git a/bin/utest/util.c b/bin/utest/util.c index a13402af37..7fc8ce65a9 100644 --- a/bin/utest/util.c +++ b/bin/utest/util.c @@ -1,6 +1,6 @@ +#include "utest.h" #include "util.h" -#include #include #include #include @@ -37,27 +37,20 @@ void _expect_signal_cleanup(void) { assert(err == 0); } -void wait_for_child_exit(int pid, int exit_code) { - int status; - assert(waitpid(pid, &status, 0) == pid); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == exit_code); -} - static void noop_handler(int signo) { } void signal_setup(int signo) { - signal(signo, noop_handler); + xsignal(signo, noop_handler); sigset_t mask; - __sigemptyset(&mask); + sigemptyset(&mask); sigaddset(&mask, signo); - assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); + xsigprocmask(SIG_BLOCK, &mask, NULL); } void wait_for_signal(int signo) { sigset_t mask; - __sigfillset(&mask); + sigfillset(&mask); sigdelset(&mask, signo); sigsuspend(&mask); } @@ -65,8 +58,7 @@ void wait_for_signal(int signo) { void open_pty(int *master_fd, int *slave_fd) { *master_fd = posix_openpt(O_NOCTTY | O_RDWR); assert(*master_fd >= 0); - *slave_fd = open(ptsname(*master_fd), O_NOCTTY | O_RDWR); - assert(*slave_fd >= 0); + *slave_fd = xopen(ptsname(*master_fd), O_NOCTTY | O_RDWR); } /* diff --git a/bin/utest/util.h b/bin/utest/util.h index 0858e15dbe..648e255bc4 100644 --- a/bin/utest/util.h +++ b/bin/utest/util.h @@ -6,9 +6,9 @@ #include #include -/* Wait for a single child process with process id `pid` to exit, - * and check that its exit code matches the expected value. */ -void wait_for_child_exit(int pid, int exit_code); +static inline int sigsetequal(sigset_t *a, sigset_t *b) { + return memcmp(a, b, sizeof(sigset_t)) == 0; +} /* Do the necessary setup needed to wait for the signal. * Must be called before receiving the signal, @@ -71,14 +71,12 @@ void _expect_signal_cleanup(void); #undef assert_open_ok #define assert_open_ok(fd, file, mode, flag) \ - n = open(file, flag, mode); \ + n = xopen(file, flag, mode); \ assert(n == fd + FD_OFFSET); #undef assert_open_fail #define assert_open_fail(file, mode, flag, err) \ - n = open(file, flag, 0); \ - assert(n < 0); \ - assert(errno == err); + syscall_fail(open(file, flag, mode), err) #undef assert_read_ok #define assert_read_ok(fd, buf, len) \ @@ -96,9 +94,7 @@ void _expect_signal_cleanup(void); #undef assert_read_fail #define assert_read_fail(fd, buf, len, err) \ - n = read(fd + FD_OFFSET, buf, len); \ - assert(n < 0); \ - assert(errno == err); + syscall_fail(read(fd + FD_OFFSET, buf, len), err) #undef assert_write_ok #define assert_write_ok(fd, buf, len) \ @@ -107,31 +103,19 @@ void _expect_signal_cleanup(void); #undef assert_write_fail #define assert_write_fail(fd, buf, len, err) \ - n = write(fd + FD_OFFSET, buf, len); \ - assert(n < 0); \ - assert(errno == err); + syscall_fail(write(fd + FD_OFFSET, buf, len), err) #undef assert_close_ok -#define assert_close_ok(fd) \ - n = close(fd + FD_OFFSET); \ - assert(n == 0); +#define assert_close_ok(fd) xclose(fd + FD_OFFSET) #undef assert_close_fail -#define assert_close_fail(fd, err) \ - n = close(fd + FD_OFFSET); \ - assert(n < 0); \ - assert(errno == err); +#define assert_close_fail(fd, err) syscall_fail(close(fd + FD_OFFSET), err) #undef assert_lseek_ok #define assert_lseek_ok(fd, offset, whence) \ n = lseek(fd + FD_OFFSET, offset, whence); \ assert(n >= 0); -#undef assert_pipe_ok -#define assert_pipe_ok(fds) \ - n = pipe(fds); \ - assert(n == 0); - #define mmap_anon_priv_flags(addr, length, prot, flags) \ mmap((addr), (length), (prot), (flags) | MAP_ANON | MAP_PRIVATE, -1, 0) diff --git a/bin/utest/vfs.c b/bin/utest/vfs.c index eb49c5e1a7..045885d102 100644 --- a/bin/utest/vfs.c +++ b/bin/utest/vfs.c @@ -13,8 +13,6 @@ #undef FD_OFFSET #define FD_OFFSET 3 -#define TESTDIR "/tmp" - /* Generate pseudo random data */ static void fill_random(uint32_t *data, size_t n) { uint32_t a = 1; @@ -26,7 +24,7 @@ static void fill_random(uint32_t *data, size_t n) { } } -TEST_ADD(vfs_rw) { +TEST_ADD(vfs_rw, TF_TMPDIR) { int n; void *wrbuf = malloc(16384); @@ -34,23 +32,23 @@ TEST_ADD(vfs_rw) { fill_random(wrbuf, 16384); - assert_open_ok(0, TESTDIR "/file", 0, O_RDWR | O_CREAT); + assert_open_ok(0, "file", 0, O_RDWR | O_CREAT); /* Write and read aligned amount of bytes */ assert_write_ok(0, wrbuf, 4096); assert_lseek_ok(0, 0, SEEK_SET); - assert(read(3, rdbuf, 5000) == 4096); + assert(xread(3, rdbuf, 5000) == 4096); assert(!memcmp(wrbuf, rdbuf, 4096)); - assert(read(3, rdbuf, 5000) == 0); + assert(xread(3, rdbuf, 5000) == 0); assert_lseek_ok(0, 0, SEEK_SET); /* Write 12000 bytes in two batches and then partially read */ assert_write_ok(0, wrbuf, 8000); assert_write_ok(0, wrbuf + 8000, 4000); assert_lseek_ok(0, 0, SEEK_SET); - assert(read(3, rdbuf, 6000) == 6000); + assert(xread(3, rdbuf, 6000) == 6000); assert(!memcmp(wrbuf, rdbuf, 6000)); - assert(read(3, rdbuf + 6000, 12000) == 6000); + assert(xread(3, rdbuf + 6000, 12000) == 6000); assert(!memcmp(wrbuf, rdbuf, 6000)); assert_lseek_ok(0, 0, SEEK_SET); @@ -59,88 +57,88 @@ TEST_ADD(vfs_rw) { assert_write_ok(0, wrbuf, 16384); assert_lseek_ok(0, 0, SEEK_SET); for (int i = 0; i < 2; i++) { - assert(read(3, rdbuf, 16384) == 16384); + assert(xread(3, rdbuf, 16384) == 16384); assert(!memcmp(wrbuf, rdbuf, 16384)); } free(wrbuf); free(rdbuf); - close(3); - unlink(TESTDIR "/file"); + xclose(3); + xunlink("file"); return 0; } -TEST_ADD(vfs_trunc) { +TEST_ADD(vfs_trunc, TF_TMPDIR) { int n; void *wrbuf = malloc(4096); void *rdbuf = malloc(8196); fill_random(wrbuf, 4096); - assert_open_ok(0, TESTDIR "/file", S_IWUSR | S_IRUSR, O_RDWR | O_CREAT); + assert_open_ok(0, "file", S_IWUSR | S_IRUSR, O_RDWR | O_CREAT); assert_write_ok(0, wrbuf, 4096); ftruncate(3, 2048); /* The file offset is bigger than size, so read should return 0 bytes. */ - assert(read(3, rdbuf, 6000) == 0); + assert(xread(3, rdbuf, 6000) == 0); assert_lseek_ok(0, 0, SEEK_SET); - assert(read(3, rdbuf, 6000) == 2048); + assert(xread(3, rdbuf, 6000) == 2048); assert(!memcmp(wrbuf, rdbuf, 2048)); ftruncate(3, 7777); assert_lseek_ok(0, 0, SEEK_SET); - assert(read(3, rdbuf, 10000) == 7777); + assert(xread(3, rdbuf, 10000) == 7777); assert(!memcmp(wrbuf, rdbuf, 2048)); /* Rest of the file should be zeroed. */ for (int i = 2048; i < 7777; i++) assert(((uint8_t *)rdbuf)[i] == 0); - truncate(TESTDIR "/file", 0); + truncate("file", 0); assert_lseek_ok(0, 0, SEEK_SET); - assert(read(3, rdbuf, 2048) == 0); + assert(xread(3, rdbuf, 2048) == 0); free(wrbuf); free(rdbuf); - unlink(TESTDIR "/file"); + unlink("file"); - syscall_fail(truncate(TESTDIR, 1023), EISDIR); + syscall_fail(truncate(testdir, 1023), EISDIR); return 0; } -TEST_ADD(vfs_dir) { +TEST_ADD(vfs_dir, TF_TMPDIR) { syscall_fail(mkdir("/", 0), EEXIST); - syscall_ok(mkdir(TESTDIR "/test", 0)); - syscall_fail(mkdir(TESTDIR "/test", 0), EEXIST); - syscall_fail(mkdir(TESTDIR "//test///", 0), EEXIST); - syscall_ok(rmdir(TESTDIR "/test")); - syscall_ok(mkdir(TESTDIR "/test", 0)); - - syscall_ok(mkdir(TESTDIR "//test2///", 0)); - syscall_ok(access(TESTDIR "/test2", 0)); - - syscall_ok(mkdir(TESTDIR "/test3", 0)); - syscall_ok(mkdir(TESTDIR "/test3/subdir1", 0)); - syscall_ok(mkdir(TESTDIR "/test3/subdir2", 0)); - syscall_ok(mkdir(TESTDIR "/test3/subdir3", 0)); - syscall_fail(mkdir(TESTDIR "/test3/subdir1", 0), EEXIST); - syscall_ok(access(TESTDIR "/test3/subdir2", 0)); - - syscall_fail(mkdir(TESTDIR "/test4/subdir4", 0), ENOENT); - - syscall_ok(rmdir(TESTDIR "/test/")); - syscall_ok(rmdir(TESTDIR "/test2")); - syscall_fail(rmdir(TESTDIR "/test3"), ENOTEMPTY); - syscall_ok(rmdir(TESTDIR "/test3/subdir1")); - syscall_ok(rmdir(TESTDIR "/test3/subdir2")); - syscall_ok(rmdir(TESTDIR "/test3/subdir3")); - syscall_ok(rmdir(TESTDIR "/test3")); - syscall_fail(rmdir(TESTDIR "/test3"), ENOENT); - syscall_fail(rmdir(TESTDIR "/test4/subdir4"), ENOENT); - - syscall_fail(mkdir(TESTDIR "/test3/subdir1", 0), ENOENT); + xmkdir("test", 0); + syscall_fail(mkdir("test", 0), EEXIST); + syscall_fail(mkdir("./test///", 0), EEXIST); + xrmdir("test"); + xmkdir("test", 0); + + xmkdir("./test2///", 0); + xaccess("test2", 0); + + xmkdir("test3", 0); + xmkdir("test3/subdir1", 0); + xmkdir("test3/subdir2", 0); + xmkdir("test3/subdir3", 0); + syscall_fail(mkdir("test3/subdir1", 0), EEXIST); + xaccess("test3/subdir2", 0); + + syscall_fail(mkdir("test4/subdir4", 0), ENOENT); + + xrmdir("test/"); + xrmdir("test2"); + syscall_fail(rmdir("test3"), ENOTEMPTY); + xrmdir("test3/subdir1"); + xrmdir("test3/subdir2"); + xrmdir("test3/subdir3"); + xrmdir("test3"); + syscall_fail(rmdir("test3"), ENOENT); + syscall_fail(rmdir("test4/subdir4"), ENOENT); + + syscall_fail(mkdir("test3/subdir1", 0), ENOENT); syscall_fail(mkdir("/", 0), EEXIST); syscall_fail(rmdir("/tmp"), EBUSY); @@ -148,158 +146,153 @@ TEST_ADD(vfs_dir) { return 0; } -TEST_ADD(vfs_relative_dir) { - syscall_ok(chdir(TESTDIR)); - syscall_ok(mkdir("test", 0)); +TEST_ADD(vfs_relative_dir, TF_TMPDIR) { + xmkdir("test", 0); syscall_fail(mkdir("test", 0), EEXIST); syscall_fail(mkdir("test///", 0), EEXIST); - syscall_ok(chdir("test")); + xchdir("test"); - syscall_ok(mkdir("test2///", 0)); - syscall_ok(rmdir("test2")); + xmkdir("test2///", 0); + xrmdir("test2"); - syscall_ok(mkdir("test3", 0)); - syscall_ok(mkdir("test3/subdir1", 0)); - syscall_ok(mkdir("test3/subdir2", 0)); - syscall_ok(mkdir("test3/subdir3", 0)); + xmkdir("test3", 0); + xmkdir("test3/subdir1", 0); + xmkdir("test3/subdir2", 0); + xmkdir("test3/subdir3", 0); syscall_fail(mkdir("test3/subdir1", 0), EEXIST); - syscall_ok(access("test3/subdir2", 0)); + xaccess("test3/subdir2", 0); - syscall_ok(rmdir(TESTDIR "/test/test3/subdir1")); - syscall_ok(rmdir(TESTDIR "/test/test3/subdir2")); - syscall_ok(rmdir(TESTDIR "/test/test3/subdir3")); - syscall_ok(rmdir("test3")); + xchdir(".."); + xrmdir("test/test3/subdir1"); + xrmdir("test/test3/subdir2"); + xrmdir("test/test3/subdir3"); + xrmdir("test/test3"); + xrmdir("test"); - syscall_ok(chdir(TESTDIR)); - syscall_ok(rmdir("test")); - syscall_ok(chdir("/")); return 0; } -TEST_ADD(vfs_dot_dot_dir) { - syscall_ok(chdir(TESTDIR)); - - syscall_ok(mkdir("test", 0)); - syscall_ok(chdir("test")); - syscall_ok(mkdir("test2///", 0)); - syscall_ok(chdir("test2")); +TEST_ADD(vfs_dot_dot_dir, TF_TMPDIR) { + xmkdir("test", 0); + xchdir("test"); + xmkdir("test2///", 0); + xchdir("test2"); - syscall_ok(chdir("..")); - syscall_ok(chdir("test2")); + xchdir(".."); + xchdir("test2"); - syscall_ok(chdir("../test2")); - syscall_ok(chdir("../../")); + xchdir("../test2"); + xchdir("../../"); syscall_fail(mkdir("test", 0), EEXIST); - syscall_ok(chdir("test")); - syscall_ok(rmdir("../test/test2")); + xchdir("test"); + xrmdir("../test/test2"); - syscall_ok(chdir("./..")); - syscall_ok(rmdir("test")); + xchdir("./.."); + xrmdir("test"); return 0; } -TEST_ADD(vfs_dot_dir) { - syscall_fail(mkdir(TESTDIR "/test/.", 0), ENOENT); +TEST_ADD(vfs_dot_dir, 0) { + syscall_fail(mkdir("test/.", 0), ENOENT); syscall_fail(mkdir("/.", 0), EEXIST); - syscall_fail(mkdir(TESTDIR "/.", 0), EEXIST); + syscall_fail(mkdir(".", 0), EEXIST); return 0; } -TEST_ADD(vfs_dot_dot_across_fs) { - syscall_ok(chdir("/../../../../")); +TEST_ADD(vfs_dot_dot_across_fs, 0) { + xchdir("/../../../../"); syscall_fail(mkdir("dev", 0), EEXIST); - syscall_ok(chdir("dev/../dev/../../../dev/../../dev")); + xchdir("dev/../dev/../../../dev/../../dev"); syscall_fail(mkdir("../dev", 0), EEXIST); - syscall_ok(chdir("../")); + xchdir("../"); syscall_fail(mkdir("dev", 0), EEXIST); return 0; } -static void test_vfs_symlink_basic(void) { - char *buff = malloc(1024); - syscall_ok(symlink("Hello, world!", TESTDIR "/testlink")); +TEST_ADD(vfs_symlink_basic, TF_TMPDIR) { + xsymlink("Hello, world!", "testlink"); + + char buf[32]; - assert(readlink(TESTDIR "/testlink", buff, 1024) == 13); - assert(!strcmp("Hello, world!", buff)); + memset(buf, 0, sizeof(buf)); + assert(xreadlink("testlink", buf, 1024) == 13); + string_eq("Hello, world!", buf); - memset(buff, 0, 13); - assert(readlink(TESTDIR "/testlink", buff, 5) == 5); - assert(!strcmp("Hello", buff)); + memset(buf, 0, sizeof(buf)); + assert(xreadlink("testlink", buf, 5) == 5); + string_eq("Hello", buf); - syscall_fail(symlink("Hello, world!", TESTDIR "/testlink"), EEXIST); + syscall_fail(symlink("Hello, world!", "testlink"), EEXIST); - syscall_ok(unlink(TESTDIR "/testlink")); - free(buff); + xunlink("testlink"); + + return 0; } -static void test_vfs_symlink_vnr(void) { +TEST_ADD(vfs_symlink_vnr, TF_TMPDIR) { int n; struct stat sb; ino_t fileino; - assert_open_ok(0, TESTDIR "/file", 0, O_RDWR | O_CREAT); - syscall_ok(stat(TESTDIR "/file", &sb)); + assert_open_ok(0, "file", 0, O_RDWR | O_CREAT); + xstat("file", &sb); fileino = sb.st_ino; /* Absolute symlink */ - syscall_ok(symlink(TESTDIR "/file", TESTDIR "/alink")); - syscall_ok(stat(TESTDIR "/alink", &sb)); + xsymlink("file", "alink"); + xstat("alink", &sb); assert(fileino == sb.st_ino); - syscall_ok(symlink(TESTDIR "/alink", TESTDIR "/alink2")); - syscall_ok(stat(TESTDIR "/alink2", &sb)); + xsymlink("alink", "alink2"); + xstat("alink2", &sb); assert(fileino == sb.st_ino); /* Relative symlink */ - syscall_ok(symlink("file", TESTDIR "/rlink")); - syscall_ok(stat(TESTDIR "/rlink", &sb)); + xsymlink("file", "rlink"); + xstat("rlink", &sb); assert(fileino == sb.st_ino); - syscall_ok(symlink("alink2", TESTDIR "/rlink2")); - syscall_ok(stat(TESTDIR "/rlink2", &sb)); + xsymlink("alink2", "rlink2"); + xstat("rlink2", &sb); assert(fileino == sb.st_ino); /* Do not follow symlink */ - syscall_ok(lstat(TESTDIR "/alink2", &sb)); + xlstat("alink2", &sb); assert(fileino != sb.st_ino); - unlink(TESTDIR "/alink"); - unlink(TESTDIR "/alink2"); - unlink(TESTDIR "/rlink"); - unlink(TESTDIR "/rlink2"); + xunlink("alink"); + xunlink("alink2"); + xunlink("rlink"); + xunlink("rlink2"); /* Symlink to directory */ - syscall_ok(symlink("/tmp", TESTDIR "/dlink")); - syscall_ok(stat(TESTDIR "/dlink/file", &sb)); + xsymlink(testdir, "dlink"); + xstat("dlink/file", &sb); assert(fileino == sb.st_ino); - unlink(TESTDIR "/dlink"); + xunlink("dlink"); /* Looped symlink */ - syscall_ok(symlink(TESTDIR "/slink", TESTDIR "/slink")); - syscall_fail(stat(TESTDIR "/slink", &sb), ELOOP); - unlink(TESTDIR "/slink"); + xsymlink("slink", "slink"); + syscall_fail(stat("slink", &sb), ELOOP); + xunlink("slink"); /* Bad symlink */ - syscall_ok(symlink(TESTDIR "/nofile", TESTDIR "/blink")); - syscall_fail(stat(TESTDIR "/blink", &sb), ENOENT); - unlink(TESTDIR "/blink"); + xsymlink("nofile", "blink"); + syscall_fail(stat("blink", &sb), ENOENT); + xunlink("blink"); - unlink(TESTDIR "/file"); -} + xunlink("file"); -TEST_ADD(vfs_symlink) { - test_vfs_symlink_basic(); - test_vfs_symlink_vnr(); return 0; } -TEST_ADD(vfs_link) { +TEST_ADD(vfs_link, TF_TMPDIR) { int n; struct stat sb; ino_t fileino; @@ -309,8 +302,8 @@ TEST_ADD(vfs_link) { fill_random(wrbuf, 64); /* Create file and fill it with random data */ - assert_open_ok(0, TESTDIR "/file", S_IWUSR | S_IRUSR, O_RDWR | O_CREAT); - syscall_ok(stat(TESTDIR "/file", &sb)); + assert_open_ok(0, "file", S_IWUSR | S_IRUSR, O_RDWR | O_CREAT); + xstat("file", &sb); assert(sb.st_nlink == 1); fileino = sb.st_ino; @@ -318,79 +311,79 @@ TEST_ADD(vfs_link) { assert_write_ok(0, wrbuf, 32); /* Make a hard link */ - syscall_ok(link(TESTDIR "/file", TESTDIR "/file2")); - syscall_ok(stat(TESTDIR "/file2", &sb)); + xlink("file", "file2"); + xstat("file2", &sb); /* Ensure if inode number and link count is proper */ assert(sb.st_ino == fileino); assert(sb.st_nlink == 2); /* Ensure if data is the same */ - assert_open_ok(1, TESTDIR "/file2", 0, O_RDWR); - assert(read(4, rdbuf, 32) == 32); + assert_open_ok(1, "file2", 0, O_RDWR); + assert(xread(4, rdbuf, 32) == 32); assert(!memcmp(wrbuf, rdbuf, 32)); /* Make another link to the same file*/ - syscall_ok(link(TESTDIR "/file2", TESTDIR "/file3")); - syscall_ok(stat(TESTDIR "/file3", &sb)); + xlink("file2", "file3"); + xstat("file3", &sb); /* Ensure if inode number and link count is proper */ assert(sb.st_ino == fileino); assert(sb.st_nlink == 3); - assert_open_ok(2, TESTDIR "/file3", 0, O_RDWR); + assert_open_ok(2, "file3", 0, O_RDWR); /* Make a change to the first file and check for change*/ assert_lseek_ok(0, 0, SEEK_SET); assert_write_ok(0, wrbuf + 32, 32); - assert(read(5, rdbuf, 32) == 32); + assert(xread(5, rdbuf, 32) == 32); assert(!memcmp(wrbuf + 32, rdbuf, 32)); /* Delete second file */ assert_close_ok(1); - syscall_ok(unlink(TESTDIR "/file2")); + xunlink("file2"); - syscall_ok(stat(TESTDIR "/file", &sb)); + xstat("file", &sb); assert(sb.st_nlink == 2); - syscall_ok(unlink(TESTDIR "/file")); + xunlink("file"); - syscall_ok(stat(TESTDIR "/file3", &sb)); + xstat("file3", &sb); assert(sb.st_nlink == 1); - syscall_ok(unlink(TESTDIR "/file3")); + xunlink("file3"); syscall_fail(link("/tmp", "/tmp/foo"), EPERM); return 0; } -TEST_ADD(vfs_chmod) { +TEST_ADD(vfs_chmod, TF_TMPDIR) { struct stat sb; - assert(open(TESTDIR "/file", O_RDWR | O_CREAT, 0) == 3); - syscall_ok(stat(TESTDIR "/file", &sb)); + assert(xopen("file", O_RDWR | O_CREAT, 0) == 3); + xstat("file", &sb); assert((sb.st_mode & ALLPERMS) == 0); - syscall_ok(chmod(TESTDIR "/file", DEFFILEMODE)); - syscall_ok(stat(TESTDIR "/file", &sb)); + xchmod("file", DEFFILEMODE); + xstat("file", &sb); assert((sb.st_mode & ALLPERMS) == DEFFILEMODE); mode_t mode = S_IXGRP | S_IWOTH | S_IRUSR | S_ISUID; - syscall_ok(chmod(TESTDIR "/file", mode)); - syscall_ok(stat(TESTDIR "/file", &sb)); + xchmod("file", mode); + xstat("file", &sb); assert((sb.st_mode & ALLPERMS) == mode); mode_t lmode = S_IWUSR | S_IRWXU | S_IRWXO; - syscall_ok(symlink(TESTDIR "/file", TESTDIR "/link")); - syscall_ok(lchmod(TESTDIR "/link", lmode)); - syscall_ok(stat(TESTDIR "/link", &sb)); + xsymlink("file", "link"); + xlchmod("link", lmode); + xstat("link", &sb); assert((sb.st_mode & ALLPERMS) == mode); - syscall_ok(lstat(TESTDIR "/link", &sb)); + xlstat("link", &sb); assert((sb.st_mode & ALLPERMS) == lmode); - unlink(TESTDIR "/file"); - unlink(TESTDIR "/link"); + xunlink("file"); + xunlink("link"); return 0; } diff --git a/bin/utest/vm_map.c b/bin/utest/vm_map.c index 13a4a15623..38e2851b97 100644 --- a/bin/utest/vm_map.c +++ b/bin/utest/vm_map.c @@ -10,13 +10,12 @@ #include #include -TEST_ADD(sharing_memory_simple) { +TEST_ADD(sharing_memory_simple, 0) { size_t pgsz = getpagesize(); char *map = - mmap(NULL, pgsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); - assert(map != (char *)MAP_FAILED); + xmmap(NULL, pgsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); - pid_t pid = fork(); + pid_t pid = xfork(); assert(pid >= 0); if (pid == 0) { @@ -26,25 +25,22 @@ TEST_ADD(sharing_memory_simple) { } /* parent */ - wait_for_child_exit(pid, 0); - assert(strcmp(map, "Hello, World!") == 0); - assert(munmap(map, pgsz) == 0); + wait_child_finished(pid); + string_eq(map, "Hello, World!"); + xmunmap(map, pgsz); return 0; } -TEST_ADD(sharing_memory_child_and_grandchild) { +TEST_ADD(sharing_memory_child_and_grandchild, 0) { size_t pgsz = getpagesize(); char *map = - mmap(NULL, pgsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); - assert(map != (char *)MAP_FAILED); + xmmap(NULL, pgsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); - pid_t pid = fork(); - assert(pid >= 0); + pid_t pid = xfork(); if (pid == 0) { /* child */ - pid = fork(); - assert(pid >= 0); + pid = xfork(); if (pid == 0) { /* grandchild */ @@ -53,15 +49,15 @@ TEST_ADD(sharing_memory_child_and_grandchild) { } /* child */ - wait_for_child_exit(pid, 0); - assert(strcmp(map, "Hello from grandchild!") == 0); + wait_child_finished(pid); + string_eq(map, "Hello from grandchild!"); strcpy(map, "Hello from child!"); exit(0); } /* parent */ - wait_for_child_exit(pid, 0); - assert(strcmp(map, "Hello from child!") == 0); - assert(munmap(map, pgsz) == 0); + wait_child_finished(pid); + string_eq(map, "Hello from child!"); + xmunmap(map, pgsz); return 0; } diff --git a/bin/utest/vm_map_prot.c b/bin/utest/vm_map_prot.c index c5fbd46a3f..8d2cc5c144 100644 --- a/bin/utest/vm_map_prot.c +++ b/bin/utest/vm_map_prot.c @@ -20,14 +20,14 @@ int func_dec(int x) { return x - 1; } -TEST_ADD(vmmap_text_access) { +TEST_ADD(vmmap_text_access, 0) { func_int_t fun = func_dec; assert((uintptr_t)func_inc < (uintptr_t)func_dec); - printf("func_inc: 0x%p\n", func_inc); - printf("func_dec: 0x%p\n", func_dec); - printf("Writing data at address: 0x%p\n", fun); + debug("func_inc: %p", func_inc); + debug("func_dec: %p", func_dec); + debug("Writing data at address: %p", fun); char *p = (char *)func_inc; char prev_value0 = p[0]; @@ -39,11 +39,12 @@ TEST_ADD(vmmap_text_access) { } CLEANUP_SIGNAL(); CHECK_SIGSEGV(&si, p, SEGV_ACCERR); - printf("SIGSEGV address: %p\nSIGSEGV code: %d\n", si.si_addr, si.si_code); + debug("SIGSEGV address: %p", si.si_addr); + debug("SIGSEGV code: %d", si.si_code); return 0; } -TEST_ADD(vmmap_data_access) { +TEST_ADD(vmmap_data_access, 0) { static char func_buf[1024]; assert((uintptr_t)func_inc < (uintptr_t)func_dec); @@ -62,36 +63,39 @@ TEST_ADD(vmmap_data_access) { } CLEANUP_SIGNAL(); CHECK_SIGSEGV(&si, ff, SEGV_ACCERR); - printf("SIGSEGV address: %p\nSIGSEGV code: %d\n", si.si_addr, si.si_code); + debug("SIGSEGV address: %p", si.si_addr); + debug("SIGSEGV code: %d", si.si_code); return 0; } /* String must be alinged as instuctions because we jump here */ static __aligned(4) const char ro_data_str[] = "String in .rodata section"; -TEST_ADD(vmmap_rodata_access) { +TEST_ADD(vmmap_rodata_access, 0) { siginfo_t si; /* Check write */ volatile char *c = (char *)ro_data_str; - printf("mem to write: 0x%p\n", c); + debug("mem to write: 0x%p", c); EXPECT_SIGNAL(SIGSEGV, &si) { /* Writing to rodata segment. */ *c = 'X'; } CLEANUP_SIGNAL(); CHECK_SIGSEGV(&si, c, SEGV_ACCERR); - printf("SIGSEGV address: %p\nSIGSEGV code: %d\n", si.si_addr, si.si_code); + debug("SIGSEGV address: %p", si.si_addr); + debug("SIGSEGV code: %d", si.si_code); /* Check execute */ func_int_t fun = (func_int_t)ro_data_str; - printf("func: 0x%p\n", fun); + debug("func: 0x%p", fun); EXPECT_SIGNAL(SIGSEGV, &si) { /* Executing from rodata segment. */ fun(1); } CLEANUP_SIGNAL(); CHECK_SIGSEGV(&si, fun, SEGV_ACCERR); - printf("SIGSEGV address: %p\nSIGSEGV code: %d\n", si.si_addr, si.si_code); + debug("SIGSEGV address: %p", si.si_addr); + debug("SIGSEGV code: %d", si.si_code); return 0; } diff --git a/bin/utest/wait.c b/bin/utest/wait.c index a1772054e7..a35ca48b1c 100644 --- a/bin/utest/wait.c +++ b/bin/utest/wait.c @@ -7,7 +7,7 @@ #include static int nothing_to_report(pid_t pid) { - return (waitpid(pid, NULL, WCONTINUED | WUNTRACED | WNOHANG) == 0); + return xwaitpid(pid, NULL, WCONTINUED | WUNTRACED | WNOHANG) == 0; } /* ======= wait_basic ======= */ @@ -17,33 +17,30 @@ static void sigcont_handler(int signo) { sigcont_handled = 1; } -TEST_ADD(wait_basic) { +TEST_ADD(wait_basic, 0) { ppid = getpid(); - signal(SIGCONT, sigcont_handler); - int pid = fork(); + xsignal(SIGCONT, sigcont_handler); + pid_t pid = xfork(); if (pid == 0) { while (!sigcont_handled) sched_yield(); sigcont_handled = 0; - kill(ppid, SIGCONT); + xkill(ppid, SIGCONT); while (!sigcont_handled) sched_yield(); return 0; } - int status; /* Nothing has happened yet, so waitpid shouldn't report anything. */ assert(nothing_to_report(pid)); - kill(pid, SIGSTOP); - assert(waitpid(pid, &status, WUNTRACED) == pid); - assert(WIFSTOPPED(status)); + xkill(pid, SIGSTOP); + wait_child_stopped(pid); assert(nothing_to_report(pid)); - kill(pid, SIGCONT); - assert(waitpid(pid, &status, WCONTINUED) == pid); - assert(WIFCONTINUED(status)); + xkill(pid, SIGCONT); + wait_child_continued(pid); assert(nothing_to_report(pid)); @@ -51,46 +48,40 @@ TEST_ADD(wait_basic) { while (!sigcont_handled) sched_yield(); - kill(pid, SIGCONT); - assert(waitpid(pid, &status, 0) == pid); - assert(WIFEXITED(status)); - assert(WEXITSTATUS(status) == 0); + xkill(pid, SIGCONT); + wait_child_finished(pid); return 0; } /* ======= wait_nohang ======= */ -TEST_ADD(wait_nohang) { +TEST_ADD(wait_nohang, 0) { ppid = getpid(); - signal(SIGCONT, sigcont_handler); - int pid = fork(); + xsignal(SIGCONT, sigcont_handler); + pid_t pid = xfork(); if (pid == 0) { while (!sigcont_handled) sched_yield(); sigcont_handled = 0; - kill(ppid, SIGCONT); + xkill(ppid, SIGCONT); while (!sigcont_handled) sched_yield(); return 0; } - int status, rv; + int status; /* Nothing has happened yet, so waitpid shouldn't report anything. */ assert(nothing_to_report(pid)); - kill(pid, SIGSTOP); - while ((rv = waitpid(pid, &status, WUNTRACED | WNOHANG)) != pid) { - assert(rv == 0); + xkill(pid, SIGSTOP); + while (xwaitpid(pid, &status, WUNTRACED | WNOHANG) != pid) sched_yield(); - } assert(WIFSTOPPED(status)); assert(nothing_to_report(pid)); - kill(pid, SIGCONT); - while ((rv = waitpid(pid, &status, WCONTINUED | WNOHANG)) != pid) { - assert(rv == 0); + xkill(pid, SIGCONT); + while (xwaitpid(pid, &status, WCONTINUED | WNOHANG) != pid) sched_yield(); - } assert(WIFCONTINUED(status)); assert(nothing_to_report(pid)); @@ -99,11 +90,9 @@ TEST_ADD(wait_nohang) { while (!sigcont_handled) sched_yield(); - kill(pid, SIGCONT); - while ((rv = waitpid(pid, &status, WNOHANG)) != pid) { - assert(rv == 0); + xkill(pid, SIGCONT); + while (xwaitpid(pid, &status, WNOHANG) != pid) sched_yield(); - } assert(WIFEXITED(status)); assert(WEXITSTATUS(status) == 0); return 0; diff --git a/build/build.kern.mk b/build/build.kern.mk index 6e1e5627ab..2f15628d57 100644 --- a/build/build.kern.mk +++ b/build/build.kern.mk @@ -25,11 +25,12 @@ include $(TOPDIR)/config.mk SOURCES_ALL = $(SOURCES) SOURCES_ALL += $(foreach var, $(CONFIG_OPTS), $(value SOURCES-$(var))) +include $(TOPDIR)/build/flags.kern.mk + SOURCES += $(foreach var, $(CONFIG_OPTS), \ $(if $(subst 0,,$(value $(var))), \ $(value SOURCES-$(var)),)) -include $(TOPDIR)/build/flags.kern.mk include $(TOPDIR)/build/compile.mk include $(TOPDIR)/build/common.mk diff --git a/build/common.mk b/build/common.mk index c0f89604f3..33cf70f849 100644 --- a/build/common.mk +++ b/build/common.mk @@ -30,7 +30,7 @@ $(BUILDDIR)%.S: %.c $(BUILDDIR)%.o: %.c @echo "[CC] $(SRCPATH) -> $(DSTPATH)" - $(CC) $(CFLAGS) $(CFLAGS.$*.c) $(CFLAGS_KASAN) $(CFLAGS_KCSAN) $(CFLAGS_KGPROF) $(CPPFLAGS) $(WFLAGS) \ + $(CC) $(CFLAGS) $(CFLAGS.$*.c) $(CFLAGS_KASAN) $(CFLAGS_KCSAN) $(CFLAGS_KFI) $(CPPFLAGS) $(WFLAGS) \ -c -o $@ $(realpath $<) $(BUILDDIR)%.o: %.S diff --git a/build/flags.kern.mk b/build/flags.kern.mk index 351bff0f37..1c65b9fdf8 100644 --- a/build/flags.kern.mk +++ b/build/flags.kern.mk @@ -8,7 +8,7 @@ include $(TOPDIR)/build/flags.mk CFLAGS += -fno-builtin -nostdinc -nostdlib -ffreestanding CPPFLAGS += -I$(TOPDIR)/include -I$(TOPDIR)/sys/contrib -D_KERNEL -CPPFLAGS += -DLOCKDEP=$(LOCKDEP) -DKASAN=$(KASAN) -DKGPROF=$(KGPROF) -DKCSAN=$(KCSAN) +CPPFLAGS += -DLOCKDEP=$(LOCKDEP) -DKASAN=$(KASAN) -DKCSAN=$(KCSAN) -DKFI=$(KFI) LDFLAGS += -nostdlib ifeq ($(KCSAN), 1) @@ -44,8 +44,16 @@ ifeq ($(KASAN), 1) -wrap=strlen endif -ifeq ($(KGPROF), 1) - CFLAGS_KGPROF = -finstrument-functions +ifeq ($(KFI),ftrace) + CFLAGS_KFI += -finstrument-functions + CPPFLAGS += -DKFTRACE + KFTRACE = 1 +endif + +ifeq ($(KFI),gprof) + CFLAGS_KFI += -finstrument-functions + CPPFLAGS += -DKGPROF + KGPROF = 1 endif KERNEL := 1 diff --git a/config.mk b/config.mk index 5c5801c24b..61a0074e43 100644 --- a/config.mk +++ b/config.mk @@ -4,7 +4,7 @@ # build system for given platform. # -CONFIG_OPTS := KASAN LOCKDEP KGPROF MIPS AARCH64 RISCV KCSAN +CONFIG_OPTS := KASAN LOCKDEP KGPROF MIPS AARCH64 RISCV KCSAN KFTRACE BOARD ?= rpi3 @@ -40,6 +40,7 @@ VERBOSE ?= 0 LLVM ?= 1 LOCKDEP ?= 0 KASAN ?= 0 -KGPROF ?= 0 KCSAN ?= 0 +# Kernel function instrumentation options: ftrace, gprof +KFI ?= TRAP_USER_ACCESS ?= 0 diff --git a/contrib/Makefile b/contrib/Makefile index 052e58500d..64c29d385f 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -2,7 +2,7 @@ TOPDIR = $(realpath ..) -SUBDIR = atto lua sbase sinit # doom mish evtest +SUBDIR = atto lua sbase sinit ubase # doom mish evtest all: build diff --git a/contrib/sbase/Makefile b/contrib/sbase/Makefile index ba1e4861fc..2f8b6dc2ab 100644 --- a/contrib/sbase/Makefile +++ b/contrib/sbase/Makefile @@ -100,8 +100,10 @@ BIN = \ false \ find \ fold \ + getconf \ grep \ head \ + hostname \ join \ kill \ link \ @@ -171,8 +173,6 @@ BIN_DISABLED = \ du \ ed \ flock \ - getconf \ - hostname \ logger \ mknod \ tftp @@ -191,6 +191,13 @@ FORMAT-EXCLUDE = $(SOURCES) $(addprefix sbase/,$(HDR)) WFLAGS += -Wno-error=sign-compare -Wno-error=implicit-fallthrough +$(BUILDDIR)sbase/getconf.o: $(BUILDDIR)/sbase/getconf.h + +$(BUILDDIR)/sbase/getconf.h: sbase/getconf.sh + ./sbase/getconf.sh > $@ + +CFLAGS.sbase/getconf.c += -I$(BUILDDIR)/sbase -Wno-error=type-limits + include $(TOPDIR)/build/build.prog.mk $(PROGRAM).c: diff --git a/contrib/sinit/patches/series b/contrib/sinit/patches/series index 4462183fcd..50a6981cff 100644 --- a/contrib/sinit/patches/series +++ b/contrib/sinit/patches/series @@ -1 +1,2 @@ +sinit.patch config.patch diff --git a/contrib/sinit/patches/sinit.patch b/contrib/sinit/patches/sinit.patch new file mode 100644 index 0000000000..a18e883924 --- /dev/null +++ b/contrib/sinit/patches/sinit.patch @@ -0,0 +1,24 @@ +Index: sinit/sinit/sinit.c +=================================================================== +--- sinit.orig/sinit/sinit.c ++++ sinit/sinit/sinit.c +@@ -6,6 +6,8 @@ + #include + #include + #include ++#include ++#include + + #define LEN(x) (sizeof (x) / sizeof *(x)) + #define TIMEO 30 +@@ -40,6 +42,10 @@ main(void) + chdir("/"); + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, NULL); ++ /* Disassociate the controlling terminal from init. File descriptors ++ 0, 1 and 2 are already opened and attached to /dev/tty (which is the ++ controlling terminal), so we can call TIOCNOTTY on one of them */ ++ ioctl(0, TIOCNOTTY, (void *)0); + spawn(rcinitcmd); + while (1) { + alarm(TIMEO); diff --git a/contrib/ubase/.gitignore b/contrib/ubase/.gitignore new file mode 100644 index 0000000000..94e2540a91 --- /dev/null +++ b/contrib/ubase/.gitignore @@ -0,0 +1 @@ +ubase-box.c diff --git a/contrib/ubase/Makefile b/contrib/ubase/Makefile new file mode 100644 index 0000000000..c52e60e54e --- /dev/null +++ b/contrib/ubase/Makefile @@ -0,0 +1,133 @@ +TOPDIR = $(realpath ../..) + + +HDR = \ + arg.h \ + compat.h \ + crypt.h \ + fs.h \ + md5.h \ + queue.h \ + sha1.h \ + sha224.h \ + sha256.h \ + sha384.h \ + sha512.h \ + sha512-224.h \ + sha512-256.h \ + text.h \ + utf.h \ + util.h + +LIBUTILSRC = \ + libutil/agetcwd.c \ + libutil/agetline.c \ + libutil/apathmax.c \ + libutil/concat.c \ + libutil/ealloc.c \ + libutil/eprintf.c \ + libutil/estrtol.c \ + libutil/estrtoul.c \ + libutil/explicit_bzero.c \ + libutil/passwd.c \ + libutil/proc.c \ + libutil/putword.c \ + libutil/recurse.c \ + libutil/strlcat.c \ + libutil/strlcpy.c \ + libutil/strtonum.c \ + libutil/tty.c + +BIN = \ + clear \ + getty \ + id \ + login \ + mesg \ + nologin \ + pagesize \ + respawn \ + stat \ + su \ + truncate \ + watch + +BIN_DONT_WORK = \ + blkdiscard \ + chvt \ + ctrlaltdel \ + dd \ + df \ + dmesg \ + eject \ + fallocate \ + free \ + freeramdisk \ + fsfreeze \ + halt \ + hwclock \ + insmod \ + killall5 \ + last \ + lastlog \ + lsmod \ + lsusb \ + mknod \ + mkswap \ + mount \ + mountpoint \ + passwd \ + pidof \ + pivot_root \ + ps \ + pwdx \ + readahead \ + rmmod \ + swaplabel \ + swapoff \ + swapon \ + switch_root \ + sysctl \ + umount \ + unshare \ + uptime \ + vtallow \ + who + +define rename_main_func = +CFLAGS.ubase/$(1).c += -Dmain=$$(subst -,_,$(1))_main +endef + +$(foreach bin,$(BIN),$(eval $(call rename_main_func,$(bin)))) + +PROGRAM = ubase-box +BINDIR = bin +BUILDDIR = build/ +SOURCES = $(PROGRAM).c $(addprefix ubase/,$(BIN:=.c)) $(addprefix ubase/,$(LIBUTILSRC)) +FORMAT-EXCLUDE = $(SOURCES) $(addprefix ubase/,$(HDR)) + +CFLAGS.ubase/stat.c += -Wno-error=format + +include $(TOPDIR)/build/build.prog.mk + +$(PROGRAM).c: + @echo "[PYTHON] generating $@" + ./box_src_generator.py --program $(PROGRAM) --tools $(BIN) + +download-here: ubase/.git + +build-before: quilt-patch + +install-here: + @echo "[INSTALL] $(BINDIR)/$(PROGRAM)" + for f in $(BIN); do ln -sf $(PROGRAM) $(SYSROOT)/$(BINDIR)/"$$f"; done + +clean-here: quilt-unpatch + rm -f $(PROGRAM).c $(PROGRAM).uelf + +$(OBJECTS): $(BUILDDIR)ubase/config.h + +$(BUILDDIR)%.h: %.def.h + cp $< $@ + +CPPFLAGS += -I$(BUILDDIR)/ubase diff --git a/contrib/ubase/box_src_generator.py b/contrib/ubase/box_src_generator.py new file mode 100755 index 0000000000..cb2ae79ee3 --- /dev/null +++ b/contrib/ubase/box_src_generator.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +import argparse + +from contextlib import redirect_stdout + + +def generate_ubase_box_source(program: str, tools: list): + main_prefixes = [tool.replace('-', '_') for tool in tools] + + print('#include ') + print('#include ') + print('#include ') + print('#include ') + print('#include "util.h"') + + # needed for su and login + print('char* envinit[1];') + + for prefix in main_prefixes: + print(f'int {prefix}_main(int, char**);') + + print('') + print('int main(int argc, char *argv[]) {') + print(' char *s = basename(argv[0]);') + print(f' if(!strcmp(s,\"{program}\")) {{') + print(' argc--;') + print(' argv++;') + print(' s = basename(argv[0]);') + print(' }') + + for tool, prefix in zip(tools, main_prefixes): + print(f' if(!strcmp(s, \"{tool}\"))') + print(f' return {prefix}_main(argc, argv);') + + for tool in tools: + print(f' fputs(\"{tool} \", stdout);') + print(' putchar(0xa);') + print(' return 0;') + print('}') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Generate ubase-box source file.') + parser.add_argument('-p', '--program', type=str, + help='Executable file name.') + parser.add_argument('-t', '--tools', nargs='+', + help='List of supported tools.') + + args = parser.parse_args() + + with open(args.program + '.c', 'w') as f: + with redirect_stdout(f): + generate_ubase_box_source(args.program, args.tools) diff --git a/contrib/ubase/patches/config.patch b/contrib/ubase/patches/config.patch new file mode 100644 index 0000000000..f1c52c9d56 --- /dev/null +++ b/contrib/ubase/patches/config.patch @@ -0,0 +1,14 @@ +Index: ubase/ubase/config.def.h +=================================================================== +--- ubase.orig/ubase/config.def.h 2023-01-15 19:44:20.149042875 +0100 ++++ ubase/ubase/config.def.h 2023-01-15 19:54:46.042355519 +0100 +@@ -1,7 +1,7 @@ + /* See LICENSE file for copyright and license details. */ + +-#define ENV_SUPATH "/bin" +-#define ENV_PATH "/bin" ++#define ENV_SUPATH "/usr/bin:/bin" ++#define ENV_PATH "/usr/bin:/bin" + #define PW_CIPHER "$6$" /* SHA-512 */ + #undef UTMP_PATH + #define UTMP_PATH "/var/run/utmp" diff --git a/contrib/ubase/patches/getty.patch b/contrib/ubase/patches/getty.patch new file mode 100644 index 0000000000..cd73e32bd8 --- /dev/null +++ b/contrib/ubase/patches/getty.patch @@ -0,0 +1,53 @@ +Index: ubase/ubase/getty.c +=================================================================== +--- ubase.orig/ubase/getty.c 2023-06-18 14:01:37.396275690 +0200 ++++ ubase/ubase/getty.c 2023-06-18 14:01:53.699608626 +0200 +@@ -15,8 +15,8 @@ + #include "config.h" + #include "util.h" + +-static char *tty = "/dev/tty1"; +-static char *defaultterm = "linux"; ++static char *tty = "/dev/uart"; ++static char *defaultterm = "xterm"; + + static void + usage(void) +@@ -58,7 +58,7 @@ + + setsid(); + +- fd = open(tty, O_RDWR); ++ fd = open(tty, O_RDWR | O_NOCTTY); + if (fd < 0) + eprintf("open %s:", tty); + if (isatty(fd) == 0) +@@ -67,19 +67,10 @@ + /* steal the controlling terminal if necessary */ + if (ioctl(fd, TIOCSCTTY, (void *)1) != 0) + weprintf("TIOCSCTTY: could not set controlling tty\n"); +- vhangup(); +- close(fd); + +- fd = open(tty, O_RDWR); +- if (fd < 0) +- eprintf("open %s:", tty); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); +- if (fchown(fd, 0, 0) < 0) +- weprintf("fchown %s:", tty); +- if (fchmod(fd, 0600) < 0) +- weprintf("fchmod %s:", tty); + if (fd > 2) + close(fd); + +@@ -118,7 +109,7 @@ + fflush(stdout); + + /* Flush pending input */ +- ioctl(0, TCFLSH, (void *)0); ++ ioctl(0, TIOCFLUSH, (void *)0); + memset(logname, 0, sizeof(logname)); + while (1) { + n = read(0, &c, 1); diff --git a/contrib/ubase/patches/libutil-passwd.patch b/contrib/ubase/patches/libutil-passwd.patch new file mode 100644 index 0000000000..7e8d088e0c --- /dev/null +++ b/contrib/ubase/patches/libutil-passwd.patch @@ -0,0 +1,42 @@ +Index: ubase/ubase/libutil/passwd.c +=================================================================== +--- ubase.orig/ubase/libutil/passwd.c 2023-09-03 22:01:32.019008176 +0200 ++++ ubase/ubase/libutil/passwd.c 2023-09-03 22:51:08.412241696 +0200 +@@ -4,7 +4,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -19,8 +18,8 @@ + int + pw_check(const struct passwd *pw, const char *pass) + { +- char *cryptpass, *p; +- struct spwd *spw; ++ const char *cryptpass, *p; ++ struct passwd* spw; + + p = pw->pw_passwd; + if (p[0] == '!' || p[0] == '*') { +@@ -37,15 +36,15 @@ + + if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') { + errno = 0; +- spw = getspnam(pw->pw_name); ++ spw = getpwnam(pw->pw_name); + if (!spw) { + if (errno) +- weprintf("getspnam: %s:", pw->pw_name); ++ weprintf("getpwnam: %s:", pw->pw_name); + else + weprintf("who are you?\n"); + return -1; + } +- p = spw->sp_pwdp; ++ p = spw->pw_passwd; + if (p[0] == '!' || p[0] == '*') { + weprintf("denied\n"); + return -1; diff --git a/contrib/ubase/patches/login.patch b/contrib/ubase/patches/login.patch new file mode 100644 index 0000000000..a2df5e80b3 --- /dev/null +++ b/contrib/ubase/patches/login.patch @@ -0,0 +1,53 @@ +Index: ubase/ubase/login.c +=================================================================== +--- ubase.orig/ubase/login.c 2023-06-21 00:29:16.879786976 +0200 ++++ ubase/ubase/login.c 2023-06-21 00:33:37.019778341 +0200 +@@ -25,11 +25,9 @@ + + memset(&usr, 0, sizeof(usr)); + +- usr.ut_type = USER_PROCESS; +- usr.ut_pid = getpid(); +- strlcpy(usr.ut_user, user, sizeof(usr.ut_user)); ++ strlcpy(usr.ut_name, user, sizeof(usr.ut_name)); + strlcpy(usr.ut_line, tty, sizeof(usr.ut_line)); +- usr.ut_tv.tv_sec = time(NULL); ++ usr.ut_time = time(NULL); + + fp = fopen(UTMP_PATH, "a"); + if (fp) { +@@ -37,15 +35,13 @@ + if (ferror(fp)) + weprintf("%s: write error:", UTMP_PATH); + fclose(fp); +- } else { +- weprintf("fopen %s:", UTMP_PATH); + } + } + + static int + dologin(struct passwd *pw, int preserve) + { +- char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell; ++ const char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell; + + if (preserve == 0) + clearenv(); +@@ -105,7 +101,7 @@ + gid = pw->pw_gid; + + /* Flush pending input */ +- ioctl(0, TCFLSH, (void *)0); ++ ioctl(0, TIOCFLUSH, (void *)0); + + pass = getpass("Password: "); + if (!pass) +@@ -115,7 +111,7 @@ + + tty = ttyname(0); + if (!tty) +- eprintf("ttyname:"); ++ tty = "unknown"; + + writeutmp(user, tty); + diff --git a/contrib/ubase/patches/mesg.patch b/contrib/ubase/patches/mesg.patch new file mode 100644 index 0000000000..1f5ec31775 --- /dev/null +++ b/contrib/ubase/patches/mesg.patch @@ -0,0 +1,13 @@ +Index: ubase/ubase/mesg.c +=================================================================== +--- ubase.orig/ubase/mesg.c 2023-09-06 21:03:02.246353135 +0200 ++++ ubase/ubase/mesg.c 2023-09-06 21:03:12.309686137 +0200 +@@ -18,7 +18,7 @@ + main(int argc, char *argv[]) + { + struct stat sb; +- mode_t mode; ++ mode_t mode = 0; + + ARGBEGIN { + default: diff --git a/contrib/ubase/patches/series b/contrib/ubase/patches/series new file mode 100644 index 0000000000..90def4f956 --- /dev/null +++ b/contrib/ubase/patches/series @@ -0,0 +1,8 @@ +mesg.patch +util.patch +getty.patch +login.patch +config.patch +su.patch +libutil-passwd.patch +watch.patch diff --git a/contrib/ubase/patches/su.patch b/contrib/ubase/patches/su.patch new file mode 100644 index 0000000000..c6fabd3fea --- /dev/null +++ b/contrib/ubase/patches/su.patch @@ -0,0 +1,31 @@ +Index: ubase/ubase/su.c +=================================================================== +--- ubase.orig/ubase/su.c 2023-06-18 17:13:13.022660405 +0200 ++++ ubase/ubase/su.c 2023-06-19 19:55:36.593117659 +0200 +@@ -21,7 +21,7 @@ + static int + dologin(struct passwd *pw) + { +- char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell; ++ const char *shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell; + char *term = getenv("TERM"); + clearenv(); + setenv("HOME", pw->pw_dir, 1); +@@ -50,7 +50,7 @@ + main(int argc, char *argv[]) + { + char *usr = "root", *pass; +- char *shell; ++ const char *shell; + struct passwd *pw; + char *newargv[2]; + uid_t uid; +@@ -102,7 +102,7 @@ + return dologin(pw); + } else { + shell = pw->pw_shell[0] == '\0' ? "/bin/sh" : pw->pw_shell; +- newargv[0] = shell; ++ newargv[0] = (char *)shell; + newargv[1] = NULL; + if (!pflag) { + setenv("HOME", pw->pw_dir, 1); diff --git a/contrib/ubase/patches/util.patch b/contrib/ubase/patches/util.patch new file mode 100644 index 0000000000..7f5eb12d4c --- /dev/null +++ b/contrib/ubase/patches/util.patch @@ -0,0 +1,18 @@ +Index: ubase/ubase/util.h +=================================================================== +--- ubase.orig/ubase/util.h 2023-06-19 19:59:09.923110619 +0200 ++++ ubase/ubase/util.h 2023-06-19 20:14:22.103080495 +0200 +@@ -12,6 +12,13 @@ + + #define LEN(x) (sizeof (x) / sizeof *(x)) + ++extern char** environ; ++extern char* envinit[1]; ++ ++static inline void clearenv(void) { ++ environ = envinit; ++} ++ + /* eprintf.c */ + extern char *argv0; + diff --git a/contrib/ubase/patches/watch.patch b/contrib/ubase/patches/watch.patch new file mode 100644 index 0000000000..93a98b145a --- /dev/null +++ b/contrib/ubase/patches/watch.patch @@ -0,0 +1,31 @@ +Index: ubase/ubase/watch.c +=================================================================== +--- ubase.orig/ubase/watch.c 2023-01-01 22:32:22.325582802 +0100 ++++ ubase/ubase/watch.c 2023-01-01 22:33:42.278913458 +0100 +@@ -17,7 +17,7 @@ + { + char cmd[BUFSIZ]; + char *end; +- useconds_t interval = 2 * 1E6; ++ unsigned int interval = 2; + float period; + int i; + +@@ -30,7 +30,7 @@ + eprintf("invalid interval\n"); + if (period < 0) + period = 0.1f; +- interval = period * 1E6; ++ interval = period; + break; + default: + usage(); +@@ -52,7 +52,7 @@ + printf("\x1b[2J\x1b[H"); /* clear */ + fflush(NULL); + system(cmd); +- usleep(interval); ++ sleep(interval); + } + return 0; + } diff --git a/contrib/ubase/ubase b/contrib/ubase/ubase new file mode 160000 index 0000000000..3c88778c6c --- /dev/null +++ b/contrib/ubase/ubase @@ -0,0 +1 @@ +Subproject commit 3c88778c6c85d97fb63c41c05304519e0484b07c diff --git a/etc/rc.init b/etc/rc.init index 5ef891b160..b02583e80c 100644 --- a/etc/rc.init +++ b/etc/rc.init @@ -10,4 +10,4 @@ echo " | | | | | | | | | | | < __/ | | |__| |____) | " echo " |_| |_|_|_| |_| |_|_|_|\_\___|_| \____/|_____/ " echo -exec /bin/ksh +exec getty diff --git a/include/aarch64/kftrace.h b/include/aarch64/kftrace.h new file mode 100644 index 0000000000..41fed5075c --- /dev/null +++ b/include/aarch64/kftrace.h @@ -0,0 +1,9 @@ +#ifndef __AARCH64_KFTRACE_H__ +#define __AARCH64_KFTRACE_H__ + +#include + +#define KFT_EVENT_MAX 0x100000 +#define kft_get_time() READ_SPECIALREG(cntpct_el0) + +#endif /* __AARCH64_KFTRACE_H__ */ diff --git a/include/aarch64/pmap.h b/include/aarch64/pmap.h index 5d068a1c19..bb6af57d00 100644 --- a/include/aarch64/pmap.h +++ b/include/aarch64/pmap.h @@ -49,13 +49,13 @@ typedef struct pmap_md { * Page directory. */ -static inline bool pde_valid_p(pde_t *pdep) { +static __no_profile inline bool pde_valid_p(pde_t *pdep) { return pdep && PTE_FRAME_ADDR(*pdep) != 0; } void *phys_to_dmap(paddr_t addr); -static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { +static __no_profile inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { pde_t *pde = phys_to_dmap(pd_pa); if (lvl == 0) return pde + L0_INDEX(va); @@ -70,15 +70,15 @@ static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { * Page table. */ -static inline paddr_t pte_frame(pte_t pte) { +static __no_profile inline paddr_t pte_frame(pte_t pte) { return PTE_FRAME_ADDR(pte); } -static inline bool pte_valid_p(pte_t *ptep) { +static __no_profile inline bool pte_valid_p(pte_t *ptep) { return ptep && (pte_frame(*ptep) != 0); } -static inline bool pte_access(pte_t pte, vm_prot_t prot) { +static __no_profile inline bool pte_access(pte_t pte, vm_prot_t prot) { switch (prot) { case VM_PROT_READ: return pte & ATTR_SW_READ; @@ -95,16 +95,17 @@ static inline bool pte_access(pte_t pte, vm_prot_t prot) { * Physical map management. */ -static inline void pmap_md_setup(pmap_t *pmap) { +static __no_profile inline void pmap_md_setup(pmap_t *pmap) { } -static inline void pmap_md_delete(pmap_t *pmap) { +static __no_profile inline void pmap_md_delete(pmap_t *pmap) { } -static inline void pmap_md_growkernel(vaddr_t old_kva, vaddr_t new_kva) { +static __no_profile inline void pmap_md_growkernel(vaddr_t old_kva, + vaddr_t new_kva) { } -static inline void pmap_md_update(pmap_t *pmap) { +static __no_profile inline void pmap_md_update(pmap_t *pmap) { } #endif /* !_AARCH64_PMAP_H_ */ diff --git a/include/mips/kftrace.h b/include/mips/kftrace.h new file mode 100644 index 0000000000..f7d7b1779d --- /dev/null +++ b/include/mips/kftrace.h @@ -0,0 +1,13 @@ +#ifndef __MIPS_KFTRACE_H__ +#define __MIPS_KFTRACE_H__ + +#define _MACHDEP + +#include + +#define KFT_EVENT_MAX 0x4000 +#define kft_get_time() mips32_getcount() + +#undef _MACHDEP + +#endif /* __MIPS_KFTRACE_H__ */ diff --git a/include/mips/pmap.h b/include/mips/pmap.h index 4744f313bc..6528066f45 100644 --- a/include/mips/pmap.h +++ b/include/mips/pmap.h @@ -77,13 +77,13 @@ typedef struct pmap_md { * Page directory. */ -static inline bool pde_valid_p(pde_t *pdep) { +static __no_profile inline bool pde_valid_p(pde_t *pdep) { return pdep && (*pdep & PDE_VALID); } void *phys_to_dmap(paddr_t addr); -static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { +static __no_profile inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { pde_t *pde = phys_to_dmap(pd_pa); if (lvl == 0) return pde + PDE_INDEX(va); @@ -94,15 +94,15 @@ static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { * Page table. */ -static inline paddr_t pte_frame(pte_t pte) { +static __no_profile inline paddr_t pte_frame(pte_t pte) { return PTE_FRAME_ADDR(pte); } -static inline bool pte_valid_p(pte_t *ptep) { +static __no_profile inline bool pte_valid_p(pte_t *ptep) { return ptep && (pte_frame(*ptep) != 0); } -static inline bool pte_access(pte_t pte, vm_prot_t prot) { +static __no_profile inline bool pte_access(pte_t pte, vm_prot_t prot) { switch (prot) { case VM_PROT_READ: return pte & PTE_SW_READ; @@ -119,13 +119,14 @@ static inline bool pte_access(pte_t pte, vm_prot_t prot) { * Physical map management. */ -static inline void pmap_md_delete(pmap_t *pmap) { +static __no_profile inline void pmap_md_delete(pmap_t *pmap) { } -static inline void pmap_md_growkernel(vaddr_t old_kva, vaddr_t new_kva) { +static __no_profile inline void pmap_md_growkernel(vaddr_t old_kva, + vaddr_t new_kva) { } -static inline void pmap_md_update(pmap_t *pmap) { +static __no_profile inline void pmap_md_update(pmap_t *pmap) { } #endif /* __ASSEMBLER__ */ diff --git a/include/poll.h b/include/poll.h new file mode 100644 index 0000000000..efc6377880 --- /dev/null +++ b/include/poll.h @@ -0,0 +1,9 @@ +/* $OpenBSD: poll.h,v 1.3 2003/10/29 16:41:13 deraadt Exp $ */ + +/* + * Written by Theo de Raadt, Public Domain + * + * Typical poll() implementations expect poll.h to be in /usr/include. + * However this is not a convenient place for the real definitions. + */ +#include diff --git a/include/riscv/kftrace.h b/include/riscv/kftrace.h new file mode 100644 index 0000000000..d5cad710df --- /dev/null +++ b/include/riscv/kftrace.h @@ -0,0 +1,9 @@ +#ifndef __RISCV_KFTRACE_H__ +#define __RISCV_KFTRACE_H__ + +#include + +#define KFT_EVENT_MAX 0x100000 +#define kft_get_time() rdtime() + +#endif /* __RISCV_KFTRACE_H__ */ diff --git a/include/riscv/pmap.h b/include/riscv/pmap.h index b22ccfaea5..0ac572bf05 100644 --- a/include/riscv/pmap.h +++ b/include/riscv/pmap.h @@ -46,13 +46,13 @@ typedef struct pmap_md { * Page directory. */ -static inline bool pde_valid_p(pde_t *pdep) { +static __no_profile inline bool pde_valid_p(pde_t *pdep) { return pdep && VALID_PDE_P(*pdep); } void *phys_to_dmap(paddr_t addr); -static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { +static __no_profile inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { pde_t *pde = phys_to_dmap(pd_pa); if (lvl == 0) return pde + L0_INDEX(va); @@ -69,15 +69,15 @@ static inline pde_t *pde_ptr(paddr_t pd_pa, int lvl, vaddr_t va) { * Page table. */ -static inline paddr_t pte_frame(pte_t pte) { +static __no_profile inline paddr_t pte_frame(pte_t pte) { return PTE_TO_PA(pte); } -static inline bool pte_valid_p(pte_t *ptep) { +static __no_profile inline bool pte_valid_p(pte_t *ptep) { return ptep && VALID_PTE_P(*ptep); } -static inline bool pte_access(pte_t pte, vm_prot_t prot) { +static __no_profile inline bool pte_access(pte_t pte, vm_prot_t prot) { switch (prot) { case VM_PROT_READ: return pte & PTE_SW_READ; @@ -94,7 +94,7 @@ static inline bool pte_access(pte_t pte, vm_prot_t prot) { * Physical map management. */ -static inline void pmap_md_delete(pmap_t *pmap) { +static __no_profile inline void pmap_md_delete(pmap_t *pmap) { } #endif /* !_RISCV_PMAP_H_ */ diff --git a/include/stdlib.h b/include/stdlib.h index 01dba55423..7a283feb59 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -99,6 +99,9 @@ u_quad_t strtouq(const char *__restrict, char **__restrict, int); int setenv(const char *, const char *, int); int unsetenv(const char *); +/* IEEE Std 1003.1c-95, also adopted by X/Open CAE Spec Issue 5 Version 2 */ +int rand_r(unsigned int *); + /* * X/Open Portability Guide >= Issue 4 Version 2 */ diff --git a/include/sys/cdefs.h b/include/sys/cdefs.h index 5e53be249c..452a6b5080 100644 --- a/include/sys/cdefs.h +++ b/include/sys/cdefs.h @@ -74,7 +74,7 @@ #define __no_sanitize \ __attribute__((no_sanitize("address", "thread", "undefined"))) #define __no_instrument_function __attribute__((no_instrument_function)) -#ifdef KGPROF +#if defined(KFI) #define __no_profile __no_instrument_function #else #define __no_profile diff --git a/include/sys/fcntl.h b/include/sys/fcntl.h index 6dad0b5def..d528ef8d99 100644 --- a/include/sys/fcntl.h +++ b/include/sys/fcntl.h @@ -25,6 +25,10 @@ #define O_RDWR 0x00000002 /* open for reading and writing */ #define O_ACCMODE 0x00000003 /* mask for above modes */ +/* TIOCFLUSH ioctl flags */ +#define FREAD 0x00000001 +#define FWRITE 0x00000002 + /* * Kernel encoding of open mode; separate read and write bits that are * independently testable: 1 greater than the above. diff --git a/include/sys/interrupt.h b/include/sys/interrupt.h index 2fd033e785..5c0dce6f11 100644 --- a/include/sys/interrupt.h +++ b/include/sys/interrupt.h @@ -37,11 +37,11 @@ bool intr_disabled(void) __no_profile; /* Two following functions are workaround to make interrupt disabling work with * scoped and with statement. */ -static inline void __intr_disable(void *data) { +static __no_profile inline void __intr_disable(void *data) { intr_disable(); } -static inline void __intr_enable(void *data) { +static __no_profile inline void __intr_enable(void *data) { intr_enable(); } diff --git a/include/sys/kftrace.h b/include/sys/kftrace.h new file mode 100644 index 0000000000..cd7018eec1 --- /dev/null +++ b/include/sys/kftrace.h @@ -0,0 +1,11 @@ +#ifndef __KFTRACE_H__ +#define __KFTRACE_H__ + +#if KFTRACE +#include +void init_kftrace(void); +#else +#define init_kftrace() __nothing +#endif + +#endif /* __KFTRACE_H__ */ diff --git a/include/sys/ktest.h b/include/sys/ktest.h index ed5f93fa73..573027a88d 100644 --- a/include/sys/ktest.h +++ b/include/sys/ktest.h @@ -23,8 +23,6 @@ typedef enum test_flag { /* Excludes the test from being run in auto mode. This flag is only useful for * temporarily marking some tests while debugging the testing framework. */ KTEST_FLAG_BROKEN = 2, - /* Marks that the test wishes to receive a random integer as an argument. */ - KTEST_FLAG_RANDINT = 4, } test_flag_t; typedef struct { @@ -35,15 +33,12 @@ typedef struct { } test_entry_t; __noreturn void ktest_main(const char *test); +__noreturn void utest_main(const char *test); #define KTEST_ADD(name, func, flags) \ test_entry_t name##_test = {#name, func, flags, 0}; \ SET_ENTRY(tests, name##_test); -#define KTEST_ADD_RANDINT(name, func, flags, max) \ - test_entry_t name##_test = {#name, func, flags | KTEST_FLAG_RANDINT, max}; \ - SET_ENTRY(tests, name##_test); - /* This function is called both by run_test, as well as ktest_assert. * It displays some troubleshooting info about the failing test. */ void ktest_log_failure(void); diff --git a/include/sys/mimiker.h b/include/sys/mimiker.h index 0a9587bf32..c6a9cc164e 100644 --- a/include/sys/mimiker.h +++ b/include/sys/mimiker.h @@ -73,7 +73,7 @@ bool intr_disabled(void) __no_profile; #define CLEANUP_FUNCTION(func) __CONCAT(__cleanup_, func) #define DEFINE_CLEANUP_FUNCTION(type, func) \ - static inline void __cleanup_##func(type *ptr) { \ + static __no_profile inline void __cleanup_##func(type *ptr) { \ if (*ptr) \ func(*ptr); \ } \ diff --git a/include/sys/mutex.h b/include/sys/mutex.h index 885b33041c..f8ada996e8 100644 --- a/include/sys/mutex.h +++ b/include/sys/mutex.h @@ -83,7 +83,7 @@ bool mtx_owned(mtx_t *m); /*! \brief Fetch mutex owner. * * \note The function is used by some tests. */ -static inline thread_t *mtx_owner(mtx_t *m) { +static __no_profile inline thread_t *mtx_owner(mtx_t *m) { return (thread_t *)(m->m_owner & ~MTX_FLAGMASK); } @@ -93,7 +93,7 @@ void _mtx_lock(mtx_t *m, const void *waitpt) __no_profile; /*! \brief Locks sleep mutex. * * If mutex is already owned, then the thread is inserted into turnstile. */ -static inline void mtx_lock(mtx_t *m) { +static __no_profile inline void mtx_lock(mtx_t *m) { _mtx_lock(m, __caller(0)); } diff --git a/include/sys/poll.h b/include/sys/poll.h new file mode 100644 index 0000000000..98a1a67bc3 --- /dev/null +++ b/include/sys/poll.h @@ -0,0 +1,67 @@ +/* $NetBSD: poll.h,v 1.16 2020/07/17 15:34:16 kamil Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_POLL_H_ +#define _SYS_POLL_H_ + +typedef unsigned int nfds_t; + +struct pollfd { + int fd; /* file descriptor */ + short events; /* events to look for */ + short revents; /* events returned */ +}; + +/* + * Testable events (may be specified in events field). + */ +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLRDNORM 0x0040 +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0080 +#define POLLWRBAND 0x0100 + +/* + * Non-testable events (may not be specified in events field). + */ +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +#include + +__BEGIN_DECLS +int poll(struct pollfd *, nfds_t, int); +__END_DECLS + +#endif /* !_SYS_POLL_H_ */ diff --git a/include/sys/sched.h b/include/sys/sched.h index fc33aa4215..ae729d7f92 100644 --- a/include/sys/sched.h +++ b/include/sys/sched.h @@ -29,11 +29,11 @@ bool preempt_disabled(void); /* Two following functions are workaround to make preemption disabling work with * scoped and with statement. */ -static inline void __preempt_disable(void *data) { +static __no_profile inline void __preempt_disable(void *data) { preempt_disable(); } -static inline void __preempt_enable(void *data) { +static __no_profile inline void __preempt_enable(void *data) { preempt_enable(); } diff --git a/include/sys/signal.h b/include/sys/signal.h index 25030e8795..7077d24d7a 100644 --- a/include/sys/signal.h +++ b/include/sys/signal.h @@ -203,7 +203,7 @@ int do_sigaction(signo_t sig, const sigaction_t *act, sigaction_t *oldact); int do_sigprocmask(int how, const sigset_t *set, sigset_t *oset); int do_sigsuspend(proc_t *p, const sigset_t *mask); int do_sigpending(proc_t *p, sigset_t *set); -int do_sigtimedwait(proc_t *p, sigset_t waitset, ksiginfo_t *kinfo, +int do_sigtimedwait(proc_t *p, sigset_t waitset, ksiginfo_t *ksi, struct timespec *timeout); #endif /* !_KERNEL */ diff --git a/include/sys/sigtypes.h b/include/sys/sigtypes.h index 36c04d467c..8766f37c29 100644 --- a/include/sys/sigtypes.h +++ b/include/sys/sigtypes.h @@ -15,6 +15,8 @@ typedef struct { uint32_t __bits; /* bit 0 is unused */ } sigset_t; +#if defined(_KERNEL) || defined(_LIBC) + /* * Macro for manipulating signal masks. */ @@ -33,6 +35,8 @@ typedef struct { { (t)->__bits &= (s)->__bits; } #define __sigfindset(s) (__builtin_ffs((s)->__bits)) +#endif /* !_KERNEL */ + typedef struct sigaltstack { void *ss_sp; /* signal stack base */ size_t ss_size; /* signal stack length */ diff --git a/include/sys/syscall.h b/include/sys/syscall.h index 23d9b285b1..cc371d4566 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -93,6 +93,7 @@ #define SYS_kevent 85 #define SYS_sigtimedwait 86 #define SYS_clock_settime 87 -#define SYS_MAXSYSCALL 88 +#define SYS_pathconf 88 +#define SYS_MAXSYSCALL 89 #define SYS_MAXSYSARGS 6 diff --git a/include/sys/syscallargs.h b/include/sys/syscallargs.h index e1afce340f..31b0952622 100644 --- a/include/sys/syscallargs.h +++ b/include/sys/syscallargs.h @@ -447,3 +447,8 @@ typedef struct { SYSCALLARG(clockid_t) clock_id; SYSCALLARG(const struct timespec *) tp; } clock_settime_args_t; + +typedef struct { + SYSCALLARG(const char *) path; + SYSCALLARG(int) name; +} pathconf_args_t; diff --git a/include/sys/syslimits.h b/include/sys/syslimits.h index 2ebe8a24c9..de84944013 100644 --- a/include/sys/syslimits.h +++ b/include/sys/syslimits.h @@ -69,5 +69,6 @@ * IEEE Std 1003.1c-95, adopted in X/Open CAE Specification Issue 5 Version 2 */ #define LOGIN_NAME_MAX 17 /* max login name length incl. NUL */ +#define HOST_NAME_MAX 255 /* max hostname length w/o NUL */ #endif /* !_SYS_SYSLIMITS_H_ */ diff --git a/include/sys/thread.h b/include/sys/thread.h index 387644eb5b..825fc77c1c 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -70,6 +70,8 @@ typedef enum { } td_flags_t; typedef enum { + /* After user context setup in execve TDP_* flags that should not be inherited + * should be cleared */ TDP_OLDSIGMASK = 0x01, /* Pass td_oldsigmask as return mask to send_sig(). */ TDP_FPUCTXSAVED = 0x02, /* FPU context was saved by `ctx_switch`. */ TDP_FPUINUSE = 0x04 /* FPU is in use and its context should be saved & diff --git a/include/sys/time.h b/include/sys/time.h index acea02436f..9cab1e990c 100644 --- a/include/sys/time.h +++ b/include/sys/time.h @@ -51,17 +51,17 @@ typedef struct bintime { /* Returns seconds after EPOCH */ time_t tm2sec(tm_t *tm); -static inline systime_t bt2st(const bintime_t *bt) { +static __no_profile inline systime_t bt2st(const bintime_t *bt) { return bt->sec * CLK_TCK + (((uint64_t)CLK_TCK * (uint32_t)(bt->frac >> 32)) >> 32); } -static inline void bt2ts(const bintime_t *bt, timespec_t *ts) { +static __no_profile inline void bt2ts(const bintime_t *bt, timespec_t *ts) { ts->tv_sec = bt->sec; ts->tv_nsec = (1000000000ULL * (uint32_t)(bt->frac >> 32)) >> 32; } -static inline void bt2tv(const bintime_t *bt, timeval_t *tv) { +static __no_profile inline void bt2tv(const bintime_t *bt, timeval_t *tv) { tv->tv_sec = bt->sec; tv->tv_usec = (1000000ULL * (uint32_t)(bt->frac >> 32)) >> 32; } @@ -91,7 +91,7 @@ static inline void bt2tv(const bintime_t *bt, timeval_t *tv) { } \ } -static inline void tv2ts(const timeval_t *tv, timespec_t *ts) { +static __no_profile inline void tv2ts(const timeval_t *tv, timespec_t *ts) { ts->tv_sec = tv->tv_sec; ts->tv_nsec = tv->tv_usec * 1000; } @@ -102,26 +102,27 @@ static inline void tv2ts(const timeval_t *tv, timespec_t *ts) { : (((a)->sec)cmp((b)->sec))) #define bintime_isset(bt) ((bt)->sec || (bt)->frac) -static inline void bintime_add_frac(bintime_t *bt, uint64_t x) { +static __no_profile inline void bintime_add_frac(bintime_t *bt, uint64_t x) { uint64_t old_frac = bt->frac; bt->frac += x; if (old_frac > bt->frac) bt->sec++; } -static inline bintime_t bintime_mul(const bintime_t bt, uint32_t x) { +static __no_profile inline bintime_t bintime_mul(const bintime_t bt, + uint32_t x) { uint64_t p1 = (bt.frac & 0xffffffffULL) * x; uint64_t p2 = (bt.frac >> 32) * x + (p1 >> 32); return (bintime_t){.sec = bt.sec * x + (p2 >> 32), .frac = (p2 << 32) | (p1 & 0xffffffffULL)}; } -static inline void bintime_add(bintime_t *bt, bintime_t *bt2) { +static __no_profile inline void bintime_add(bintime_t *bt, bintime_t *bt2) { bintime_add_frac(bt, bt2->frac); bt->sec += bt2->sec; } -static inline void bintime_sub(bintime_t *bt, bintime_t *bt2) { +static __no_profile inline void bintime_sub(bintime_t *bt, bintime_t *bt2) { uint64_t old_frac = bt->frac; bt->frac -= bt2->frac; if (old_frac < bt->frac) @@ -197,7 +198,7 @@ void kitimer_init(proc_t *p); bool kitimer_stop(proc_t *p); /* Time measured from the start of system. */ -bintime_t binuptime(void); +__no_profile bintime_t binuptime(void); /* UTC/POSIX time */ bintime_t bintime(void); diff --git a/include/sys/ttycom.h b/include/sys/ttycom.h index cd74b9d47f..4b9d85d8c9 100644 --- a/include/sys/ttycom.h +++ b/include/sys/ttycom.h @@ -65,6 +65,7 @@ struct winsize { typedef char linedn_t[TTLINEDNAMELEN]; #define TIOCGLINED _IOR('t', 66, linedn_t) /* get line discipline (new) */ +#define TIOCFLUSH _IOW('t', 16, int) /* flush buffers */ #define TIOCGETA _IOR('t', 19, struct termios) /* get termios struct */ #define TIOCSETA _IOW('t', 20, struct termios) /* set termios struct */ #define TIOCSETAW _IOW('t', 21, struct termios) /* drain output, set */ @@ -75,6 +76,7 @@ typedef char linedn_t[TTLINEDNAMELEN]; #define TIOCPTSNAME _IOW('t', 105, char *) /* get slave device path */ #define TIOCSTART _IO('t', 110) /* start output, like ^Q */ #define TIOCSTOP _IO('t', 111) /* stop output, like ^S */ +#define TIOCNOTTY _IO('t', 113) /* void tty association */ #define TIOCGPGRP _IOR('t', 119, int) /* get pgrp of tty */ #define TIOCSPGRP _IOW('t', 118, int) /* set pgrp of tty */ #define TIOCGQSIZE _IOR('t', 129, int) /* get queue size */ diff --git a/include/sys/vfs.h b/include/sys/vfs.h index 91b7d55d4f..f42d0b4a94 100644 --- a/include/sys/vfs.h +++ b/include/sys/vfs.h @@ -103,6 +103,7 @@ int do_fchown(proc_t *p, int fd, uid_t uid, gid_t gid); int do_fchownat(proc_t *p, int fd, char *path, uid_t uid, gid_t gid, int flag); int do_futimens(proc_t *p, int fd, timespec_t *times); int do_utimensat(proc_t *p, int fd, char *path, timespec_t *times, int flag); +int do_pathconf(proc_t *p, char *path, int name, register_t *res); /* Mount a new instance of the filesystem named fs at the requested path. */ int do_mount(proc_t *p, const char *fs, const char *path); diff --git a/include/sys/vnode.h b/include/sys/vnode.h index e8aa368047..45791924e8 100644 --- a/include/sys/vnode.h +++ b/include/sys/vnode.h @@ -57,6 +57,7 @@ typedef int vnode_readlink_t(vnode_t *v, uio_t *uio); typedef int vnode_symlink_t(vnode_t *dv, componentname_t *cn, vattr_t *va, char *target, vnode_t **vp); typedef int vnode_link_t(vnode_t *dv, vnode_t *v, componentname_t *cn); +typedef int vnode_pathconf_t(vnode_t *v, int name, register_t *res); typedef struct vnodeops { vnode_lookup_t *v_lookup; @@ -78,6 +79,7 @@ typedef struct vnodeops { vnode_readlink_t *v_readlink; vnode_symlink_t *v_symlink; vnode_link_t *v_link; + vnode_pathconf_t *v_pathconf; } vnodeops_t; /* Fill missing entries with default vnode operation. */ @@ -210,6 +212,10 @@ static inline int VOP_LINK(vnode_t *dv, vnode_t *v, componentname_t *cn) { return VOP_CALL(link, dv, v, cn); } +static inline int VOP_PATHCONF(vnode_t *v, int name, register_t *res) { + return VOP_CALL(pathconf, v, name, res); +} + #undef VOP_CALL /* Allocates and initializes a new vnode */ @@ -239,6 +245,7 @@ int vnode_seek_generic(vnode_t *v, off_t oldoff, off_t newoff); int vnode_access_generic(vnode_t *v, accmode_t mode, cred_t *cred); /* When successful increments reference counter for given vnode.*/ int vnode_open_generic(vnode_t *v, int mode, file_t *fp); +int vnode_pathconf_generic(vnode_t *v, int name, register_t *res); /* Default fileops implementations for files with v-nodes. */ int default_vnread(file_t *f, uio_t *uio); diff --git a/include/unistd.h b/include/unistd.h index 7e6995ed89..95fd96868a 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -122,6 +122,7 @@ pid_t getpgid(pid_t); int lchown(const char *, uid_t, gid_t); int lockf(int, int, off_t); void *sbrk(intptr_t); +int sethostname(const char *, size_t); int setpgrp(pid_t, pid_t); int setregid(gid_t, gid_t); int setreuid(uid_t, uid_t); diff --git a/launch b/launch index 6c5dc4064d..2ef76e9599 100755 --- a/launch +++ b/launch @@ -171,6 +171,7 @@ CONFIG = { '-iex=set auto-load safe-path {}/'.format(os.getcwd()), '-ex=set tcp connect-timeout 30', '-ex=target extended-remote localhost:{gdbport}', + '-ex=source .gdbinit-common', '--silent', ], 'extra-options': [], @@ -514,6 +515,8 @@ if __name__ == '__main__': parser.add_argument('-s', '--storage', type=str, help='QCOW2 image to be attached as a default storage ' 'device for given platform.') + parser.add_argument('-k', '--kft', action='store_true', + help='Enable kftrace dump by GDB') args = parser.parse_args() # Used by tmux to override ./.tmux.conf with ./.tmux.conf.local @@ -569,6 +572,23 @@ if __name__ == '__main__': else: dbg = None + if args.kft: + kft_out = 'dump.kft' + print(f'KFTrace output will be saved to {kft_out}') + + # Remove a kft file because GDB only appends to it + try: + os.remove(kft_out) + except FileNotFoundError: + pass + except IsADirectoryError: + print(f"{kft_out} is a directory! " + "Can't use it to dump KFTrace data.") + sys.exit(1) + + setvar('gdb.post-options', ['-ex=source .gdbinit-kftrace']) + dbg = GDB() + if args.test_run: TestRun(sim, dbg, args.timeout) else: diff --git a/lib/libc/gen/gethostname.c b/lib/libc/gen/gethostname.c new file mode 100644 index 0000000000..f1aee79040 --- /dev/null +++ b/lib/libc/gen/gethostname.c @@ -0,0 +1,6 @@ +#include + +int gethostname(char *name, size_t namelen) { + strlcpy(name, "localhost", namelen); + return 0; +} diff --git a/lib/libc/gen/sethostname.c b/lib/libc/gen/sethostname.c new file mode 100644 index 0000000000..2494a9b413 --- /dev/null +++ b/lib/libc/gen/sethostname.c @@ -0,0 +1,7 @@ +#include +#include + +int sethostname(const char *name, size_t namelen) { + errno = ENOTSUP; + return -1; +} diff --git a/lib/libc/gen/sigismember.c b/lib/libc/gen/sigismember.c new file mode 100644 index 0000000000..0a38aa5f14 --- /dev/null +++ b/lib/libc/gen/sigismember.c @@ -0,0 +1,10 @@ +#include +#include + +int sigismember(const sigset_t *set, int signo) { + if (signo <= 0 || signo >= NSIG) { + errno = EINVAL; + return -1; + } + return (__sigismember(set, signo)); +} diff --git a/lib/libc/gen/utmp.c b/lib/libc/gen/utmp.c new file mode 100644 index 0000000000..de4dc096b5 --- /dev/null +++ b/lib/libc/gen/utmp.c @@ -0,0 +1,101 @@ +/* $NetBSD: utmp.c,v 1.10 2011/10/15 23:00:02 christos Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct utmp utmp; +static FILE *ut; +static char utfile[MAXPATHLEN] = _PATH_UTMP; + +void +setutent(void) +{ + if (ut == NULL) + return; + (void)fseeko(ut, (off_t)0, SEEK_SET); +} + +struct utmp * +getutent(void) +{ + if (ut == NULL) { + struct stat st; + off_t numentries; + if ((ut = fopen(utfile, "re")) == NULL) + return NULL; + if (fstat(fileno(ut), &st) == -1) + goto out; + /* + * If we have a an old version utmp file bail. + */ + numentries = st.st_size / sizeof(utmp); + if ((off_t)(numentries * sizeof(utmp)) != st.st_size) + goto out; + } + if (fread(&utmp, sizeof(utmp), 1, ut) == 1) + return &utmp; +out: + (void)fclose(ut); + return NULL; +} + +void +endutent(void) +{ + if (ut != NULL) { + (void)fclose(ut); + ut = NULL; + } +} + +int +utmpname(const char *fname) +{ + size_t len = strlen(fname); + + if (len >= sizeof(utfile)) + return 0; + + /* must not end in x! */ + if (fname[len - 1] == 'x') + return 0; + + (void)strlcpy(utfile, fname, sizeof(utfile)); + endutent(); + return 1; +} diff --git a/usr.bin/su/suutil.c b/lib/libc/stdlib/cxa_thread_atexit.c similarity index 50% rename from usr.bin/su/suutil.c rename to lib/libc/stdlib/cxa_thread_atexit.c index e8d6dff20e..f06d7f09ae 100644 --- a/usr.bin/su/suutil.c +++ b/lib/libc/stdlib/cxa_thread_atexit.c @@ -1,7 +1,7 @@ -/* $NetBSD: suutil.c,v 1.1 2007/10/17 21:05:39 christos Exp $ */ +/* $NetBSD: cxa_thread_atexit.c,v 1.1 2017/07/11 15:21:35 joerg Exp $ */ -/* - * Copyright (c) 1988 The Regents of the University of California. +/*- + * Copyright (c) 2017 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -12,14 +12,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -28,34 +25,52 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include -#include -#include -#include -#include -#include "suutil.h" +#include +#include -int -chshell(const char *sh) +#include "atexit.h" + +__dso_hidden bool __cxa_thread_atexit_used; + +struct cxa_dtor { + SLIST_ENTRY(cxa_dtor) link; + void *dso_symbol; + void *obj; + void (*dtor)(void *); +}; + +/* Assumes NULL initialization. */ +static /* __thread */ SLIST_HEAD(, cxa_dtor) cxa_dtors = SLIST_HEAD_INITIALIZER(cxa_dstors); + +void +__cxa_thread_run_atexit(void) { - const char *cp; + struct cxa_dtor *entry; - setusershell(); - while ((cp = getusershell()) != NULL) - if (strcmp(cp, sh) == 0) - return 1; - return 0; + while ((entry = SLIST_FIRST(&cxa_dtors)) != NULL) { + SLIST_REMOVE_HEAD(&cxa_dtors, link); + (*entry->dtor)(entry->obj); + free(entry); + } } -char * -ontty(void) +int __cxa_thread_atexit(void (*)(void *), void *, void *); + +int +__cxa_thread_atexit_impl(void (*dtor)(void *), void *obj, void *dso_symbol) { - const char *p; - static char buf[MAXPATHLEN + 4]; + struct cxa_dtor *entry; - buf[0] = 0; - if ((p = ttyname(STDERR_FILENO)) != NULL) - (void)snprintf(buf, sizeof buf, " on %s", p); - return buf; -} + __cxa_thread_atexit_used = true; + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return -1; + entry->dso_symbol = dso_symbol; + entry->obj = obj; + entry->dtor = dtor; + SLIST_INSERT_HEAD(&cxa_dtors, entry, link); + return 0; +} diff --git a/lib/libc/stdlib/exit.c b/lib/libc/stdlib/exit.c index 97be5156b1..4c07c4423e 100644 --- a/lib/libc/stdlib/exit.c +++ b/lib/libc/stdlib/exit.c @@ -32,6 +32,8 @@ #include #include #include +#include "atexit.h" +#include "reentrant.h" void (*__cleanup)(void); @@ -39,6 +41,9 @@ void (*__cleanup)(void); * Exit, flushing stdio buffers if necessary. */ void exit(int status) { + if (__cxa_thread_atexit_used) + __cxa_thread_run_atexit(); + __cxa_finalize(NULL); if (__cleanup) (*__cleanup)(); _exit(status); diff --git a/usr.bin/su/suutil.h b/lib/libc/stdlib/rand_r.c similarity index 86% rename from usr.bin/su/suutil.h rename to lib/libc/stdlib/rand_r.c index b4db687d2f..275fb66d11 100644 --- a/usr.bin/su/suutil.h +++ b/lib/libc/stdlib/rand_r.c @@ -1,7 +1,7 @@ -/* $NetBSD: suutil.h,v 1.1 2007/10/17 21:05:40 christos Exp $ */ +/* $NetBSD: rand_r.c,v 1.6 2012/06/25 22:32:45 abs Exp $ */ -/* - * Copyright (c) 1988 The Regents of the University of California. +/*- + * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,9 +28,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include +#include -__BEGIN_DECLS -int chshell(const char *); -char *ontty(void); -__END_DECLS +int +rand_r(unsigned int *seed) +{ + return ((*seed = *seed * 1103515245 + 12345) & RAND_MAX); +} diff --git a/lib/libc/sys/poll.c b/lib/libc/sys/poll.c new file mode 100644 index 0000000000..77e51223d2 --- /dev/null +++ b/lib/libc/sys/poll.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) { + int kq; + int ret; + struct kevent *events; + struct timespec timeout_ts; + int nevents = 0; + + if (nfds > (unsigned int)sysconf(_SC_OPEN_MAX)) { + errno = EINVAL; + return -1; + } + + if (timeout >= 0) { + timeout_ts.tv_sec = timeout / 1000; + timeout_ts.tv_nsec = (timeout % 1000) * 1000000; + } + /* if timeout == -1, then poll should wait indefinitely */ + else if (timeout < -1) { + errno = EINVAL; + return -1; + } + + kq = kqueue1(O_CLOEXEC); + if (kq < 0) + return -1; + + events = malloc(nfds * sizeof(struct kevent)); + if (!events) { + errno = ENOMEM; + ret = -1; + goto close_kq; + } + + for (nfds_t i = 0; i < nfds; i++) { + if (fds[i].events & POLLIN) + EV_SET(&events[nevents++], fds[i].fd, EVFILT_READ, EV_ADD, 0, 0, &fds[i]); + if (fds[i].events & POLLOUT) + EV_SET(&events[nevents++], fds[i].fd, EVFILT_WRITE, EV_ADD, 0, 0, + &fds[i]); + } + + ret = kevent(kq, events, nevents, events, nevents, + timeout == -1 ? NULL : &timeout_ts); + if (ret == -1) + goto end; + + for (int i = 0; i < ret; i++) { + struct pollfd *pfd = (struct pollfd *)events[i].udata; + + if (events[i].flags & EV_ERROR) { + errno = events[i].data; + if (errno == EBADF) + pfd->revents |= POLLNVAL; + else + pfd->revents |= POLLERR; + } else if (events[i].filter == EVFILT_READ) { + pfd->revents |= POLLIN; + } + else if (events[i].filter == EVFILT_WRITE) { + pfd->revents |= POLLOUT; + } + } + +end: + free(events); +close_kq: + close(kq); + return ret; +} diff --git a/lib/libc/sys/select.c b/lib/libc/sys/select.c index eb8b1b0fcf..5e84540103 100644 --- a/lib/libc/sys/select.c +++ b/lib/libc/sys/select.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, tv2ts(timeout, &timeout_ts); } - kq = kqueue(); + kq = kqueue1(O_CLOEXEC); if (kq < 0) return -1; diff --git a/lib/libc/sys/syscalls.S b/lib/libc/sys/syscalls.S index 5962a1f797..26c8bf78cc 100644 --- a/lib/libc/sys/syscalls.S +++ b/lib/libc/sys/syscalls.S @@ -86,3 +86,4 @@ SYSCALL(kqueue1, SYS_kqueue1) SYSCALL(kevent, SYS_kevent) SYSCALL(sigtimedwait, SYS_sigtimedwait) SYSCALL(clock_settime, SYS_clock_settime) +SYSCALL(pathconf, SYS_pathconf) diff --git a/run_tests.py b/run_tests.py index 6a71810ed6..4b29e83687 100755 --- a/run_tests.py +++ b/run_tests.py @@ -24,17 +24,19 @@ def setup_terminal(): subprocess.run(['stty', 'cols', str(cols), 'rows', str(rows)]) -def run_test(seed, board, timeout): - print("Testing seed %u..." % seed) +def run_test(seed, board, timeout, parallel, suite=None): + print('Testing seed %u...' % seed) + + args = ['%s=all' % suite, 'seed=%u' % seed, 'repeat=%d' % REPEAT, + 'parallel=%d' % parallel] try: launch = subprocess.Popen( - ['./launch', '--board', board, '-t', '--timeout=%d' % timeout, - 'test=all', 'seed=%u' % seed, 'repeat=%d' % REPEAT]) + ['./launch', '-b', board, '-t', '-T', str(timeout)] + args) rc = launch.wait() if rc: - print("Run `launch -d -b %s test=all seed=%u repeat=%u` " - "to reproduce the failure." % (board, seed, REPEAT)) + print('Run `launch -d -b %s %s` to reproduce the failure.' % + (board, ' '.join(args))) sys.exit(rc) except KeyboardInterrupt: launch.send_signal(signal.SIGINT) @@ -51,13 +53,22 @@ def run_test(seed, board, timeout): parser.add_argument('-b', '--board', default='rpi3', choices=['malta', 'rpi3', 'sifive_u'], help='Emulated board.') + parser.add_argument('-p', '--parallel', type=int, default=1, + help='Run at most N tests in parallel.') + parser.add_argument('-s', '--suite', default='all', + choices=['all', 'user', 'kernel'], + help='Test suite to run.') parser.add_argument('-T', '--timeout', type=int, default=DEFAULT_TIMEOUT, help='Test-run will fail after n seconds.') args = parser.parse_args() # Run tests using n random seeds for _ in range(0, args.times): - run_test(random.randint(0, 2**32), args.board, args.timeout) + rand = random.randint(0, 2**32) + if args.suite in ['all', 'kernel']: + run_test(rand, args.board, args.timeout, args.parallel, 'ktest') + if args.suite in ['all', 'user']: + run_test(rand, args.board, args.timeout, args.parallel, 'utest') - print("Tests successful!") + print('Tests successful!') sys.exit(0) diff --git a/sys/aarch64/Makefile b/sys/aarch64/Makefile index 3b4c200b04..14de0980d1 100644 --- a/sys/aarch64/Makefile +++ b/sys/aarch64/Makefile @@ -31,4 +31,4 @@ CPPFLAGS += -D_MACHDEP boot.o : CFLAGS_KASAN = boot.o : CFLAGS_KCSAN = -boot.o : CFLAGS_KGPROF = +boot.o : CFLAGS_KFI = diff --git a/sys/aarch64/pmap.c b/sys/aarch64/pmap.c index 063207e1b3..efbcdae9d7 100644 --- a/sys/aarch64/pmap.c +++ b/sys/aarch64/pmap.c @@ -106,7 +106,7 @@ void pmap_md_bootstrap(pde_t *pd __unused) { * Direct map. */ -void *phys_to_dmap(paddr_t addr) { +void __no_profile *phys_to_dmap(paddr_t addr) { assert((addr >= dmap_paddr_base) && (addr < dmap_paddr_end)); return (void *)(addr - dmap_paddr_base) + DMAP_BASE; } diff --git a/sys/aarch64/timer.c b/sys/aarch64/timer.c index e3e24faa94..b1d86f7cf2 100644 --- a/sys/aarch64/timer.c +++ b/sys/aarch64/timer.c @@ -36,7 +36,7 @@ static int arm_timer_stop(timer_t *tm) { return 0; } -static bintime_t arm_timer_gettime(timer_t *tm) { +static __no_profile bintime_t arm_timer_gettime(timer_t *tm) { uint64_t count = READ_SPECIALREG(cntpct_el0); bintime_t res = bintime_mul(tm->tm_min_period, (uint32_t)count); bintime_t high_bits = bintime_mul(tm->tm_min_period, (uint32_t)(count >> 32)); diff --git a/sys/contrib/libfdt/libfdt_env.h b/sys/contrib/libfdt/libfdt_env.h index 43fd1cc08a..e4e7b0af2c 100644 --- a/sys/contrib/libfdt/libfdt_env.h +++ b/sys/contrib/libfdt/libfdt_env.h @@ -85,29 +85,29 @@ typedef uint64_t FDT_BITWISE fdt64_t; (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) -static inline uint16_t fdt16_to_cpu(fdt16_t x) +static __no_profile inline uint16_t fdt16_to_cpu(fdt16_t x) { return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); } -static inline fdt16_t cpu_to_fdt16(uint16_t x) +static __no_profile inline fdt16_t cpu_to_fdt16(uint16_t x) { return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); } -static inline uint32_t fdt32_to_cpu(fdt32_t x) +static __no_profile inline uint32_t fdt32_to_cpu(fdt32_t x) { return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); } -static inline fdt32_t cpu_to_fdt32(uint32_t x) +static __no_profile inline fdt32_t cpu_to_fdt32(uint32_t x) { return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); } -static inline uint64_t fdt64_to_cpu(fdt64_t x) +static __no_profile inline uint64_t fdt64_to_cpu(fdt64_t x) { return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); } -static inline fdt64_t cpu_to_fdt64(uint64_t x) +static __no_profile inline fdt64_t cpu_to_fdt64(uint64_t x) { return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); } diff --git a/sys/debug/events.py b/sys/debug/events.py index 1469ca1ed3..d55431465f 100644 --- a/sys/debug/events.py +++ b/sys/debug/events.py @@ -32,10 +32,11 @@ def stop_handler(event): current_host_path = str(Path(current_host_path).resolve()) if loaded_host_path != current_host_path: try: - gdb.execute( - f'remove-symbol-file -a {hex(user_elf_base_addr)}', - to_string=True - ) + if loaded_host_path is not None: + gdb.execute( + f'remove-symbol-file -a {hex(user_elf_base_addr)}', + to_string=True + ) gdb.execute( f'add-symbol-file {current_host_path}', to_string=True) except: # noqa: E722 diff --git a/sys/gen/Makefile b/sys/gen/Makefile index 11acef6492..1dc2fc5bed 100644 --- a/sys/gen/Makefile +++ b/sys/gen/Makefile @@ -14,4 +14,4 @@ CPPFLAGS += -D_MACHDEP boot.o : CFLAGS_KASAN = boot.o : CFLAGS_KCSAN = -boot.o : CFLAGS_KGPROF = +boot.o : CFLAGS_KFI = diff --git a/sys/gen/pmap.c b/sys/gen/pmap.c index 01a524b04c..82c7a59e1a 100644 --- a/sys/gen/pmap.c +++ b/sys/gen/pmap.c @@ -45,40 +45,41 @@ static MTX_DEFINE(pv_list_lock, 0); * Helper functions. */ -static inline pde_t page_offset(vaddr_t addr) { +static __no_profile inline pde_t page_offset(vaddr_t addr) { return addr & (PAGESIZE - 1); } -static bool kern_addr_p(vaddr_t addr) { +static __no_profile bool kern_addr_p(vaddr_t addr) { return addr >= KERNEL_SPACE_BEGIN && addr < KERNEL_SPACE_END; } -vaddr_t pmap_start(pmap_t *pmap) { +vaddr_t __no_profile pmap_start(pmap_t *pmap) { return pmap->asid ? USER_SPACE_BEGIN : KERNEL_SPACE_BEGIN; } -vaddr_t pmap_end(pmap_t *pmap) { +vaddr_t __no_profile pmap_end(pmap_t *pmap) { return pmap->asid ? USER_SPACE_END : KERNEL_SPACE_END; } -static bool pmap_address_p(pmap_t *pmap, vaddr_t va) { +static __no_profile bool pmap_address_p(pmap_t *pmap, vaddr_t va) { return va >= pmap_start(pmap) && va < pmap_end(pmap); } -static bool pmap_contains_p(pmap_t *pmap, vaddr_t start, vaddr_t end) { +static __no_profile bool pmap_contains_p(pmap_t *pmap, vaddr_t start, + vaddr_t end) { assert(start < end); return start >= pmap_start(pmap) && end <= pmap_end(pmap); } -inline pmap_t *pmap_kernel(void) { +__no_profile inline pmap_t *pmap_kernel(void) { return &kernel_pmap; } -inline pmap_t *pmap_user(void) { +__no_profile inline pmap_t *pmap_user(void) { return PCPU_GET(curpmap); } -static void *pg_dmap_addr(vm_page_t *pg) { +static __no_profile void *pg_dmap_addr(vm_page_t *pg) { return phys_to_dmap(pg->paddr); } @@ -88,11 +89,11 @@ static vm_page_t *pmap_pagealloc(void) { return pg; } -void pmap_zero_page(vm_page_t *pg) { +void __no_profile pmap_zero_page(vm_page_t *pg) { memset((void *)pg_dmap_addr(pg), 0, PAGESIZE); } -void pmap_copy_page(vm_page_t *src, vm_page_t *dst) { +void __no_profile pmap_copy_page(vm_page_t *src, vm_page_t *dst) { memcpy((void *)pg_dmap_addr(dst), (void *)pg_dmap_addr(src), PAGESIZE); } @@ -165,7 +166,7 @@ paddr_t pde_alloc(pmap_t *pmap) { return pg->paddr; } -static pte_t *pmap_lookup_pte(pmap_t *pmap, vaddr_t va) { +static __no_profile pte_t *pmap_lookup_pte(pmap_t *pmap, vaddr_t va) { pde_t *pdep = pde_ptr(pmap->pde, 0, va); for (int lvl = 1; lvl < PAGE_TABLE_DEPTH; lvl++) { diff --git a/sys/kern/Makefile b/sys/kern/Makefile index 7a3d0ea105..5af693e9b5 100644 --- a/sys/kern/Makefile +++ b/sys/kern/Makefile @@ -31,7 +31,6 @@ SOURCES = \ kenv.c \ klog.c \ kmem.c \ - ktest.c \ main.c \ malloc.c \ mutex.c \ @@ -75,6 +74,9 @@ SOURCES-KASAN = \ SOURCES-LOCKDEP = \ lockdep.c +SOURCES-KFTRACE = \ + kftrace.c + SOURCES-KGPROF = \ kgprof.c \ mcount.c @@ -92,5 +94,7 @@ sysent.h $(SYSCALL_H) $(SYSCALLARGS_H): syscalls.conf syscalls.master $(TOPDIR)/sys/script/makesyscalls.sh $^ kasan.o : CFLAGS_KASAN = -mcount.o : CFLAGS_KGPROF = +mcount.o : CFLAGS_KFI = kcsan.o : CFLAGS_KCSAN = +kftrace.o : CFLAGS_KFI = +timer.o : CPPFLAGS += -DUNIXTIME_NOW=$(shell date +%s) diff --git a/sys/kern/clock.c b/sys/kern/clock.c index 67852a68fa..8df54b1dba 100644 --- a/sys/kern/clock.c +++ b/sys/kern/clock.c @@ -6,12 +6,12 @@ #include #include -static systime_t now = 0; +static _Atomic(systime_t) now = 0; static timer_t *clock = NULL; static timer_t *profclock = NULL; systime_t getsystime(void) { - return now; + return atomic_load(&now); } static void prof_clock(timer_t *tm, void *arg) { @@ -20,7 +20,7 @@ static void prof_clock(timer_t *tm, void *arg) { static void clock_cb(timer_t *tm, void *arg) { bintime_t bin = binuptime(); - now = bt2st(&bin); + atomic_store(&now, bt2st(&bin)); if (profclock == NULL) prof_clock(tm, arg); callout_process(now); diff --git a/sys/kern/exec.c b/sys/kern/exec.c index a098e5ecdb..98b3cc7dee 100644 --- a/sys/kern/exec.c +++ b/sys/kern/exec.c @@ -413,6 +413,9 @@ static int _do_execve(exec_args_t *args) { /* Set up user context. */ mcontext_init(td->td_uctx, (void *)eh.e_entry, (void *)stack_top); + /* All thread pflags that should not be inherited should be cleared */ + td->td_pflags = 0; + WITH_PROC_LOCK(p) { sig_onexec(p); /* Set new credentials if needed */ diff --git a/sys/kern/initrd.c b/sys/kern/initrd.c index b2de15ef36..d3b7cdb644 100644 --- a/sys/kern/initrd.c +++ b/sys/kern/initrd.c @@ -13,6 +13,7 @@ #include #include #include +#include typedef uint32_t cpio_dev_t; typedef uint32_t cpio_ino_t; @@ -267,6 +268,16 @@ static int initrd_vnode_readlink(vnode_t *v, uio_t *uio) { uio); } +static int initrd_vnode_pathconf(vnode_t *v, int name, register_t *res) { + switch (name) { + case _PC_NAME_MAX: + *res = NAME_MAX; + return 0; + default: + return vnode_pathconf_generic(v, name, res); + } +} + static inline cpio_node_t *vn2cn(vnode_t *v) { return (cpio_node_t *)v->v_data; } @@ -344,7 +355,8 @@ static vnodeops_t initrd_vops = {.v_lookup = initrd_vnode_lookup, .v_seek = vnode_seek_generic, .v_getattr = initrd_vnode_getattr, .v_access = vnode_access_generic, - .v_readlink = initrd_vnode_readlink}; + .v_readlink = initrd_vnode_readlink, + .v_pathconf = initrd_vnode_pathconf}; static int initrd_init(vfsconf_t *vfc) { vnodeops_init(&initrd_vops); diff --git a/sys/kern/kftrace.c b/sys/kern/kftrace.c new file mode 100644 index 0000000000..25479d6570 --- /dev/null +++ b/sys/kern/kftrace.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +/* Kernel Function Trace + * + * In this file there are defined structures and functions that are used to + * implement function tracking in kernel. + * + * When entering or exiting a function an event is added to `kft_event_list`. + * The size of the list is specified by `KFT_EVENT_MAX`. + * + * When the list is full the function `kft_flush` is invoked. Currently it only + * resets the event count. The main purpose of it is to set a breakpoint on it + * and dump the contents of the list of events to a file by the debugger. + * + * Information about the event is compressed into a single 64 bit value. + * Below is a picture of layout of information encoded in that value. + * + * +---------+-----------+-----------+------------+ + * | 24 bits | 31 bits | 8 bits | 1 bit | + * | PC | timestamp | thread id | event type | + * +---------+-----------+-----------+------------+ + */ + +typedef uint64_t kft_event_t; + +static kft_event_t *kft_event_list; +static unsigned kft_used = 0; +static MTX_DEFINE(kft_lock, MTX_SPIN); + +#define PC_MASK 0xFFFFFC /* 22 bits */ +#define TIMESTAMP_MASK 0x1FFFFFFFF /* 33 bits */ +#define THREAD_MASK 0xFF /* 8 bits */ +#define TYPE_MASK 0x1 /* 1 bit */ + +#define PC_SHIFT 40 +#define TIMESTAMP_SHIFT 9 +#define THREAD_SHIFT 1 +#define TYPE_SHIFT 0 + +typedef enum kft_event_type { + KFT_EVENT_FUN_IN, + KFT_EVENT_FUN_OUT, +} kft_event_type_t; + +void init_kftrace(void) { + kft_event_list = kmem_alloc(sizeof(kft_event_t) * (KFT_EVENT_MAX), M_WAITOK); +} + +static __no_profile inline kft_event_t make_event(kft_event_type_t type, + tid_t thread, + uint64_t timestamp, + uint64_t pc) { + return ((type & TYPE_MASK) << TYPE_SHIFT) | + ((thread & THREAD_MASK) << THREAD_SHIFT) | + ((timestamp & TIMESTAMP_MASK) << TIMESTAMP_SHIFT) | + ((pc & PC_MASK) << PC_SHIFT); +} + +/* XXX: function for debugger breakpoint. */ +static __no_profile void kft_flush(void) { + /* Free the kft event list because all events are recorded by debugger. */ + kft_used = 0; +} + +static __no_profile void add_event(void *pc, kft_event_type_t type) { + if (!kft_event_list) + return; + + SCOPED_MTX_LOCK(&kft_lock); + + uint64_t time = kft_get_time(); + tid_t thread = thread_self()->td_tid; + + uintptr_t rel_pc = (uintptr_t)pc - (uintptr_t)__kernel_start; + kft_event_list[kft_used++] = make_event(type, thread, time, rel_pc); + + /* If buffer is full flush it. */ + if (kft_used >= KFT_EVENT_MAX) { + kft_flush(); + } +} + +__no_profile void __cyg_profile_func_enter(void *this_fn, void *call_site) { + add_event(this_fn, KFT_EVENT_FUN_IN); +} + +__no_profile void __cyg_profile_func_exit(void *this_fn, void *call_site) { + add_event(this_fn, KFT_EVENT_FUN_OUT); +} diff --git a/sys/kern/klog.c b/sys/kern/klog.c index 018cbc7ca7..d47f3d83c3 100644 --- a/sys/kern/klog.c +++ b/sys/kern/klog.c @@ -46,16 +46,16 @@ static const char *subsystems[] = { [KL_TTY] = "tty", [KL_UNDEF] = "???", }; -void init_klog(void) { +void __no_profile init_klog(void) { const char *mask = kenv_get("klog-mask"); klog.mask = mask ? (unsigned)strtol(mask, NULL, 16) : KL_DEFAULT_MASK; } -static inline unsigned next(unsigned i) { +static __no_profile inline unsigned next(unsigned i) { return (i + 1) % KL_SIZE; } -static void klog_entry_dump(klog_entry_t *entry) { +static __no_profile void klog_entry_dump(klog_entry_t *entry) { if (entry->kl_origin == KL_UNDEF) kprintf("[%s:%d] ", entry->kl_file, entry->kl_line); else @@ -66,7 +66,7 @@ static void klog_entry_dump(klog_entry_t *entry) { kprintf("\n"); } -static void klog_entry_add(klog_entry_t *newentry) { +static __no_profile void klog_entry_add(klog_entry_t *newentry) { klog_entry_t *entry = &klog.array[klog.last]; memcpy(entry, newentry, sizeof(klog_entry_t)); @@ -76,10 +76,10 @@ static void klog_entry_add(klog_entry_t *newentry) { klog.first = next(klog.first); } -void klog_append(klog_origin_t origin, const char *file, unsigned line, - const char *format, uintptr_t arg1, uintptr_t arg2, - uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, - uintptr_t arg6) { +void __no_profile klog_append(klog_origin_t origin, const char *file, + unsigned line, const char *format, uintptr_t arg1, + uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, + uintptr_t arg5, uintptr_t arg6) { if (!(KL_MASK(origin) & atomic_load(&klog.mask))) return; @@ -134,11 +134,11 @@ void klog_append(klog_origin_t origin, const char *file, unsigned line, } } -unsigned klog_setmask(unsigned newmask) { +unsigned __no_profile klog_setmask(unsigned newmask) { return atomic_exchange(&klog.mask, newmask); } -void klog_dump(void) { +void __no_profile klog_dump(void) { klog_entry_t entry; while (klog.first != klog.last) { @@ -167,18 +167,19 @@ static __noreturn void halt(void) { continue; } -__noreturn void klog_panic(klog_origin_t origin, const char *file, - unsigned line, const char *format, uintptr_t arg1, - uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, - uintptr_t arg5, uintptr_t arg6) { +__no_profile __noreturn void klog_panic(klog_origin_t origin, const char *file, + unsigned line, const char *format, + uintptr_t arg1, uintptr_t arg2, + uintptr_t arg3, uintptr_t arg4, + uintptr_t arg5, uintptr_t arg6) { klog.mask = -1; klog_append(origin, file, line, format, arg1, arg2, arg3, arg4, arg5, arg6); ktest_log_failure(); halt(); } -__noreturn void klog_assert(klog_origin_t origin, const char *file, - unsigned line, const char *expr) { +__no_profile __noreturn void klog_assert(klog_origin_t origin, const char *file, + unsigned line, const char *expr) { klog_append(origin, file, line, "Assertion \"%s\" failed!", (intptr_t)expr, 0, 0, 0, 0, 0); ktest_log_failure(); diff --git a/sys/kern/main.c b/sys/kern/main.c index 1cd9de8d06..76a2787284 100644 --- a/sys/kern/main.c +++ b/sys/kern/main.c @@ -29,6 +29,7 @@ #include #include #include +#include /* This function mounts some initial filesystems. Normally this would be done by userspace init program. */ @@ -37,7 +38,9 @@ static void mount_fs(void) { do_mount(p, "initrd", "/"); do_mount(p, "devfs", "/dev"); do_mount(p, "tmpfs", "/tmp"); + do_mount(p, "tmpfs", "/root"); do_fchmodat(p, AT_FDCWD, "/tmp", ACCESSPERMS | S_ISTXT, 0); + do_fchmodat(p, AT_FDCWD, "/root", S_IRWXU, 0); } static __noreturn void start_init(__unused void *arg) { @@ -67,10 +70,13 @@ static __noreturn void start_init(__unused void *arg) { assert(_stdout == 1); assert(_stderr == 2); - char *test = kenv_get("test"); - if (test) + char *test; + if ((test = kenv_get("ktest"))) ktest_main(test); + if ((test = kenv_get("utest"))) + utest_main(test); + char *init = kenv_get("init"); if (init == NULL) init = "/sbin/init"; @@ -117,6 +123,7 @@ __noreturn void kernel_init(void) { init_clock(); init_kgprof(); + init_kftrace(); klog("Kernel initialized!"); diff --git a/sys/kern/mutex.c b/sys/kern/mutex.c index 2cff000be5..1201112500 100644 --- a/sys/kern/mutex.c +++ b/sys/kern/mutex.c @@ -5,7 +5,7 @@ #include #include -bool mtx_owned(mtx_t *m) { +bool __no_profile mtx_owned(mtx_t *m) { return (mtx_owner(m) == thread_self()); } @@ -20,7 +20,7 @@ void _mtx_init(mtx_t *m, intptr_t flags, const char *name, #endif } -void _mtx_lock(mtx_t *m, const void *waitpt) { +void __no_profile _mtx_lock(mtx_t *m, const void *waitpt) { intptr_t flags = m->m_owner & (MTX_SPIN | MTX_NODEBUG); if (flags & MTX_SPIN) { @@ -71,7 +71,7 @@ void _mtx_lock(mtx_t *m, const void *waitpt) { } } -void mtx_unlock(mtx_t *m) { +void __no_profile mtx_unlock(mtx_t *m) { intptr_t flags = m->m_owner & (MTX_SPIN | MTX_NODEBUG); assert(mtx_owned(m)); diff --git a/sys/kern/proc.c b/sys/kern/proc.c index 6ef5ac062d..ff053536f1 100644 --- a/sys/kern/proc.c +++ b/sys/kern/proc.c @@ -433,15 +433,15 @@ int pgrp_enter(proc_t *p, pid_t target, pgid_t pgid) { } /* Process functions */ -proc_t *proc_self(void) { +__no_profile proc_t *proc_self(void) { return thread_self()->td_proc; } -void proc_lock(proc_t *p) { +__no_profile void proc_lock(proc_t *p) { mtx_lock(&p->p_lock); } -void proc_unlock(proc_t *p) { +__no_profile void proc_unlock(proc_t *p) { mtx_unlock(&p->p_lock); } diff --git a/sys/kern/signal.c b/sys/kern/signal.c index 4195fba973..63eb1c42a0 100644 --- a/sys/kern/signal.c +++ b/sys/kern/signal.c @@ -13,6 +13,9 @@ #include #include +#include +#include + static KMALLOC_DEFINE(M_SIGNAL, "signal"); /*!\brief Signal properties. */ @@ -201,6 +204,11 @@ int do_sigsuspend(proc_t *p, const sigset_t *mask) { return ERESTARTNOHAND; } +int do_sigtimedwait(proc_t *p, sigset_t waitset, ksiginfo_t *ksi, + timespec_t *tsp) { + return ENOTSUP; +} + int do_sigpending(proc_t *p, sigset_t *set) { SCOPED_MTX_LOCK(&p->p_lock); thread_t *td = p->p_thread; diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 01a33b7320..13f0faab32 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -1333,10 +1333,54 @@ static int sys_kevent(proc_t *p, kevent_args_t *args, register_t *res) { static int sys_sigtimedwait(proc_t *p, sigtimedwait_args_t *args, register_t *res) { - return ENOTSUP; + const sigset_t *u_set = SCARG(args, set); + siginfo_t *u_info = SCARG(args, info); + const timespec_t *u_timeout = SCARG(args, timeout); + sigset_t set; + ksiginfo_t ksi; + timespec_t timeout = {}; + int error; + + if (u_timeout) { + error = copyin_s(u_timeout, timeout); + if (error) + return error; + } + + if ((error = copyin_s(u_set, set))) + return error; + + if ((error = do_sigtimedwait(p, set, &ksi, u_timeout ? &timeout : NULL))) + return error; + + if (u_info) + error = copyout_s(ksi.ksi_info, u_info); + + *res = ksi.ksi_info.si_signo; + + return error; } static int sys_clock_settime(proc_t *p, clock_settime_args_t *args, register_t *res) { return ENOTSUP; } + +static int sys_pathconf(proc_t *p, pathconf_args_t *args, register_t *res) { + int error; + int name = SCARG(args, name); + const char *u_path = SCARG(args, path); + + char *path = kmalloc(M_TEMP, PATH_MAX, 0); + + if ((error = copyinstr(u_path, path, PATH_MAX, NULL))) + goto end; + + klog("pathconf(\"%s\", %d)", path, name); + + error = do_pathconf(p, path, name, res); + +end: + kfree(M_TEMP, path); + return error; +} diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index b0521cc816..0bb23c2ae4 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -102,5 +102,6 @@ 85 { int sys_kevent(int kq, const struct kevent *changelist, size_t nchanges, struct kevent *eventlist, size_t nevents, const struct timespec *timeout); } 86 { int sys_sigtimedwait(const sigset_t *set, siginfo_t *info, struct timespec *timeout); } 87 { int sys_clock_settime(clockid_t clock_id, const struct timespec *tp); } +88 { long sys_pathconf(const char *path, int name); } ; vim: ts=4 sw=4 sts=4 et diff --git a/sys/kern/sysent.h b/sys/kern/sysent.h index 22e2339924..d91464cfeb 100644 --- a/sys/kern/sysent.h +++ b/sys/kern/sysent.h @@ -93,6 +93,7 @@ static int sys_kqueue1(proc_t *, kqueue1_args_t *, register_t *); static int sys_kevent(proc_t *, kevent_args_t *, register_t *); static int sys_sigtimedwait(proc_t *, sigtimedwait_args_t *, register_t *); static int sys_clock_settime(proc_t *, clock_settime_args_t *, register_t *); +static int sys_pathconf(proc_t *, pathconf_args_t *, register_t *); struct sysent sysent[] = { [SYS_syscall] = { .name = "syscall", .nargs = 1, .call = (syscall_t *)sys_syscall }, @@ -183,5 +184,6 @@ struct sysent sysent[] = { [SYS_kevent] = { .name = "kevent", .nargs = 6, .call = (syscall_t *)sys_kevent }, [SYS_sigtimedwait] = { .name = "sigtimedwait", .nargs = 3, .call = (syscall_t *)sys_sigtimedwait }, [SYS_clock_settime] = { .name = "clock_settime", .nargs = 2, .call = (syscall_t *)sys_clock_settime }, + [SYS_pathconf] = { .name = "pathconf", .nargs = 2, .call = (syscall_t *)sys_pathconf }, }; diff --git a/sys/kern/timer.c b/sys/kern/timer.c index 3888209996..786b606028 100644 --- a/sys/kern/timer.c +++ b/sys/kern/timer.c @@ -9,7 +9,7 @@ static MTX_DEFINE(timers_mtx, 0); static timer_list_t timers = TAILQ_HEAD_INITIALIZER(timers); static timer_t *time_source = NULL; -static bintime_t boottime = BINTIME(0); +static bintime_t boottime = BINTIME(UNIXTIME_NOW); /* These flags are used internally to encode timer state. * Following state transitions are possible: diff --git a/sys/kern/tmpfs.c b/sys/kern/tmpfs.c index 18c9f9118a..66bd0c560f 100644 --- a/sys/kern/tmpfs.c +++ b/sys/kern/tmpfs.c @@ -13,6 +13,7 @@ #include #include #include +#include /* * All memory used by the tmpfs is organized in the list of arenas. A single @@ -614,6 +615,16 @@ static int tmpfs_vop_link(vnode_t *dv, vnode_t *v, componentname_t *cn) { return 0; } +static int tmpfs_vop_pathconf(vnode_t *v, int name, register_t *res) { + switch (name) { + case _PC_NAME_MAX: + *res = TMPFS_NAME_MAX; + return 0; + default: + return vnode_pathconf_generic(v, name, res); + } +} + static vnodeops_t tmpfs_vnodeops = {.v_lookup = tmpfs_vop_lookup, .v_readdir = tmpfs_vop_readdir, .v_open = vnode_open_generic, @@ -631,7 +642,8 @@ static vnodeops_t tmpfs_vnodeops = {.v_lookup = tmpfs_vop_lookup, .v_reclaim = tmpfs_vop_reclaim, .v_readlink = tmpfs_vop_readlink, .v_symlink = tmpfs_vop_symlink, - .v_link = tmpfs_vop_link}; + .v_link = tmpfs_vop_link, + .v_pathconf = tmpfs_vop_pathconf}; /* tmpfs internal routines */ diff --git a/sys/kern/tty.c b/sys/kern/tty.c index b2f8a4ddc8..1fa7e24871 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -989,6 +989,24 @@ static int tty_set_winsize(tty_t *tty, struct winsize *sz) { return 0; } +static int tty_drop_ctty(proc_t *p, tty_t *tty) { + SCOPED_MTX_LOCK(&all_proc_mtx); + SCOPED_MTX_LOCK(&tty->t_lock); + + session_t *session = p->p_pgrp->pg_session; + if (session->s_tty == NULL || session->s_tty != tty) + return ENOTTY; + + if (!proc_is_session_leader(p)) + return EPERM; + + session->s_tty = NULL; + tty->t_session = NULL; + tty->t_pgrp = NULL; + + return 0; +} + int tty_ioctl(file_t *f, u_long cmd, void *data) { tty_t *tty = f->f_data; @@ -998,6 +1016,20 @@ int tty_ioctl(file_t *f, u_long cmd, void *data) { *(int *)data = tty->t_inq.count; return 0; } + case TIOCFLUSH: { + SCOPED_MTX_LOCK(&tty->t_lock); + int flags = *(int *)data; + if (flags == 0) + flags = FREAD | FWRITE; + else + flags &= FREAD | FWRITE; + + if (flags & FREAD) + tty_discard_input(tty); + if (flags & FWRITE) + tty_discard_output(tty); + return 0; + } case TIOCGETA: return tty_get_termios(tty, (struct termios *)data); case TIOCSETA: /* Set termios immediately */ @@ -1022,6 +1054,8 @@ int tty_ioctl(file_t *f, u_long cmd, void *data) { return EPERM; return 0; } + case TIOCNOTTY: + return tty_drop_ctty(proc_self(), tty); case 0: return EPASSTHROUGH; default: { diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 37382e8397..b33f25cbaa 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -742,3 +742,16 @@ int do_utimensat(proc_t *p, int fd, char *path, timespec_t *times, int flag) { vnode_put(v); return error; } + +int do_pathconf(proc_t *p, char *path, int name, register_t *res) { + vnode_t *v; + int error; + + if ((error = vfs_namelookup(path, &v, &p->p_cred))) + return error; + + error = VOP_PATHCONF(v, name, res); + vnode_drop(v); + + return error; +} diff --git a/sys/kern/vfs_vnode.c b/sys/kern/vfs_vnode.c index 76599fcc1e..e2070dd24b 100644 --- a/sys/kern/vfs_vnode.c +++ b/sys/kern/vfs_vnode.c @@ -12,6 +12,7 @@ #include #include #include +#include static POOL_DEFINE(P_VNODE, "vnode", sizeof(vnode_t)); @@ -340,3 +341,25 @@ int vnode_access_generic(vnode_t *v, accmode_t acc, cred_t *cred) { return cred_can_access(&va, cred, acc); } + +int vnode_pathconf_generic(vnode_t *v, int name, register_t *res) { + switch (name) { + case _PC_LINK_MAX: + *res = LINK_MAX; + return 0; + case _PC_MAX_CANON: + *res = MAX_CANON; + return 0; + case _PC_MAX_INPUT: + *res = MAX_INPUT; + return 0; + case _PC_PATH_MAX: + *res = PATH_MAX; + return 0; + case _PC_PIPE_BUF: + *res = PIPE_BUF; + return 0; + default: + return EINVAL; + } +} diff --git a/sys/mips/Makefile b/sys/mips/Makefile index e95f11f972..f02b935f3f 100644 --- a/sys/mips/Makefile +++ b/sys/mips/Makefile @@ -29,4 +29,4 @@ CPPFLAGS += -D_MACHDEP boot.o : CFLAGS_KASAN = boot.o : CFLAGS_KCSAN = -boot.o : CFLAGS_KGPROF = +boot.o : CFLAGS_KFI = diff --git a/sys/mips/boot.c b/sys/mips/boot.c index f87ae7ade3..69a3dd026d 100644 --- a/sys/mips/boot.c +++ b/sys/mips/boot.c @@ -82,6 +82,9 @@ __boot_text void *mips_init(void) { vaddr_t va = MIPS_PHYS_TO_KSEG2(text); /* assume that kernel image will be covered by single PDE (4MiB) */ + if (ebss - text > (1 << PDE_INDEX_SHIFT)) + halt(); + pde[PDE_INDEX(va)] = PTE_PFN((paddr_t)pte) | PTE_KERNEL; /* read-only segment - sections: .text, .rodata, etc. */ diff --git a/sys/mips/pmap.c b/sys/mips/pmap.c index efb2c3c6bb..25e6295820 100644 --- a/sys/mips/pmap.c +++ b/sys/mips/pmap.c @@ -92,7 +92,7 @@ void pmap_md_bootstrap(pde_t *pd __unused) { * Direct map. */ -void *phys_to_dmap(paddr_t addr) { +__no_profile void *phys_to_dmap(paddr_t addr) { if (addr >= DMAP_BASE) return (void *)addr; return (void *)(addr - dmap_paddr_base) + DMAP_BASE; diff --git a/sys/mips/timer.c b/sys/mips/timer.c index 8a264ec9fb..08d28e5b93 100644 --- a/sys/mips/timer.c +++ b/sys/mips/timer.c @@ -97,7 +97,7 @@ static int mips_timer_stop(timer_t *tm) { return 0; } -static bintime_t mips_timer_gettime(timer_t *tm) { +static __no_profile bintime_t mips_timer_gettime(timer_t *tm) { device_t *dev = tm->tm_priv; mips_timer_state_t *state = dev->state; uint64_t sec; diff --git a/sys/mips/trap.c b/sys/mips/trap.c index dadfca3420..0710efad88 100644 --- a/sys/mips/trap.c +++ b/sys/mips/trap.c @@ -174,6 +174,8 @@ static void kern_trap_handler(ctx_t *ctx) { case EXC_MOD: case EXC_TLBL: case EXC_TLBS: + case EXC_TLBRI: + case EXC_TLBXI: klog("%s at $%08x, caused by reference to $%08lx!", exceptions[code], _REG(ctx, EPC), vaddr); if (pmap_fault_handler(ctx, vaddr, exc_access(code))) diff --git a/sys/riscv/Makefile b/sys/riscv/Makefile index 0eee303b95..dfddec1a44 100644 --- a/sys/riscv/Makefile +++ b/sys/riscv/Makefile @@ -28,4 +28,4 @@ CPPFLAGS += -D_MACHDEP boot.o : CFLAGS_KASAN = boot.o : CFLAGS_KCSAN = -boot.o : CFLAGS_KGPROF = +boot.o : CFLAGS_KFI = diff --git a/sys/riscv/pmap.c b/sys/riscv/pmap.c index 19d3259b06..52621dbe0d 100644 --- a/sys/riscv/pmap.c +++ b/sys/riscv/pmap.c @@ -76,7 +76,7 @@ pte_t pte_make(paddr_t pa, vm_prot_t prot, unsigned flags) { return pte; } -inline pte_t pte_protect(pte_t pte, vm_prot_t prot) { +__no_profile inline pte_t pte_protect(pte_t pte, vm_prot_t prot) { return (pte & ~PTE_PROT_MASK) | vm_prot_map[prot]; } @@ -199,7 +199,7 @@ void pmap_md_bootstrap(pde_t *pd) { * Direct map. */ -void *phys_to_dmap(paddr_t addr) { +__no_profile void *phys_to_dmap(paddr_t addr) { assert((addr >= dmap_paddr_base) && (addr < dmap_paddr_end)); return (void *)(addr - dmap_paddr_base) + DMAP_BASE; } diff --git a/sys/tests/Makefile b/sys/tests/Makefile index 225c1f5387..42ae1302be 100644 --- a/sys/tests/Makefile +++ b/sys/tests/Makefile @@ -3,6 +3,7 @@ TOPDIR = $(realpath ../..) SOURCES = \ + main.c \ broken.c \ callout.c \ crash.c \ @@ -27,7 +28,6 @@ SOURCES = \ turnstile_propagate_once.c \ turnstile_propagate_many.c \ uiomove.c \ - utest.c \ vm_map.c \ devclass.c \ vfs.c \ diff --git a/sys/kern/ktest.c b/sys/tests/main.c similarity index 78% rename from sys/kern/ktest.c rename to sys/tests/main.c index f58513b7f3..fbf915e2cc 100644 --- a/sys/kern/ktest.c +++ b/sys/tests/main.c @@ -1,11 +1,16 @@ #define KL_LOG KL_TEST #include #include +#include +#include #include +#include #include -#include #include -#include +#include +#include +#include +#include #define KTEST_MAX_NO 1024 @@ -60,17 +65,7 @@ static void run_test(test_entry_t *t) { klog("Running test \"%s\".", current_test->test_name); - test_func_t test_fn = (void *)t->test_func; - int randint = 0; - if (t->flags & KTEST_FLAG_RANDINT) { - /* NOTE: Numbers generated here will be the same on each run, since test are - started in a deterministic order. This is not a bug! In fact, it allows - to reproduce test cases easily, just by reusing the seed.*/ - /* TODO: Low discrepancy sampling? */ - randint = rand_r(&seed) % t->randint_max; - } - - if (test_fn(randint) == KTEST_FAILURE) + if (t->test_func() == KTEST_FAILURE) ktest_failure(); current_test = NULL; @@ -171,3 +166,41 @@ __noreturn void ktest_main(const char *test) { /* If we've managed to get here, it means all tests passed with no issues. */ ktest_success(); } + +#define UTEST_PATH "/bin/utest" + +static __noreturn void utest_thread(void *arg) { + char seed[32] = "seed="; + char repeat[32] = "repeat="; + char parallel[32] = "parallel="; + + strlcat(seed, kenv_get("seed") ?: "0", sizeof(seed)); + strlcat(repeat, kenv_get("repeat") ?: "1", sizeof(repeat)); + strlcat(parallel, kenv_get("parallel") ?: "1", sizeof(parallel)); + + kern_execve(UTEST_PATH, (char *[]){UTEST_PATH, arg, NULL}, + (char *[]){seed, repeat, parallel, NULL}); +} + +/* This is the klog mask used with utests. */ +#define KL_UTEST_MASK \ + (KL_ALL & (~(KL_MASK(KL_INTR) | KL_MASK(KL_PMAP) | KL_MASK(KL_PHYSMEM)))) + +__noreturn void utest_main(const char *test) { + const char *mask = kenv_get("klog-utest-mask"); + klog_setmask(mask ? (unsigned)strtol(mask, NULL, 16) : KL_UTEST_MASK); + + pid_t cpid; + if (do_fork(utest_thread, (void *)test, &cpid)) + panic("Could not start test!"); + + int status; + pid_t pid = 0; + do_waitpid(cpid, &status, 0, &pid); + assert(cpid == pid); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + ktest_success(); + + ktest_failure(); +} diff --git a/sys/tests/sleepq_timed.c b/sys/tests/sleepq_timed.c index a0948f9c96..ca7787855c 100644 --- a/sys/tests/sleepq_timed.c +++ b/sys/tests/sleepq_timed.c @@ -66,7 +66,7 @@ static int test_sleepq_timed(void) { waker = thread_create("test-sleepq-waker", waker_routine, NULL, prio_kthread(0)); for (int i = 0; i < THREADS; i++) { - char name[20]; + char name[32]; snprintf(name, sizeof(name), "test-sleepq-waiter-%d", i); waiters[i] = thread_create(name, waiter_routine, NULL, prio_kthread(0) + RQ_PPQ); diff --git a/sys/tests/utest.c b/sys/tests/utest.c deleted file mode 100644 index 3c81b96325..0000000000 --- a/sys/tests/utest.c +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#define UTEST_PATH "/bin/utest" - -static __noreturn void utest_generic_thread(void *arg) { - proc_t *p = proc_self(); - /* Run user tests in a separate session. */ - int error = session_enter(p); - assert(error == 0); - kern_execve(UTEST_PATH, (char *[]){UTEST_PATH, arg, NULL}, (char *[]){NULL}); -} - -/* This is the klog mask used with utests. */ -#define KL_UTEST_MASK \ - (KL_ALL & (~(KL_MASK(KL_INTR) | KL_MASK(KL_PMAP) | KL_MASK(KL_PHYSMEM)))) - -static int utest_generic(const char *name, int status_success) { - const char *utest_mask = kenv_get("klog-utest-mask"); - unsigned new_klog_mask = - utest_mask ? (unsigned)strtol(utest_mask, NULL, 16) : KL_UTEST_MASK; - unsigned old_klog_mask = klog_setmask(new_klog_mask); - - /* Prefix test thread's name with `utest-`. */ - char prefixed_name[TD_NAME_MAX]; - snprintf(prefixed_name, TD_NAME_MAX, "utest-%s", name); - - klog("User test '%s' started", name); - - pid_t cpid; - if (do_fork(utest_generic_thread, (void *)name, &cpid)) - panic("Could not start test!"); - - int status; - pid_t pid; - do_waitpid(cpid, &status, 0, &pid); - assert(cpid == pid); - - /* Restore previous klog mask */ - /* XXX: If we'll use klog_setmask heavily, maybe we should consider - klog_{push,pop}_mask. */ - klog_setmask(old_klog_mask); - - klog("User test '%s' finished with status: %d (expected: %d)", name, status, - status_success); - if (status == status_success) - return KTEST_SUCCESS; - else - return KTEST_FAILURE; -} - -/* Adds a new test executed by running /bin/utest. */ -#define UTEST_ADD(name) \ - static int utest_test_##name(void) { \ - return utest_generic(#name, MAKE_STATUS_EXIT(0)); \ - } \ - KTEST_ADD(user_##name, utest_test_##name, KTEST_FLAG_USERMODE) - -UTEST_ADD(vmmap_text_access); -UTEST_ADD(vmmap_data_access); -UTEST_ADD(vmmap_rodata_access); - -UTEST_ADD(mmap); -UTEST_ADD(munmap); -UTEST_ADD(munmap_sigsegv); -UTEST_ADD(munmap_many_1); -UTEST_ADD(munmap_many_2); -UTEST_ADD(mmap_prot_none); -UTEST_ADD(mmap_prot_read); -UTEST_ADD(mmap_fixed); -UTEST_ADD(mmap_fixed_excl); -UTEST_ADD(mmap_fixed_replace); -UTEST_ADD(mmap_fixed_replace_many_1); -UTEST_ADD(mmap_fixed_replace_many_2); -UTEST_ADD(mprotect_fail); -UTEST_ADD(mprotect1); -UTEST_ADD(mprotect2); -UTEST_ADD(mprotect3); -UTEST_ADD(sbrk); -UTEST_ADD(sbrk_sigsegv); -UTEST_ADD(misbehave); - -UTEST_ADD(fd_read); -UTEST_ADD(fd_devnull); -UTEST_ADD(fd_multidesc); -UTEST_ADD(fd_readwrite); -UTEST_ADD(fd_copy); -UTEST_ADD(fd_bad_desc); -UTEST_ADD(fd_open_path); -UTEST_ADD(fd_dup); -UTEST_ADD(fd_pipe); -UTEST_ADD(fd_readv); -UTEST_ADD(fd_writev); -UTEST_ADD(fd_all); - -UTEST_ADD(signal_basic); -UTEST_ADD(signal_send); -UTEST_ADD(signal_abort); -UTEST_ADD(signal_segfault); -UTEST_ADD(signal_stop); -UTEST_ADD(signal_cont_masked); -UTEST_ADD(signal_mask); -UTEST_ADD(signal_mask_nonmaskable); -UTEST_ADD(signal_sigsuspend); -UTEST_ADD(signal_sigsuspend_stop); -UTEST_ADD(signal_handler_mask); - -UTEST_ADD(fork_wait); -UTEST_ADD(fork_signal); -UTEST_ADD(fork_sigchld_ignored); - -UTEST_ADD(lseek_basic); -UTEST_ADD(lseek_errors); - -UTEST_ADD(access_basic); - -UTEST_ADD(stat); -UTEST_ADD(fstat); - -UTEST_ADD(setjmp); - -UTEST_ADD(sigaction_with_setjmp); -UTEST_ADD(sigaction_handler_returns); - -UTEST_ADD(vfs_dir); -UTEST_ADD(vfs_relative_dir); -UTEST_ADD(vfs_rw); -UTEST_ADD(vfs_dot_dot_dir); -UTEST_ADD(vfs_dot_dir); -UTEST_ADD(vfs_dot_dot_across_fs); -UTEST_ADD(vfs_trunc); -UTEST_ADD(vfs_symlink); -UTEST_ADD(vfs_link); -UTEST_ADD(vfs_chmod); - -UTEST_ADD(wait_basic); -UTEST_ADD(wait_nohang); - -#if 0 -UTEST_ADD(fpu_fcsr); -UTEST_ADD(fpu_gpr_preservation); -UTEST_ADD(fpu_cpy_ctx_on_fork); -UTEST_ADD(fpu_ctx_signals); -#endif - -#ifdef __mips__ -UTEST_ADD(exc_cop_unusable); -UTEST_ADD(exc_reserved_instruction); -UTEST_ADD(exc_unaligned_access); -UTEST_ADD(exc_integer_overflow); - -UTEST_ADD(exc_sigsys); -#endif - -#ifdef __aarch64__ -UTEST_ADD(exc_unknown_instruction); -UTEST_ADD(exc_msr_instruction); -UTEST_ADD(exc_mrs_instruction); - -UTEST_ADD(exc_brk); -#endif - -UTEST_ADD(getcwd); -/* XXX UTEST_ADD(syscall_in_bds); */ - -UTEST_ADD(setpgid); -UTEST_ADD(setpgid_leader); -UTEST_ADD(setpgid_child); -UTEST_ADD(kill); -UTEST_ADD(killpg_same_group); -UTEST_ADD(killpg_other_group); -UTEST_ADD(pgrp_orphan); -UTEST_ADD(session_basic); -UTEST_ADD(session_login_name); - -#ifdef __mips__ -UTEST_ADD(gettimeofday); -#endif -// UTEST_ADD(nanosleep); -UTEST_ADD(itimer); - -UTEST_ADD(get_set_uid); -UTEST_ADD(get_set_gid); -UTEST_ADD(get_set_groups); - -UTEST_ADD(sharing_memory_simple); -UTEST_ADD(sharing_memory_child_and_grandchild); - -UTEST_ADD(pty_simple); - -UTEST_ADD(tty_canon); -UTEST_ADD(tty_echo); -UTEST_ADD(tty_signals); - -UTEST_ADD(procstat); - -UTEST_ADD(pipe_parent_signaled); -UTEST_ADD(pipe_child_signaled); -UTEST_ADD(pipe_blocking_flag_manipulation); -UTEST_ADD(pipe_write_interruptible_sleep); -UTEST_ADD(pipe_write_errno_eagain); -UTEST_ADD(pipe_read_interruptible_sleep); -UTEST_ADD(pipe_read_errno_eagain); -UTEST_ADD(pipe_read_return_zero); diff --git a/usr.bin/Makefile b/usr.bin/Makefile index c33db92ebc..fecc13cc96 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -2,7 +2,7 @@ TOPDIR = $(realpath ..) -SUBDIR = id login stat script su +SUBDIR = script all: build diff --git a/usr.bin/id/Makefile b/usr.bin/id/Makefile deleted file mode 100644 index 936ab59571..0000000000 --- a/usr.bin/id/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOPDIR = $(realpath ../..) - -PROGRAM = id - -include $(TOPDIR)/build/build.prog.mk - -WFLAGS += -Wno-format-nonliteral diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c deleted file mode 100644 index b902d34d8a..0000000000 --- a/usr.bin/id/id.c +++ /dev/null @@ -1,357 +0,0 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -static void current(void); -static void pretty(struct passwd *); -static void group(struct passwd *, int); -__noreturn static void usage(void); -static void user(struct passwd *); -static struct passwd *who(char *); - -static int maxgroups; -static gid_t *groups; - -int -main(int argc, char *argv[]) -{ - struct group *gr; - struct passwd *pw; - int ch, id; - int Gflag, gflag, nflag, pflag, rflag, uflag; - const char *opts; - - Gflag = gflag = nflag = pflag = rflag = uflag = 0; - - if (strcmp(getprogname(), "groups") == 0) { - Gflag = 1; - nflag = 1; - opts = ""; - if (argc > 2) - usage(); - } else if (strcmp(getprogname(), "whoami") == 0) { - uflag = 1; - nflag = 1; - opts = ""; - if (argc > 1) - usage(); - } else - opts = "Ggnpru"; - - while ((ch = getopt(argc, argv, opts)) != -1) - switch (ch) { - case 'G': - Gflag = 1; - break; - case 'g': - gflag = 1; - break; - case 'n': - nflag = 1; - break; - case 'p': - pflag = 1; - break; - case 'r': - rflag = 1; - break; - case 'u': - uflag = 1; - break; - case '?': - default: - usage(); - } - argc -= optind; - argv += optind; - - switch (Gflag + gflag + pflag + uflag) { - case 1: - break; - case 0: - if (!nflag && !rflag) - break; - /* FALLTHROUGH */ - default: - usage(); - } - - if (strcmp(opts, "") != 0 && argc > 1) - usage(); - - pw = *argv ? who(*argv) : NULL; - - maxgroups = sysconf(_SC_NGROUPS_MAX); - if ((groups = malloc((maxgroups + 1) * sizeof(gid_t))) == NULL) - err(1, NULL); - - if (gflag) { - id = pw ? pw->pw_gid : rflag ? getgid() : getegid(); - if (nflag && (gr = getgrgid(id))) - (void)printf("%s\n", gr->gr_name); - else - (void)printf("%u\n", id); - goto done; - } - - if (uflag) { - id = pw ? pw->pw_uid : rflag ? getuid() : geteuid(); - if (nflag && (pw = getpwuid(id))) - (void)printf("%s\n", pw->pw_name); - else - (void)printf("%u\n", id); - goto done; - } - - if (Gflag) { - group(pw, nflag); - goto done; - } - - if (pflag) { - pretty(pw); - goto done; - } - - if (pw) - user(pw); - else - current(); -done: - free(groups); - - return 0; -} - -static void -pretty(struct passwd *pw) -{ - struct group *gr; - u_int eid, rid; - const char *login; - - if (pw) { - (void)printf("uid\t%s\n", pw->pw_name); - (void)printf("groups\t"); - group(pw, 1); - } else { - if ((login = getlogin()) == NULL) - err(1, "getlogin"); - - pw = getpwuid(rid = getuid()); - if (pw == NULL || strcmp(login, pw->pw_name)) - (void)printf("login\t%s\n", login); - if (pw) - (void)printf("uid\t%s\n", pw->pw_name); - else - (void)printf("uid\t%u\n", rid); - - if ((eid = geteuid()) != rid) { - if ((pw = getpwuid(eid)) != NULL) - (void)printf("euid\t%s\n", pw->pw_name); - else - (void)printf("euid\t%u\n", eid); - } - if ((rid = getgid()) != (eid = getegid())) { - if ((gr = getgrgid(rid)) != NULL) - (void)printf("rgid\t%s\n", gr->gr_name); - else - (void)printf("rgid\t%u\n", rid); - } - (void)printf("groups\t"); - group(NULL, 1); - } -} - -static void -current(void) -{ - struct group *gr; - struct passwd *pw; - gid_t gid, egid, lastid; - uid_t uid, euid; - int cnt, ngroups; - const char *fmt; - - uid = getuid(); - (void)printf("uid=%ju", (uintmax_t)uid); - if ((pw = getpwuid(uid)) != NULL) - (void)printf("(%s)", pw->pw_name); - gid = getgid(); - (void)printf(" gid=%ju", (uintmax_t)gid); - if ((gr = getgrgid(gid)) != NULL) - (void)printf("(%s)", gr->gr_name); - if ((euid = geteuid()) != uid) { - (void)printf(" euid=%ju", (uintmax_t)euid); - if ((pw = getpwuid(euid)) != NULL) - (void)printf("(%s)", pw->pw_name); - } - if ((egid = getegid()) != gid) { - (void)printf(" egid=%ju", (uintmax_t)egid); - if ((gr = getgrgid(egid)) != NULL) - (void)printf("(%s)", gr->gr_name); - } - if ((ngroups = getgroups(maxgroups, groups)) != 0) { - for (fmt = " groups=%ju", lastid = -1, cnt = 0; cnt < ngroups; - fmt = ",%ju", lastid = gid, cnt++) { - gid = groups[cnt]; - if (lastid == gid) - continue; - (void)printf(fmt, (uintmax_t)gid); - if ((gr = getgrgid(gid)) != NULL) - (void)printf("(%s)", gr->gr_name); - } - } - (void)printf("\n"); -} - -static void -user(struct passwd *pw) -{ - struct group *gr; - const char *fmt; - int cnt, id, lastid, ngroups; - gid_t *glist = groups; - - id = pw->pw_uid; - (void)printf("uid=%u(%s)", id, pw->pw_name); - (void)printf(" gid=%lu", (u_long)pw->pw_gid); - if ((gr = getgrgid(pw->pw_gid)) != NULL) - (void)printf("(%s)", gr->gr_name); - ngroups = maxgroups + 1; - if (getgrouplist(pw->pw_name, pw->pw_gid, glist, &ngroups) == -1) { - glist = malloc(ngroups * sizeof(gid_t)); - (void) getgrouplist(pw->pw_name, pw->pw_gid, glist, &ngroups); - } - for (fmt = " groups=%u", lastid = -1, cnt = 0; cnt < ngroups; - fmt=",%u", lastid = id, cnt++) { - id = glist[cnt]; - if (lastid == id) - continue; - (void)printf(fmt, id); - if ((gr = getgrgid(id)) != NULL) - (void)printf("(%s)", gr->gr_name); - } - (void)printf("\n"); - if (glist != groups) - free(glist); -} - -static void -group(struct passwd *pw, int nflag) -{ - struct group *gr; - int cnt, ngroups; - gid_t id, lastid; - const char *fmt; - gid_t *glist = groups; - - if (pw) { - ngroups = maxgroups; - if (getgrouplist(pw->pw_name, pw->pw_gid, glist, &ngroups) - == -1) { - glist = malloc(ngroups * sizeof(gid_t)); - (void) getgrouplist(pw->pw_name, pw->pw_gid, glist, - &ngroups); - } - } else { - glist[0] = getgid(); - ngroups = getgroups(maxgroups, glist + 1) + 1; - } - fmt = nflag ? "%s" : "%u"; - for (lastid = -1, cnt = 0; cnt < ngroups; ++cnt) { - if (lastid == (id = glist[cnt]) || (cnt && id == glist[0])) - continue; - if (nflag) { - if ((gr = getgrgid(id)) != NULL) - (void)printf(fmt, gr->gr_name); - else - (void)printf(*fmt == ' ' ? " %u" : "%u", - id); - fmt = " %s"; - } else { - (void)printf(fmt, id); - fmt = " %u"; - } - lastid = id; - } - (void)printf("\n"); - if (glist != groups) - free(glist); -} - -static struct passwd * -who(char *u) -{ - struct passwd *pw; - long id; - char *ep; - - /* - * Translate user argument into a pw pointer. First, try to - * get it as specified. If that fails, try it as a number. - */ - if ((pw = getpwnam(u)) != NULL) - return pw; - id = strtol(u, &ep, 10); - if (*u && !*ep && (pw = getpwuid(id))) - return pw; - errx(1, "%s: No such user", u); - /* NOTREACHED */ - return NULL; -} - -static void -usage(void) -{ - - if (strcmp(getprogname(), "groups") == 0) { - (void)fprintf(stderr, "usage: groups [user]\n"); - } else if (strcmp(getprogname(), "whoami") == 0) { - (void)fprintf(stderr, "usage: whoami\n"); - } else { - (void)fprintf(stderr, "usage: id [user]\n"); - (void)fprintf(stderr, " id -G [-n] [user]\n"); - (void)fprintf(stderr, " id -g [-nr] [user]\n"); - (void)fprintf(stderr, " id -p [user]\n"); - (void)fprintf(stderr, " id -u [-nr] [user]\n"); - } - exit(1); -} diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile deleted file mode 100644 index 5330303bd3..0000000000 --- a/usr.bin/login/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOPDIR = $(realpath ../..) - -PROGRAM = login -SOURCES = login.c common.c - -include $(TOPDIR)/build/build.prog.mk - -WFLAGS += -Wno-format-nonliteral diff --git a/usr.bin/login/common.c b/usr.bin/login/common.c deleted file mode 100644 index 26d0aad4da..0000000000 --- a/usr.bin/login/common.c +++ /dev/null @@ -1,418 +0,0 @@ -/* $NetBSD: common.c,v 1.8 2015/10/28 07:27:24 shm Exp $ */ - -/*- - * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef NOT_IMPLEMENTED -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include "pathnames.h" -#include "common.h" - -#if defined(KERBEROS5) -#define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */ -#else -#define NBUFSIZ (MAXLOGNAME + 1) -#endif - -#ifdef SUPPORT_UTMP -#include -static void doutmp(void); -static void dolastlog(int); -#endif -#ifdef SUPPORT_UTMPX -#include -static void doutmpx(void); -static void dolastlogx(int); -#endif - -/* - * This bounds the time given to login. Not a define so it can - * be patched on machines where it's too small. - */ -u_int timeout = 300; - -#ifdef NOT_IMPLEMENTED -void decode_ss(const char *); -#endif -struct passwd *pwd; -int failures, have_ss; -char term[64], *envinit[1], *hostname, *tty, *nested; -const char *username; -struct timeval now; -#ifdef NOT_IMPLEMENTED -struct sockaddr_storage ss; -#endif - -char * -trimloginname(char *u) -{ - if (strlen(u) > MAXLOGNAME) - u[MAXLOGNAME] = '\0'; - return u; -} - -char * -getloginname(void) -{ - int ch; - char *p; - static char nbuf[NBUFSIZ]; - - for (;;) { - (void)printf("login: "); - //fflush(stdout); - for (p = nbuf; (ch = getchar()) != '\n'; ) { - if (ch == EOF) { - badlogin(username); - exit(EXIT_FAILURE); - } - if (p < nbuf + (NBUFSIZ - 1)) - *p++ = ch; - } - if (p > nbuf) { - if (nbuf[0] == '-') - (void)fprintf(stderr, - "login names may not start with '-'.\n"); - else { - *p = '\0'; - return nbuf; - } - } - } -} - -int -rootterm(char *ttyn) -{ -#ifdef NOT_IMPLEMENTED - struct ttyent *t; - - return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); -#endif - return 1; -} - -static jmp_buf motdinterrupt; - -void -motd(const char *fname) -{ - int fd, nchars; - sig_t oldint; - char tbuf[8192]; - - if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0) - return; - oldint = signal(SIGINT, sigint); - if (setjmp(motdinterrupt) == 0) - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) - (void)write(fileno(stdout), tbuf, nchars); - (void)signal(SIGINT, oldint); - (void)close(fd); -} - -/* ARGSUSED */ -void -sigint(int signo) -{ - - longjmp(motdinterrupt, 1); -} - -/* ARGSUSED */ -void -timedout(int signo) -{ - - (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); - exit(EXIT_FAILURE); -} - -#ifdef NOT_IMPLEMENTED -void -update_db(int quietlog, int rootlogin, int fflag) -{ - struct sockaddr_storage ass; - char assbuf[1024]; - socklen_t alen; - const char *hname; - int remote; - - hname = (hostname == NULL) ? "?" : hostname; - alen = sizeof(ass); - if (getpeername(STDIN_FILENO, (struct sockaddr *)&ass, &alen) != -1) { - (void)sockaddr_snprintf(assbuf, - sizeof(assbuf), "%A (%a)", (void *)&ass); - if (have_ss) { - char ssbuf[1024]; - (void)sockaddr_snprintf(ssbuf, - sizeof(ssbuf), "%A(%a)", (void *)&ss); - if (memcmp(&ass, &ss, alen) != 0) - syslog(LOG_NOTICE, - "login %s on tty %s address mismatch " - "passed %s != actual %s", username, tty, - ssbuf, assbuf); - } else - ss = ass; - remote = 1; - } else if (have_ss) { - (void)sockaddr_snprintf(assbuf, - sizeof(assbuf), "%A(%a)", (void *)&ss); - remote = 1; - } else if (hostname) { - (void)snprintf(assbuf, sizeof(assbuf), "? ?"); - remote = 1; - } else - remote = 0; - - /* If fflag is on, assume caller/authenticator has logged root login. */ - if (rootlogin && fflag == 0) { - if (remote) - syslog(LOG_NOTICE, "ROOT LOGIN (%s) on tty %s from %s /" - " %s", username, tty, hname, assbuf); - else - syslog(LOG_NOTICE, "ROOT LOGIN (%s) on tty %s", - username, tty); - } else if (nested != NULL) { - if (remote) - syslog(LOG_NOTICE, "%s to %s on tty %s from %s / " - "%s", nested, pwd->pw_name, tty, hname, assbuf); - else - syslog(LOG_NOTICE, "%s to %s on tty %s", nested, - pwd->pw_name, tty); - } else { - if (remote) - syslog(LOG_NOTICE, "%s on tty %s from %s / %s", - pwd->pw_name, tty, hname, assbuf); - else - syslog(LOG_NOTICE, "%s on tty %s", - pwd->pw_name, tty); - } - (void)gettimeofday(&now, NULL); -#ifdef SUPPORT_UTMPX - doutmpx(); - dolastlogx(quietlog); - quietlog = 1; -#endif -#ifdef SUPPORT_UTMP - doutmp(); - dolastlog(quietlog); -#endif -} -#endif /* !NOT_IMPLEMENTED */ - -#ifdef SUPPORT_UTMPX -static void -doutmpx(void) -{ - struct utmpx utmpx; - char *t; - - memset((void *)&utmpx, 0, sizeof(utmpx)); - utmpx.ut_tv = now; - (void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name)); - if (hostname) { - (void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host)); - utmpx.ut_ss = ss; - } - (void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line)); - utmpx.ut_type = USER_PROCESS; - utmpx.ut_pid = getpid(); - t = tty + strlen(tty); - if ((size_t)(t - tty) >= sizeof(utmpx.ut_id)) { - (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id), - sizeof(utmpx.ut_id)); - } else { - (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id)); - } - if (pututxline(&utmpx) == NULL) - syslog(LOG_NOTICE, "Cannot update utmpx: %m"); - endutxent(); - if (updwtmpx(_PATH_WTMPX, &utmpx) != 0) - syslog(LOG_NOTICE, "Cannot update wtmpx: %m"); -} - -static void -dolastlogx(int quiet) -{ - struct lastlogx ll; - if (!quiet && getlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != NULL) { - time_t t = (time_t)ll.ll_tv.tv_sec; - (void)printf("Last login: %.24s ", ctime(&t)); - if (*ll.ll_host != '\0') - (void)printf("from %.*s ", - (int)sizeof(ll.ll_host), - ll.ll_host); - (void)printf("on %.*s\n", - (int)sizeof(ll.ll_line), - ll.ll_line); - } - ll.ll_tv = now; - (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); - if (hostname) - (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); - else - (void)memset(ll.ll_host, '\0', sizeof(ll.ll_host)); - if (have_ss) - ll.ll_ss = ss; - else - (void)memset(&ll.ll_ss, 0, sizeof(ll.ll_ss)); - if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0) - syslog(LOG_NOTICE, "Cannot update lastlogx: %m"); -} -#endif - -#ifdef SUPPORT_UTMP -static void -doutmp(void) -{ - struct utmp utmp; - - (void)memset((void *)&utmp, 0, sizeof(utmp)); - utmp.ut_time = now.tv_sec; - (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); - if (hostname) - (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); - (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); - login(&utmp); -} - -static void -dolastlog(int quiet) -{ - struct lastlog ll; - int fd; - - if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { - (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET); - if (!quiet) { - if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && - ll.ll_time != 0) { - (void)printf("Last login: %.24s ", - ctime(&ll.ll_time)); - if (*ll.ll_host != '\0') - (void)printf("from %.*s ", - (int)sizeof(ll.ll_host), - ll.ll_host); - (void)printf("on %.*s\n", - (int)sizeof(ll.ll_line), ll.ll_line); - } - (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), - SEEK_SET); - } - memset((void *)&ll, 0, sizeof(ll)); - ll.ll_time = now.tv_sec; - (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); - if (hostname) - (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); - (void)write(fd, (char *)&ll, sizeof(ll)); - (void)close(fd); - } -} -#endif - -void -badlogin(const char *name) -{ - - if (failures == 0) - return; - if (hostname) { - syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", - failures, failures > 1 ? "S" : "", hostname); - syslog(LOG_AUTHPRIV|LOG_NOTICE, - "%d LOGIN FAILURE%s FROM %s, %s", - failures, failures > 1 ? "S" : "", hostname, name); - } else { - syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", - failures, failures > 1 ? "S" : "", tty); - syslog(LOG_AUTHPRIV|LOG_NOTICE, - "%d LOGIN FAILURE%s ON %s, %s", - failures, failures > 1 ? "S" : "", tty, name); - } -} - -const char * -stypeof(const char *ttyid) -{ -#ifdef NOT_IMPLEMENTED - struct ttyent *t; - - return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL); -#endif - return NULL; -} - -void -sleepexit(int eval) -{ - - (void)sleep(5); - exit(eval); -} - -#ifdef NOT_IMPLEMENTED -void -decode_ss(const char *arg) -{ - struct sockaddr_storage *ssp; - size_t len = strlen(arg); - - if (len > sizeof(*ssp) * 4 + 1 || len < sizeof(*ssp)) - errx(EXIT_FAILURE, "Bad argument"); - - if ((ssp = malloc(len + 1)) == NULL) - err(EXIT_FAILURE, NULL); - - if (strunvis((char *)ssp, arg) != sizeof(*ssp)) - errx(EXIT_FAILURE, "Decoding error"); - - (void)memcpy(&ss, ssp, sizeof(ss)); - free(ssp); - have_ss = 1; -} -#endif diff --git a/usr.bin/login/common.h b/usr.bin/login/common.h deleted file mode 100644 index 7bda6c632e..0000000000 --- a/usr.bin/login/common.h +++ /dev/null @@ -1,60 +0,0 @@ -/* $NetBSD: common.h,v 1.4 2012/05/19 00:02:44 christos Exp $ */ - -/*- - * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -__BEGIN_DECLS - -void badlogin(const char *); -char *trimloginname(char *); -char *getloginname(void); -void motd(const char *); -int rootterm(char *); -void sigint(int); -void sleepexit(int); -const char *stypeof(const char *); -void timedout(int); -#ifdef NOT_IMPLEMENTED -void decode_ss(const char *); -void update_db(int, int, int); -#else -#define decode_ss(...) -#define update_db(...) -#endif - -extern u_int timeout; -extern struct passwd *pwd; -extern int failures, have_ss; -extern char term[64], *envinit[1], *hostname, *tty, *nested; -extern const char *username; -extern struct timeval now; -extern struct sockaddr_storage ss; -extern const char copyrightstr[]; - -__END_DECLS diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c deleted file mode 100644 index c3019018df..0000000000 --- a/usr.bin/login/login.c +++ /dev/null @@ -1,797 +0,0 @@ -/* $NetBSD: login.c,v 1.105 2014/11/12 22:23:38 aymeric Exp $ */ - -/*- - * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -/* - * login [ name ] - * login -h hostname (for telnetd, etc.) - * login -f name (for pre-authenticated login: datakit, xterm, etc.) - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef NOT_IMPLEMENTED -#include -#endif -#include -#include -#include -#ifdef SUPPORT_UTMP -#include -#endif -#ifdef SUPPORT_UTMPX -#include -#endif -#include -#ifdef SKEY -#include -#endif -#ifdef KERBEROS5 -#include -#include -#endif -#ifdef LOGIN_CAP -#include -#endif -#include - -#include "pathnames.h" -#include "common.h" - -#ifdef KERBEROS5 -int login_krb5_forwardable_tgt = 0; -static int login_krb5_get_tickets = 1; -static int login_krb5_retain_ccache = 0; -#endif - -static void checknologin(char *); -#ifdef KERBEROS5 -int k5login(struct passwd *, char *, char *, char *); -void k5destroy(void); -int k5_read_creds(const char *); -int k5_write_creds(void); -#endif -#if defined(KERBEROS5) -static void dofork(void); -#endif -static void usage(void) __attribute__((__noreturn__)); - -#define TTYGRPNAME "tty" /* name of group to own ttys */ - -#define DEFAULT_BACKOFF 3 -#define DEFAULT_RETRIES 10 - -#if defined(KERBEROS5) -int has_ccache = 0; -int notickets = 1; -extern krb5_context kcontext; -extern int have_forward; -static char *instance; -extern char *krb5tkfile_env; -extern int krb5_configured; -#endif - -#if defined(KERBEROS5) -#define KERBEROS_CONFIGURED krb5_configured -#endif - -extern char **environ; - -int -main(int argc, char *argv[]) -{ - struct group *gr; -#ifdef NOT_IMPLEMENTED - struct stat st; -#endif - int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval; - uid_t uid, saved_uid; - gid_t saved_gid, saved_gids[NGROUPS_MAX]; - int nsaved_gids; -#ifdef notdef - char *domain; -#endif - char *p, *ttyn; - const char *pwprompt; - char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; -#ifdef NOT_IMPLEMENTED - char localhost[MAXHOSTNAMELEN + 1]; -#endif - int need_chpass, require_chpass; - int login_retries = DEFAULT_RETRIES, - login_backoff = DEFAULT_BACKOFF; - time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY; - char *loginname = NULL; -#ifdef KERBEROS5 - int Fflag; - krb5_error_code kerror; -#endif -#if defined(KERBEROS5) - int got_tickets = 0; -#endif -#ifdef LOGIN_CAP - char *shell = NULL; - login_cap_t *lc = NULL; -#endif - - tbuf[0] = '\0'; - rval = 0; - pwprompt = NULL; - nested = NULL; - need_chpass = require_chpass = 0; - - (void)signal(SIGALRM, timedout); - (void)alarm(timeout); - (void)signal(SIGQUIT, SIG_IGN); - (void)signal(SIGINT, SIG_IGN); - (void)setpriority(PRIO_PROCESS, 0, 0); - -#ifdef NOT_IMPLEMENTED - openlog("login", 0, LOG_AUTH); -#endif - - /* - * -p is used by getty to tell login not to destroy the environment - * -f is used to skip a second login authentication - * -h is used by other servers to pass the name of the remote host to - * login so that it may be placed in utmp/utmpx and wtmp/wtmpx - * -a in addition to -h, a server may supply -a to pass the actual - * server address. - * -s is used to force use of S/Key or equivalent. - */ -#ifdef NOT_IMPLEMENTED - if (gethostname(localhost, sizeof(localhost)) < 0) { - syslog(LOG_ERR, "couldn't get local hostname: %m"); - strcpy(hostname, "amnesiac"); - } - #endif -#ifdef notdef - domain = strchr(localhost, '.'); -#endif -#ifdef NOT_IMPLEMENTED - localhost[sizeof(localhost) - 1] = '\0'; -#endif - - fflag = hflag = pflag = sflag = 0; - have_ss = 0; -#ifdef KERBEROS5 - Fflag = 0; - have_forward = 0; -#endif - uid = getuid(); - while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1) - switch (ch) { - case 'a': - if (uid) - errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM)); - decode_ss(optarg); -#ifdef notdef - (void)sockaddr_snprintf(optarg, - sizeof(struct sockaddr_storage), "%a", (void *)&ss); -#endif - break; - case 'F': -#ifdef KERBEROS5 - Fflag = 1; -#endif - /* FALLTHROUGH */ - case 'f': - fflag = 1; - break; - case 'h': - if (uid) - errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM)); - hflag = 1; -#ifdef notdef - if (domain && (p = strchr(optarg, '.')) != NULL && - strcasecmp(p, domain) == 0) - *p = '\0'; -#endif - hostname = optarg; - break; - case 'p': - pflag = 1; - break; - case 's': - sflag = 1; - break; - default: - case '?': - usage(); - break; - } - -#ifdef NOT_IMPLEMENTED - setproctitle(NULL); -#endif - argc -= optind; - argv += optind; - - if (*argv) { - username = loginname = *argv; - ask = 0; - } else - ask = 1; - -#ifdef F_CLOSEM - (void)fcntl(3, F_CLOSEM, 0); -#else - for (cnt = sysconf(_SC_OPEN_MAX); cnt > 2; cnt--) - (void)close(cnt); -#endif - - ttyn = ttyname(STDIN_FILENO); - if (ttyn == NULL || *ttyn == '\0') { - (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); - ttyn = tname; - } - if ((tty = strstr(ttyn, "/pts/")) != NULL) - ++tty; - else if ((tty = strrchr(ttyn, '/')) != NULL) - ++tty; - else - tty = ttyn; - - if (issetugid()) { - nested = strdup(user_from_uid(getuid(), 0)); - if (nested == NULL) { - syslog(LOG_ERR, "strdup: %m"); - sleepexit(EXIT_FAILURE); - } - } - -#ifdef LOGIN_CAP - /* Get "login-retries" and "login-backoff" from default class */ - if ((lc = login_getclass(NULL)) != NULL) { - login_retries = (int)login_getcapnum(lc, "login-retries", - DEFAULT_RETRIES, DEFAULT_RETRIES); - login_backoff = (int)login_getcapnum(lc, "login-backoff", - DEFAULT_BACKOFF, DEFAULT_BACKOFF); - login_close(lc); - lc = NULL; - } -#endif - -#ifdef KERBEROS5 - kerror = krb5_init_context(&kcontext); - if (kerror) { - /* - * If Kerberos is not configured, that is, we are - * not using Kerberos, do not log the error message. - * However, if Kerberos is configured, and the - * context init fails for some other reason, we need - * to issue a no tickets warning to the user when the - * login succeeds. - */ - if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */ - syslog(LOG_NOTICE, - "%s when initializing Kerberos context", - error_message(kerror)); - krb5_configured = 1; - } - login_krb5_get_tickets = 0; - } -#endif /* KERBEROS5 */ - - for (cnt = 0;; ask = 1) { -#if defined(KERBEROS5) - if (login_krb5_get_tickets) - k5destroy(); -#endif - if (ask) { - fflag = 0; - loginname = getloginname(); - } - rootlogin = 0; -#ifdef KERBEROS5 - if ((instance = strchr(loginname, '/')) != NULL) - *instance++ = '\0'; - else - instance = __UNCONST(""); -#endif - username = trimloginname(loginname); - /* - * Note if trying multiple user names; log failures for - * previous user name, but don't bother logging one failure - * for nonexistent name (mistyped username). - */ - if (failures && strcmp(tbuf, username)) { - if (failures > (pwd ? 0 : 1)) - badlogin(tbuf); - failures = 0; - } - (void)strlcpy(tbuf, username, sizeof(tbuf)); - - pwd = getpwnam(username); - -#ifdef LOGIN_CAP - /* - * Establish the class now, before we might goto - * within the next block. pwd can be NULL since it - * falls back to the "default" class if it is. - */ - lc = login_getclass(pwd ? pwd->pw_class : NULL); -#endif - /* - * if we have a valid account name, and it doesn't have a - * password, or the -f option was specified and the caller - * is root or the caller isn't changing their uid, don't - * authenticate. - */ - if (pwd) { - if (pwd->pw_uid == 0) - rootlogin = 1; - - if (fflag && (uid == 0 || uid == pwd->pw_uid)) { - /* already authenticated */ -#ifdef KERBEROS5 - if (login_krb5_get_tickets && Fflag) - k5_read_creds(username); -#endif - break; - } else if (pwd->pw_passwd[0] == '\0') { - /* pretend password okay */ - rval = 0; - goto ttycheck; - } - } - - fflag = 0; - - (void)setpriority(PRIO_PROCESS, 0, -4); - -#ifdef SKEY - if (skey_haskey(username) == 0) { - static char skprompt[80]; - const char *skinfo = skey_keyinfo(username); - - (void)snprintf(skprompt, sizeof(skprompt), - "Password [ %s ]:", - skinfo ? skinfo : "error getting challenge"); - pwprompt = skprompt; - } else -#endif - pwprompt = "Password:"; - - p = getpass(pwprompt); - - if (pwd == NULL) { - rval = 1; - goto skip; - } -#ifdef KERBEROS5 - if (login_krb5_get_tickets && - k5login(pwd, instance, localhost, p) == 0) { - rval = 0; - got_tickets = 1; - } -#endif -#if defined(KERBEROS5) - if (got_tickets) - goto skip; -#endif -#ifdef SKEY - if (skey_haskey(username) == 0 && - skey_passcheck(username, p) != -1) { - rval = 0; - goto skip; - } -#endif - if (!sflag && *pwd->pw_passwd != '\0' && - !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) { - rval = 0; - require_chpass = 1; - goto skip; - } - rval = 1; - - skip: - memset(p, 0, strlen(p)); - - (void)setpriority(PRIO_PROCESS, 0, 0); - - ttycheck: - /* - * If trying to log in as root without Kerberos, - * but with insecure terminal, refuse the login attempt. - */ - if (pwd && !rval && rootlogin && !rootterm(tty)) { - (void)printf("Login incorrect or refused on this " - "terminal.\n"); - if (hostname) - syslog(LOG_NOTICE, - "LOGIN %s REFUSED FROM %s ON TTY %s", - pwd->pw_name, hostname, tty); - else - syslog(LOG_NOTICE, - "LOGIN %s REFUSED ON TTY %s", - pwd->pw_name, tty); - continue; - } - - if (pwd && !rval) - break; - - (void)printf("Login incorrect or refused on this " - "terminal.\n"); - failures++; - cnt++; - /* - * We allow login_retries tries, but after login_backoff - * we start backing off. These default to 10 and 3 - * respectively. - */ - if (cnt > login_backoff) { - if (cnt >= login_retries) { - badlogin(username); - sleepexit(EXIT_FAILURE); - } - sleep((u_int)((cnt - login_backoff) * 5)); - } - } - - /* committed to login -- turn off timeout */ - (void)alarm((u_int)0); - - endpwent(); - - /* if user not super-user, check for disabled logins */ -#ifdef LOGIN_CAP - if (!login_getcapbool(lc, "ignorenologin", rootlogin)) - checknologin(login_getcapstr(lc, "nologin", NULL, NULL)); -#else - if (!rootlogin) - checknologin(NULL); -#endif - -#ifdef LOGIN_CAP - quietlog = login_getcapbool(lc, "hushlogin", 0); -#else - quietlog = 0; -#endif - /* Temporarily give up special privileges so we can change */ - /* into NFS-mounted homes that are exported for non-root */ - /* access and have mode 7x0 */ - saved_uid = geteuid(); - saved_gid = getegid(); - nsaved_gids = getgroups(NGROUPS_MAX, saved_gids); - - (void)setegid(pwd->pw_gid); - initgroups(username, pwd->pw_gid); - (void)seteuid(pwd->pw_uid); - - if (chdir(pwd->pw_dir) < 0) { -#ifdef LOGIN_CAP - if (login_getcapbool(lc, "requirehome", 0)) { - (void)printf("Home directory %s required\n", - pwd->pw_dir); - sleepexit(EXIT_FAILURE); - } -#endif - (void)printf("No home directory %s!\n", pwd->pw_dir); - if (chdir("/") == -1) - exit(EXIT_FAILURE); - pwd->pw_dir = __UNCONST("/"); - (void)printf("Logging in with home = \"/\".\n"); - } - - if (!quietlog) - quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; - - /* regain special privileges */ - (void)seteuid(saved_uid); - setgroups(nsaved_gids, saved_gids); - (void)setegid(saved_gid); - -#ifdef LOGIN_CAP - pw_warntime = login_getcaptime(lc, "password-warn", - _PASSWORD_WARNDAYS * SECSPERDAY, - _PASSWORD_WARNDAYS * SECSPERDAY); -#endif - - (void)gettimeofday(&now, NULL); - if (pwd->pw_expire) { - if (now.tv_sec >= pwd->pw_expire) { - (void)printf("Sorry -- your account has expired.\n"); - sleepexit(EXIT_FAILURE); - } else if (pwd->pw_expire - now.tv_sec < pw_warntime && - !quietlog) - (void)printf("Warning: your account expires on %s", - ctime(&pwd->pw_expire)); - } - if (pwd->pw_change) { - if (pwd->pw_change == _PASSWORD_CHGNOW) - need_chpass = 1; - else if (now.tv_sec >= pwd->pw_change) { - (void)printf("Sorry -- your password has expired.\n"); - sleepexit(EXIT_FAILURE); - } else if (pwd->pw_change - now.tv_sec < pw_warntime && - !quietlog) - (void)printf("Warning: your password expires on %s", - ctime(&pwd->pw_change)); - - } - /* Nothing else left to fail -- really log in. */ - update_db(quietlog, rootlogin, fflag); - - (void)chown(ttyn, pwd->pw_uid, - (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); - -#ifdef NOT_IMPLEMENTED - if (ttyaction(ttyn, "login", pwd->pw_name)) - (void)printf("Warning: ttyaction failed.\n"); -#endif - -#if defined(KERBEROS5) - /* Fork so that we can call kdestroy */ - if (! login_krb5_retain_ccache && has_ccache) - dofork(); -#endif - - /* Destroy environment unless user has requested its preservation. */ - if (!pflag) - environ = envinit; - -#ifdef LOGIN_CAP - if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid, - LOGIN_SETLOGIN) != 0) { - syslog(LOG_ERR, "setusercontext failed"); - exit(EXIT_FAILURE); - } - if (setusercontext(lc, pwd, pwd->pw_uid, - (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) { - syslog(LOG_ERR, "setusercontext failed"); - exit(EXIT_FAILURE); - } -#else - (void)setgid(pwd->pw_gid); - - initgroups(username, pwd->pw_gid); - - if (nested == NULL && setlogin(pwd->pw_name) < 0) - syslog(LOG_ERR, "setlogin() failure: %m"); - - /* Discard permissions last so can't get killed and drop core. */ - if (rootlogin) - (void)setuid(0); - else - (void)setuid(pwd->pw_uid); -#endif - - if (*pwd->pw_shell == '\0') - pwd->pw_shell = __UNCONST(_PATH_BSHELL); -#ifdef LOGIN_CAP - if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) { - if ((shell = strdup(shell)) == NULL) { - syslog(LOG_ERR, "Cannot alloc mem"); - sleepexit(EXIT_FAILURE); - } - pwd->pw_shell = shell; - } -#endif - - (void)setenv("HOME", pwd->pw_dir, 1); - (void)setenv("SHELL", pwd->pw_shell, 1); - if (term[0] == '\0') { - const char *tt = stypeof(tty); -#ifdef LOGIN_CAP - if (tt == NULL) - tt = login_getcapstr(lc, "term", NULL, NULL); -#endif - /* unknown term -> "su" */ - (void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term)); - } - (void)setenv("TERM", term, 0); - (void)setenv("LOGNAME", pwd->pw_name, 1); - (void)setenv("USER", pwd->pw_name, 1); - -#ifdef LOGIN_CAP - setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH); -#else - (void)setenv("PATH", _PATH_DEFPATH, 0); -#endif - -#ifdef KERBEROS5 - if (krb5tkfile_env) - (void)setenv("KRB5CCNAME", krb5tkfile_env, 1); -#endif - - /* If fflag is on, assume caller/authenticator has logged root login. */ - if (rootlogin && fflag == 0) { - if (hostname) - syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", - username, tty, hostname); - else - syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", - username, tty); - } - -#if defined(KERBEROS5) - if (KERBEROS_CONFIGURED && !quietlog && notickets == 1) - (void)printf("Warning: no Kerberos tickets issued.\n"); -#endif - - if (!quietlog) { - const char *fname; -#ifdef LOGIN_CAP - fname = login_getcapstr(lc, "copyright", NULL, NULL); - if (fname != NULL && access(fname, F_OK) == 0) - motd(fname); - else -#endif -#ifdef NOT_IMPLEMENTED - (void)printf("%s", copyrightstr); -#endif - -#ifdef LOGIN_CAP - fname = login_getcapstr(lc, "welcome", NULL, NULL); - if (fname == NULL || access(fname, F_OK) != 0) -#endif - fname = _PATH_MOTDFILE; - motd(fname); - -#ifdef NOT_IMPLEMENTED - (void)snprintf(tbuf, - sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); - if (stat(tbuf, &st) == 0 && st.st_size != 0) - (void)printf("You have %smail.\n", - (st.st_mtime > st.st_atime) ? "new " : ""); -#endif - } - -#ifdef LOGIN_CAP - login_close(lc); -#endif - - (void)signal(SIGALRM, SIG_DFL); - (void)signal(SIGQUIT, SIG_DFL); - (void)signal(SIGINT, SIG_DFL); - (void)signal(SIGTSTP, SIG_IGN); - - tbuf[0] = '-'; - (void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? - p + 1 : pwd->pw_shell, sizeof(tbuf) - 1); - - /* Wait to change password until we're unprivileged */ - if (need_chpass) { - if (!require_chpass) - (void)printf( -"Warning: your password has expired. Please change it as soon as possible.\n"); - else { - int status; - - (void)printf( - "Your password has expired. Please choose a new one.\n"); - switch (fork()) { - case -1: - warn("fork"); - sleepexit(EXIT_FAILURE); - break; - case 0: - execl(_PATH_BINPASSWD, "passwd", NULL); - _exit(EXIT_FAILURE); - default: - if (wait(&status) == -1 || - WEXITSTATUS(status)) - sleepexit(EXIT_FAILURE); - } - } - } - -#ifdef KERBEROS5 - if (login_krb5_get_tickets) - k5_write_creds(); -#endif - execlp(pwd->pw_shell, tbuf, NULL); - err(EXIT_FAILURE, "%s", pwd->pw_shell); -} - -#if defined(KERBEROS5) -/* - * This routine handles cleanup stuff, and the like. - * It exists only in the child process. - */ -static void -dofork(void) -{ - pid_t child, wchild; - - switch (child = fork()) { - case 0: - return; /* Child process */ - case -1: - err(EXIT_FAILURE, "Can't fork"); - /*NOTREACHED*/ - default: - break; - } - - /* - * Setup stuff? This would be things we could do in parallel - * with login - */ - if (chdir("/") == -1) /* Let's not keep the fs busy... */ - err(EXIT_FAILURE, "Can't chdir to `/'"); - - /* If we're the parent, watch the child until it dies */ - while ((wchild = wait(NULL)) != child) - if (wchild == -1) - err(EXIT_FAILURE, "Can't wait"); - - /* Cleanup stuff */ - /* Run kdestroy to destroy tickets */ - if (login_krb5_get_tickets) - k5destroy(); - - /* Leave */ - exit(EXIT_SUCCESS); -} -#endif - -static void -checknologin(char *fname) -{ - int fd, nchars; - char tbuf[8192]; - - if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) - (void)write(fileno(stdout), tbuf, nchars); - sleepexit(EXIT_SUCCESS); - } -} - -static void -usage(void) -{ - (void)fprintf(stderr, - "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n", - getprogname()); - exit(EXIT_FAILURE); -} diff --git a/usr.bin/login/pathnames.h b/usr.bin/login/pathnames.h deleted file mode 100644 index e95e5cbe01..0000000000 --- a/usr.bin/login/pathnames.h +++ /dev/null @@ -1,38 +0,0 @@ -/* $NetBSD: pathnames.h,v 1.6 2003/08/07 11:14:26 agc Exp $ */ - -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/9/93 - */ - -#include - -#define _PATH_HUSHLOGIN ".hushlogin" -#define _PATH_MOTDFILE "/etc/motd" -#define _PATH_BINPASSWD "/usr/bin/passwd" diff --git a/usr.bin/stat/Makefile b/usr.bin/stat/Makefile deleted file mode 100644 index 9df1cad358..0000000000 --- a/usr.bin/stat/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOPDIR = $(realpath ../..) - -PROGRAM = stat - -include $(TOPDIR)/build/build.prog.mk - -WFLAGS += -Wno-format-nonliteral diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c deleted file mode 100644 index 2829441471..0000000000 --- a/usr.bin/stat/stat.c +++ /dev/null @@ -1,1138 +0,0 @@ -/* $NetBSD: stat.c,v 1.38 2013/01/03 13:28:41 dsl Exp $ */ - -/* - * Copyright (c) 2002-2011 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Andrew Brown. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#undef HAVE_STRUCT_STAT_ST_FLAGS -#undef HAVE_STRUCT_STAT_ST_GEN -#undef HAVE_STRUCT_STAT_ST_BIRTHTIME -#undef HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC -#undef HAVE_STRUCT_STAT_ST_MTIMENSEC -#undef HAVE_DEVNAME - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if HAVE_STRUCT_STAT_ST_FLAGS -#define DEF_F "%#Xf " -#define RAW_F "%f " -#define SHELL_F " st_flags=%f" -#else /* HAVE_STRUCT_STAT_ST_FLAGS */ -#define DEF_F -#define RAW_F -#define SHELL_F -#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ - -#if HAVE_STRUCT_STAT_ST_BIRTHTIME -#define DEF_B "\"%SB\" " -#define RAW_B "%B " -#define SHELL_B "st_birthtime=%SB " -#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ -#define DEF_B -#define RAW_B -#define SHELL_B -#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ - -#if HAVE_STRUCT_STAT_ST_ATIM -#define st_atimespec st_atim -#define st_ctimespec st_ctim -#define st_mtimespec st_mtim -#endif /* HAVE_STRUCT_STAT_ST_ATIM */ - -#define DEF_FORMAT \ - "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ - "%k %b " DEF_F "%N" -#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ - "%k %b " RAW_F "%N" -#define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" -#define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" -#define SHELL_FORMAT \ - "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ - "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ - "st_atime=%Sa st_mtime=%Sm st_ctime=%Sc " SHELL_B \ - "st_blksize=%k st_blocks=%b" SHELL_F -#define LINUX_FORMAT \ - " File: \"%N\"%n" \ - " Size: %-11z FileType: %HT%n" \ - " Mode: (%04OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ - "Device: %Hd,%Ld Inode: %i Links: %l%n" \ - "Access: %Sa%n" \ - "Modify: %Sm%n" \ - "Change: %Sc" - -#define TIME_FORMAT "%b %e %T %Y" - -#define FLAG_POUND 0x01 -#define FLAG_SPACE 0x02 -#define FLAG_PLUS 0x04 -#define FLAG_ZERO 0x08 -#define FLAG_MINUS 0x10 - -/* - * These format characters must all be unique, except the magic one. - */ -#define FMT_MAGIC '%' -#define FMT_DOT '.' - -#define SIMPLE_NEWLINE 'n' -#define SIMPLE_TAB 't' -#define SIMPLE_PERCENT '%' -#define SIMPLE_NUMBER '@' - -#define FMT_POUND '#' -#define FMT_SPACE ' ' -#define FMT_PLUS '+' -#define FMT_ZERO '0' -#define FMT_MINUS '-' - -#define FMT_DECIMAL 'D' -#define FMT_OCTAL 'O' -#define FMT_UNSIGNED 'U' -#define FMT_HEX 'X' -#define FMT_FLOAT 'F' -#define FMT_STRING 'S' - -#define FMTF_DECIMAL 0x01 -#define FMTF_OCTAL 0x02 -#define FMTF_UNSIGNED 0x04 -#define FMTF_HEX 0x08 -#define FMTF_FLOAT 0x10 -#define FMTF_STRING 0x20 - -#define HIGH_PIECE 'H' -#define MIDDLE_PIECE 'M' -#define LOW_PIECE 'L' - -#define SHOW_realpath 'R' -#define SHOW_st_dev 'd' -#define SHOW_st_ino 'i' -#define SHOW_st_mode 'p' -#define SHOW_st_nlink 'l' -#define SHOW_st_uid 'u' -#define SHOW_st_gid 'g' -#define SHOW_st_rdev 'r' -#define SHOW_st_atime 'a' -#define SHOW_st_mtime 'm' -#define SHOW_st_ctime 'c' -#define SHOW_st_btime 'B' -#define SHOW_st_size 'z' -#define SHOW_st_blocks 'b' -#define SHOW_st_blksize 'k' -#define SHOW_st_flags 'f' -#define SHOW_st_gen 'v' -#define SHOW_symlink 'Y' -#define SHOW_filetype 'T' -#define SHOW_filename 'N' -#define SHOW_sizerdev 'Z' - -static void usage(const char *) __noreturn; -static void output(const struct stat *, const char *, - const char *, int, int, int); -static int format1(const struct stat *, /* stat info */ - const char *, /* the file name */ - const char *, int, /* the format string itself */ - char *, size_t, /* a place to put the output */ - int, int, int, int, /* the parsed format */ - int, int, int); - -static const char *timefmt; -static int linkfail; - -#define addchar(s, c, nl) \ - do { \ - (void)fputc((c), (s)); \ - (*nl) = ((c) == '\n'); \ - } while (0/*CONSTCOND*/) - -int -main(int argc, char *argv[]) -{ - struct stat st; - int ch, rc, errs, am_readlink; - int lsF, fmtchar, usestat, fn, nonl, quiet; - const char *statfmt, *options, *synopsis; - - am_readlink = 0; - lsF = 0; - fmtchar = '\0'; - usestat = 0; - nonl = 0; - quiet = 0; - linkfail = 0; - statfmt = NULL; - timefmt = NULL; - - setprogname(argv[0]); - - if (strcmp(getprogname(), "readlink") == 0) { - am_readlink = 1; - options = "fnqsv"; - synopsis = "[-fnqsv] [file ...]"; - statfmt = "%Y"; - fmtchar = 'f'; - quiet = 1; - } else { - options = "f:FlLnqrst:x"; - synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; - } - - while ((ch = getopt(argc, argv, options)) != -1) - switch (ch) { - case 'F': - lsF = 1; - break; - case 'L': - usestat = 1; - break; - case 'n': - nonl = 1; - break; - case 'q': - quiet = 1; - break; - case 'f': - if (am_readlink) { - statfmt = "%R"; - break; - } - statfmt = optarg; - /* FALLTHROUGH */ - case 'l': - case 'r': - case 's': - if (am_readlink) { - quiet = 1; - break; - } - /*FALLTHROUGH*/ - case 'x': - if (fmtchar != 0) - errx(1, "can't use format '%c' with '%c'", - fmtchar, ch); - fmtchar = ch; - break; - case 't': - timefmt = optarg; - break; - case 'v': - quiet = 0; - break; - default: - usage(synopsis); - } - - argc -= optind; - argv += optind; - fn = 1; - - if (fmtchar == '\0') { - if (lsF) - fmtchar = 'l'; - else { - fmtchar = 'f'; - statfmt = DEF_FORMAT; - } - } - - if (lsF && fmtchar != 'l') - errx(1, "can't use format '%c' with -F", fmtchar); - - switch (fmtchar) { - case 'f': - /* statfmt already set */ - break; - case 'l': - statfmt = lsF ? LSF_FORMAT : LS_FORMAT; - break; - case 'r': - statfmt = RAW_FORMAT; - break; - case 's': - statfmt = SHELL_FORMAT; - if (timefmt == NULL) - timefmt = "%s"; - break; - case 'x': - statfmt = LINUX_FORMAT; - if (timefmt == NULL) - timefmt = "%c"; - break; - default: - usage(synopsis); - /*NOTREACHED*/ - } - - if (timefmt == NULL) - timefmt = TIME_FORMAT; - - errs = 0; - do { - if (argc == 0) - rc = fstat(STDIN_FILENO, &st); - else if (usestat) { - /* - * Try stat() and if it fails, fall back to - * lstat() just in case we're examining a - * broken symlink. - */ - if ((rc = stat(argv[0], &st)) == -1 && - errno == ENOENT && - (rc = lstat(argv[0], &st)) == -1) - errno = ENOENT; - } - else - rc = lstat(argv[0], &st); - - if (rc == -1) { - errs = 1; - linkfail = 1; - if (!quiet) - warn("%s: %s", - argc == 0 ? "(stdin)" : argv[0], - usestat ? "stat" : "lstat"); - } - else - output(&st, argv[0], statfmt, fn, nonl, quiet); - - argv++; - argc--; - fn++; - } while (argc > 0); - - return (am_readlink ? linkfail : errs); -} - -static void -usage(const char *synopsis) -{ - - (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); - exit(1); -} - -/* - * Parses a format string. - */ -static void -output(const struct stat *st, const char *file, - const char *statfmt, int fn, int nonl, int quiet) -{ - int flags, size, prec, ofmt, hilo, what; - /* - * buf size is enough for an item of length PATH_MAX, - * multiplied by 4 for vis encoding, plus 4 for symlink - * " -> " prefix, plus 1 for \0 terminator. - */ - char buf[PATH_MAX * 4 + 4 + 1]; - const char *subfmt; - int nl, t, i; - - nl = 1; - while (*statfmt != '\0') { - - /* - * Non-format characters go straight out. - */ - if (*statfmt != FMT_MAGIC) { - addchar(stdout, *statfmt, &nl); - statfmt++; - continue; - } - - /* - * The current format "substring" starts here, - * and then we skip the magic. - */ - subfmt = statfmt; - statfmt++; - - /* - * Some simple one-character "formats". - */ - switch (*statfmt) { - case SIMPLE_NEWLINE: - addchar(stdout, '\n', &nl); - statfmt++; - continue; - case SIMPLE_TAB: - addchar(stdout, '\t', &nl); - statfmt++; - continue; - case SIMPLE_PERCENT: - addchar(stdout, '%', &nl); - statfmt++; - continue; - case SIMPLE_NUMBER: { - char num[12], *p; - - snprintf(num, sizeof(num), "%d", fn); - for (p = &num[0]; *p; p++) - addchar(stdout, *p, &nl); - statfmt++; - continue; - } - } - - /* - * This must be an actual format string. Format strings are - * similar to printf(3) formats up to a point, and are of - * the form: - * - * % required start of format - * [-# +0] opt. format characters - * size opt. field width - * . opt. decimal separator, followed by - * prec opt. precision - * fmt opt. output specifier (string, numeric, etc.) - * sub opt. sub field specifier (high, middle, low) - * datum required field specifier (size, mode, etc) - * - * Only the % and the datum selector are required. All data - * have reasonable default output forms. The "sub" specifier - * only applies to certain data (mode, dev, rdev, filetype). - * The symlink output defaults to STRING, yet will only emit - * the leading " -> " if STRING is explicitly specified. The - * sizerdev datum will generate rdev output for character or - * block devices, and size output for all others. - * For STRING output, the # format requests vis encoding. - */ - flags = 0; - do { - if (*statfmt == FMT_POUND) - flags |= FLAG_POUND; - else if (*statfmt == FMT_SPACE) - flags |= FLAG_SPACE; - else if (*statfmt == FMT_PLUS) - flags |= FLAG_PLUS; - else if (*statfmt == FMT_ZERO) - flags |= FLAG_ZERO; - else if (*statfmt == FMT_MINUS) - flags |= FLAG_MINUS; - else - break; - statfmt++; - } while (1/*CONSTCOND*/); - - size = -1; - if (isdigit((unsigned)*statfmt)) { - size = 0; - while (isdigit((unsigned)*statfmt)) { - size = (size * 10) + (*statfmt - '0'); - statfmt++; - if (size < 0) - goto badfmt; - } - } - - prec = -1; - if (*statfmt == FMT_DOT) { - statfmt++; - - prec = 0; - while (isdigit((unsigned)*statfmt)) { - prec = (prec * 10) + (*statfmt - '0'); - statfmt++; - if (prec < 0) - goto badfmt; - } - } - -#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break -#define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break - switch (*statfmt) { - fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); - fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); - fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); - fmtcasef(ofmt, FMT_HEX, FMTF_HEX); - fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); - fmtcasef(ofmt, FMT_STRING, FMTF_STRING); - default: - ofmt = 0; - break; - } - - switch (*statfmt) { - fmtcase(hilo, HIGH_PIECE); - fmtcase(hilo, MIDDLE_PIECE); - fmtcase(hilo, LOW_PIECE); - default: - hilo = 0; - break; - } - - switch (*statfmt) { - fmtcase(what, SHOW_realpath); - fmtcase(what, SHOW_st_dev); - fmtcase(what, SHOW_st_ino); - fmtcase(what, SHOW_st_mode); - fmtcase(what, SHOW_st_nlink); - fmtcase(what, SHOW_st_uid); - fmtcase(what, SHOW_st_gid); - fmtcase(what, SHOW_st_rdev); - fmtcase(what, SHOW_st_atime); - fmtcase(what, SHOW_st_mtime); - fmtcase(what, SHOW_st_ctime); - fmtcase(what, SHOW_st_btime); - fmtcase(what, SHOW_st_size); - fmtcase(what, SHOW_st_blocks); - fmtcase(what, SHOW_st_blksize); - fmtcase(what, SHOW_st_flags); - fmtcase(what, SHOW_st_gen); - fmtcase(what, SHOW_symlink); - fmtcase(what, SHOW_filetype); - fmtcase(what, SHOW_filename); - fmtcase(what, SHOW_sizerdev); - default: - goto badfmt; - } -#undef fmtcasef -#undef fmtcase - - t = format1(st, - file, - subfmt, statfmt - subfmt, - buf, sizeof(buf), - flags, size, prec, ofmt, hilo, what, quiet); - - for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) - addchar(stdout, buf[i], &nl); - - continue; - - badfmt: - errx(1, "%.*s: bad format", - (int)(statfmt - subfmt + 1), subfmt); - } - - if (!nl && !nonl) - (void)fputc('\n', stdout); - (void)fflush(stdout); -} - -/* - * Arranges output according to a single parsed format substring. - */ -static int -format1(const struct stat *st, - const char *file, - const char *fmt, int flen, - char *buf, size_t blen, - int flags, int size, int prec, int ofmt, - int hilo, int what, int quiet) -{ - u_int64_t data; - char *stmp, lfmt[24], tmp[20]; - const char *sdata = NULL; - char smode[12], sid[12], path[PATH_MAX + 4], visbuf[PATH_MAX * 4 + 4]; - struct passwd *pw; - struct group *gr; - struct tm *tm; - time_t secs; - long nsecs; - int l; - int formats; /* bitmap of allowed formats for this datum */ - int small; /* true if datum is a small integer */ - int gottime; /* true if secs and nsecs are valid */ - int shift; /* powers of 2 to scale numbers before printing */ - size_t prefixlen; /* length of constant prefix for string data */ - - formats = 0; - small = 0; - gottime = 0; - secs = 0; - nsecs = 0; - shift = 0; - prefixlen = 0; - - /* - * First, pick out the data and tweak it based on hilo or - * specified output format (symlink output only). - */ - switch (what) { - case SHOW_st_dev: - case SHOW_st_rdev: - small = (sizeof(st->st_dev) == 4); - data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; -#if HAVE_DEVNAME - sdata = (what == SHOW_st_dev) ? - devname(st->st_dev, S_IFBLK) : - devname(st->st_rdev, - S_ISCHR(st->st_mode) ? S_IFCHR : - S_ISBLK(st->st_mode) ? S_IFBLK : - 0U); - if (sdata == NULL) - sdata = "???"; -#endif /* HAVE_DEVNAME */ - if (hilo == HIGH_PIECE) { - data = major(data); - hilo = 0; - } - else if (hilo == LOW_PIECE) { - data = minor((unsigned)data); - hilo = 0; - } - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | -#if HAVE_DEVNAME - FMTF_STRING; -#else /* HAVE_DEVNAME */ - 0; -#endif /* HAVE_DEVNAME */ - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_ino: - small = (sizeof(st->st_ino) == 4); - data = st->st_ino; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_mode: - small = (sizeof(st->st_mode) == 4); - data = st->st_mode; - strmode(st->st_mode, smode); - stmp = smode; - l = strlen(stmp); - if (stmp[l - 1] == ' ') - stmp[--l] = '\0'; - if (hilo == HIGH_PIECE) { - data >>= 12; - stmp += 1; - stmp[3] = '\0'; - hilo = 0; - } - else if (hilo == MIDDLE_PIECE) { - data = (data >> 9) & 07; - stmp += 4; - stmp[3] = '\0'; - hilo = 0; - } - else if (hilo == LOW_PIECE) { - data &= 0777; - stmp += 7; - stmp[3] = '\0'; - hilo = 0; - } - sdata = stmp; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | - FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_OCTAL; - break; - case SHOW_st_nlink: - small = (sizeof(st->st_dev) == 4); - data = st->st_nlink; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_uid: - small = (sizeof(st->st_uid) == 4); - data = st->st_uid; - if ((pw = getpwuid(st->st_uid)) != NULL) - sdata = pw->pw_name; - else { - snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); - sdata = sid; - } - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | - FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_gid: - small = (sizeof(st->st_gid) == 4); - data = st->st_gid; - if ((gr = getgrgid(st->st_gid)) != NULL) - sdata = gr->gr_name; - else { - snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); - sdata = sid; - } - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | - FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_atime: - gottime = 1; - secs = st->st_atime; -#if HAVE_STRUCT_STAT_ST_MTIMENSEC - nsecs = st->st_atimensec; -#endif - /* FALLTHROUGH */ - case SHOW_st_mtime: - if (!gottime) { - gottime = 1; - secs = st->st_mtime; -#if HAVE_STRUCT_STAT_ST_MTIMENSEC - nsecs = st->st_mtimensec; -#endif - } - /* FALLTHROUGH */ - case SHOW_st_ctime: - if (!gottime) { - gottime = 1; - secs = st->st_ctime; -#if HAVE_STRUCT_STAT_ST_MTIMENSEC - nsecs = st->st_ctimensec; -#endif - } - /* FALLTHROUGH */ -#if HAVE_STRUCT_STAT_ST_BIRTHTIME - case SHOW_st_btime: - if (!gottime) { - gottime = 1; - secs = st->st_birthtime; -#if HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC - nsecs = st->st_birthtimensec; -#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC */ - } -#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ - small = (sizeof(secs) == 4); - data = secs; - tm = localtime(&secs); - if (tm == NULL) { - secs = 0; - tm = localtime(&secs); - } - (void)strftime(path, sizeof(path), timefmt, tm); - sdata = path; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | - FMTF_FLOAT | FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_DECIMAL; - break; - case SHOW_st_size: - small = (sizeof(st->st_size) == 4); - data = st->st_size; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - switch (hilo) { - case HIGH_PIECE: - shift = 30; /* gigabytes */ - hilo = 0; - break; - case MIDDLE_PIECE: - shift = 20; /* megabytes */ - hilo = 0; - break; - case LOW_PIECE: - shift = 10; /* kilobytes */ - hilo = 0; - break; - } - break; - case SHOW_st_blocks: - small = (sizeof(st->st_blocks) == 4); - data = st->st_blocks; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; - case SHOW_st_blksize: - small = (sizeof(st->st_blksize) == 4); - data = st->st_blksize; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; -#if HAVE_STRUCT_STAT_ST_FLAGS - case SHOW_st_flags: - small = (sizeof(st->st_flags) == 4); - data = st->st_flags; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; -#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ -#if HAVE_STRUCT_STAT_ST_GEN - case SHOW_st_gen: - small = (sizeof(st->st_gen) == 4); - data = st->st_gen; - sdata = NULL; - formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; - if (ofmt == 0) - ofmt = FMTF_UNSIGNED; - break; -#endif /* HAVE_STRUCT_STAT_ST_GEN */ - case SHOW_realpath: - small = 0; - data = 0; - if (file == NULL) { - (void)strlcpy(path, "(stdin)", sizeof(path)); - sdata = path; - } else { - snprintf(path, sizeof(path), " -> "); - if (realpath(file, path + 4) == NULL) { - if (!quiet) - warn("realpath `%s'", file); - linkfail = 1; - l = 0; - path[0] = '\0'; - } - sdata = path + (ofmt == FMTF_STRING ? 0 : 4); - prefixlen = (ofmt == FMTF_STRING ? 4 : 0); - } - - formats = FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_STRING; - break; - case SHOW_symlink: - small = 0; - data = 0; - if (S_ISLNK(st->st_mode)) { - snprintf(path, sizeof(path), " -> "); - l = readlink(file, path + 4, sizeof(path) - 4 - 1); - if (l == -1) { - if (!quiet) - warn("readlink `%s'", file); - linkfail = 1; - l = 0; - path[0] = '\0'; - } - path[l + 4] = '\0'; - sdata = path + (ofmt == FMTF_STRING ? 0 : 4); - prefixlen = (ofmt == FMTF_STRING ? 4 : 0); - } - else { - linkfail = 1; - sdata = ""; - } - formats = FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_STRING; - break; - case SHOW_filetype: - small = 0; - data = 0; - sdata = ""; - if (hilo == 0 || hilo == LOW_PIECE) { - switch (st->st_mode & S_IFMT) { - case S_IFIFO: sdata = "|"; break; - case S_IFDIR: sdata = "/"; break; - case S_IFREG: - if (st->st_mode & - (S_IXUSR | S_IXGRP | S_IXOTH)) - sdata = "*"; - break; - case S_IFLNK: sdata = "@"; break; -#ifdef S_IFSOCK - case S_IFSOCK: sdata = "="; break; -#endif -#ifdef S_IFWHT - case S_IFWHT: sdata = "%"; break; -#endif /* S_IFWHT */ -#ifdef S_IFDOOR - case S_IFDOOR: sdata = ">"; break; -#endif /* S_IFDOOR */ - } - hilo = 0; - } - else if (hilo == HIGH_PIECE) { - switch (st->st_mode & S_IFMT) { - case S_IFIFO: sdata = "Fifo File"; break; - case S_IFCHR: sdata = "Character Device"; break; - case S_IFDIR: sdata = "Directory"; break; - case S_IFBLK: sdata = "Block Device"; break; - case S_IFREG: sdata = "Regular File"; break; - case S_IFLNK: sdata = "Symbolic Link"; break; -#ifdef S_IFSOCK - case S_IFSOCK: sdata = "Socket"; break; -#endif -#ifdef S_IFWHT - case S_IFWHT: sdata = "Whiteout File"; break; -#endif /* S_IFWHT */ -#ifdef S_IFDOOR - case S_IFDOOR: sdata = "Door"; break; -#endif /* S_IFDOOR */ - default: sdata = "???"; break; - } - hilo = 0; - } - formats = FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_STRING; - break; - case SHOW_filename: - small = 0; - data = 0; - if (file == NULL) { - (void)strlcpy(path, "(stdin)", sizeof(path)); - if (hilo == HIGH_PIECE || hilo == LOW_PIECE) - hilo = 0; - } - else if (hilo == 0) - (void)strlcpy(path, file, sizeof(path)); - else { - char *s; - (void)strlcpy(path, file, sizeof(path)); - s = strrchr(path, '/'); - if (s != NULL) { - /* trim off trailing /'s */ - while (s != path && - s[0] == '/' && s[1] == '\0') - *s-- = '\0'; - s = strrchr(path, '/'); - } - if (hilo == HIGH_PIECE) { - if (s == NULL) - (void)strlcpy(path, ".", sizeof(path)); - else { - while (s != path && s[0] == '/') - *s-- = '\0'; - } - hilo = 0; - } - else if (hilo == LOW_PIECE) { - if (s != NULL && s[1] != '\0') - (void)strlcpy(path, s + 1, - sizeof(path)); - hilo = 0; - } - } - sdata = path; - formats = FMTF_STRING; - if (ofmt == 0) - ofmt = FMTF_STRING; - break; - case SHOW_sizerdev: - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { - char majdev[20], mindev[20]; - int l1, l2; - - l1 = format1(st, - file, - fmt, flen, - majdev, sizeof(majdev), - flags, size, prec, - ofmt, HIGH_PIECE, SHOW_st_rdev, quiet); - l2 = format1(st, - file, - fmt, flen, - mindev, sizeof(mindev), - flags, size, prec, - ofmt, LOW_PIECE, SHOW_st_rdev, quiet); - return (snprintf(buf, blen, "%.*s,%.*s", - l1, majdev, l2, mindev)); - } - else { - return (format1(st, - file, - fmt, flen, - buf, blen, - flags, size, prec, - ofmt, 0, SHOW_st_size, quiet)); - } - /*NOTREACHED*/ - default: - errx(1, "%.*s: bad format", (int)flen, fmt); - } - - /* - * If a subdatum was specified but not supported, or an output - * format was selected that is not supported, that's an error. - */ - if (hilo != 0 || (ofmt & formats) == 0) - errx(1, "%.*s: bad format", (int)flen, fmt); - - /* - * FLAG_POUND with FMTF_STRING means use vis(3) encoding. - * First prefixlen chars are not encoded. - */ - if ((flags & FLAG_POUND) != 0 && ofmt == FMTF_STRING) { - flags &= !FLAG_POUND; - strncpy(visbuf, sdata, prefixlen); - strnvis(visbuf + prefixlen, sizeof(visbuf) - prefixlen, - sdata + prefixlen, VIS_WHITE | VIS_OCTAL | VIS_CSTYLE); - sdata = visbuf; - } - - /* - * Assemble the format string for passing to printf(3). - */ - lfmt[0] = '\0'; - (void)strcat(lfmt, "%"); - if (flags & FLAG_POUND) - (void)strcat(lfmt, "#"); - if (flags & FLAG_SPACE) - (void)strcat(lfmt, " "); - if (flags & FLAG_PLUS) - (void)strcat(lfmt, "+"); - if (flags & FLAG_MINUS) - (void)strcat(lfmt, "-"); - if (flags & FLAG_ZERO) - (void)strcat(lfmt, "0"); - - /* - * Only the timespecs support the FLOAT output format, and that - * requires work that differs from the other formats. - */ - if (ofmt == FMTF_FLOAT) { - /* - * Nothing after the decimal point, so just print seconds. - */ - if (prec == 0) { - if (size != -1) { - (void)snprintf(tmp, sizeof(tmp), "%d", size); - (void)strcat(lfmt, tmp); - } - (void)strcat(lfmt, "lld"); - return (snprintf(buf, blen, lfmt, - (long long)secs)); - } - - /* - * Unspecified precision gets all the precision we have: - * 9 digits. - */ - if (prec == -1) - prec = 9; - - /* - * Adjust the size for the decimal point and the digits - * that will follow. - */ - size -= prec + 1; - - /* - * Any leftover size that's legitimate will be used. - */ - if (size > 0) { - (void)snprintf(tmp, sizeof(tmp), "%d", size); - (void)strcat(lfmt, tmp); - } - /* Seconds: time_t cast to long long. */ - (void)strcat(lfmt, "lld"); - - /* - * The stuff after the decimal point always needs zero - * filling. - */ - (void)strcat(lfmt, ".%0"); - - /* - * We can "print" at most nine digits of precision. The - * rest we will pad on at the end. - * - * Nanoseconds: long. - */ - (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); - (void)strcat(lfmt, tmp); - - /* - * For precision of less that nine digits, trim off the - * less significant figures. - */ - for (; prec < 9; prec++) - nsecs /= 10; - - /* - * Use the format, and then tack on any zeroes that - * might be required to make up the requested precision. - */ - l = snprintf(buf, blen, lfmt, (long long)secs, nsecs); - for (; prec > 9 && l < (int)blen; prec--, l++) - (void)strcat(buf, "0"); - return (l); - } - - /* - * Add on size and precision, if specified, to the format. - */ - if (size != -1) { - (void)snprintf(tmp, sizeof(tmp), "%d", size); - (void)strcat(lfmt, tmp); - } - if (prec != -1) { - (void)snprintf(tmp, sizeof(tmp), ".%d", prec); - (void)strcat(lfmt, tmp); - } - - /* - * String output uses the temporary sdata. - */ - if (ofmt == FMTF_STRING) { - if (sdata == NULL) - errx(1, "%.*s: bad format", (int)flen, fmt); - (void)strcat(lfmt, "s"); - return (snprintf(buf, blen, lfmt, sdata)); - } - - /* - * Ensure that sign extension does not cause bad looking output - * for some forms. - */ - if (small && ofmt != FMTF_DECIMAL) - data = (u_int32_t)data; - - /* - * The four "numeric" output forms. - */ - (void)strcat(lfmt, "ll"); - switch (ofmt) { - case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; - case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; - case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; - case FMTF_HEX: (void)strcat(lfmt, "x"); break; - } - - /* - * shift and round to nearest for kilobytes, megabytes, - * gigabytes. - */ - if (shift > 0) { - data >>= (shift - 1); - data++; - data >>= 1; - } - - return (snprintf(buf, blen, lfmt, data)); -} diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile deleted file mode 100644 index 14dee1e440..0000000000 --- a/usr.bin/su/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -TOPDIR = $(realpath ../..) - -PROGRAM = su -SOURCES = su.c suutil.c -BINMODE = 4755 -LDLIBS = -lutil - -include $(TOPDIR)/build/build.prog.mk - -WFLAGS += -Wno-format-nonliteral diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c deleted file mode 100644 index 38cbd26066..0000000000 --- a/usr.bin/su/su.c +++ /dev/null @@ -1,548 +0,0 @@ -/* $NetBSD: su.c,v 1.72 2015/06/16 22:54:11 christos Exp $ */ - -/* - * Copyright (c) 1988 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef SKEY -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#ifdef LOGIN_CAP -#include -#endif - -#ifdef KERBEROS5 -#include -#endif - -#ifdef ALLOW_GROUP_CHANGE -#include "grutil.h" -#endif -#include "suutil.h" - -#ifdef KERBEROS5 -#define ARGSTRX "-Kdflm" -static int kerberos5(char *, const char *, uid_t); -int use_kerberos = 1; -#else -#define ARGSTRX "-dflm" -#endif - -#ifndef SU_GROUP -#define SU_GROUP "wheel" -#endif - -#define GROUP_PASSWORD "Group Password:" - -#ifdef LOGIN_CAP -#define ARGSTR ARGSTRX "c:" -#else -#define ARGSTR ARGSTRX -#endif - -static int check_ingroup(int, const char *, const char *, int); - -int -main(int argc, char **argv) -{ - extern char **environ; - struct passwd *pwd; - char *p; - uid_t ruid; - int asme, ch, asthem, fastlogin, prio, gohome; - enum { UNSET, YES, NO } iscsh = UNSET; - const char *user, *shell, *avshell; - char *username, **np; -#ifdef SU_ROOTAUTH - char *userpass; -#endif - char *class; - char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN]; -#ifdef LOGIN_CAP - time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY; - login_cap_t *lc; -#endif -#ifdef ALLOW_GROUP_CHANGE - char *gname; -#endif - - (void)setprogname(argv[0]); - asme = asthem = fastlogin = 0; - gohome = 1; - shell = class = NULL; - while ((ch = getopt(argc, argv, ARGSTR)) != -1) - switch((char)ch) { -#ifdef KERBEROS5 - case 'K': - use_kerberos = 0; - break; -#endif -#ifdef LOGIN_CAP - case 'c': - class = optarg; - break; -#endif - case 'd': - asme = 0; - asthem = 1; - gohome = 0; - break; - case 'f': - fastlogin = 1; - break; - case '-': - case 'l': - asme = 0; - asthem = 1; - break; - case 'm': - asme = 1; - asthem = 0; - break; - case '?': - default: - (void)fprintf(stderr, -#ifdef ALLOW_GROUP_CHANGE - "usage: %s [%s] [login[:group] [shell arguments]]\n", -#else - "usage: %s [%s] [login [shell arguments]]\n", -#endif - getprogname(), ARGSTR); - exit(EXIT_FAILURE); - } - argv += optind; - - /* Lower the priority so su runs faster */ - errno = 0; - prio = getpriority(PRIO_PROCESS, 0); - if (errno) - prio = 0; - if (prio > -2) - (void)setpriority(PRIO_PROCESS, 0, -2); - - /* get current login name and shell */ - ruid = getuid(); - username = getlogin(); - if (username == NULL || (pwd = getpwnam(username)) == NULL || - pwd->pw_uid != ruid) - pwd = getpwuid(ruid); - if (pwd == NULL) - errx(EXIT_FAILURE, "who are you?"); - username = estrdup(pwd->pw_name); -#ifdef SU_ROOTAUTH - userpass = estrdup(pwd->pw_passwd); -#endif - - if (asme) { - if (pwd->pw_shell && *pwd->pw_shell) { - (void)estrlcpy(shellbuf, pwd->pw_shell, sizeof(shellbuf)); - shell = shellbuf; - } else { - shell = _PATH_BSHELL; - iscsh = NO; - } - } - /* get target login information, default to root */ - user = *argv ? *argv : "root"; - np = *argv ? argv : argv - 1; - -#ifdef ALLOW_GROUP_CHANGE - if ((p = strchr(user, ':')) != NULL) { - *p = '\0'; - gname = ++p; - } - else - gname = NULL; - -#ifdef ALLOW_EMPTY_USER - if (user[0] == '\0' && gname != NULL) - user = username; -#endif -#endif - if ((pwd = getpwnam(user)) == NULL) - errx(EXIT_FAILURE, "unknown login `%s'", user); - -#ifdef LOGIN_CAP - /* force the usage of specified class */ - if (class) { - if (ruid) - errx(EXIT_FAILURE, "Only root may use -c"); - - pwd->pw_class = class; - } - if ((lc = login_getclass(pwd->pw_class)) == NULL) - errx(EXIT_FAILURE, "Unknown class %s", pwd->pw_class); - - pw_warntime = (time_t)login_getcaptime(lc, "password-warn", - _PASSWORD_WARNDAYS * SECSPERDAY, - _PASSWORD_WARNDAYS * SECSPERDAY); -#endif - - if (ruid -#ifdef KERBEROS5 - && (!use_kerberos || kerberos5(username, user, pwd->pw_uid)) -#endif - ) { - char *pass = strdup(pwd->pw_passwd); - int ok = pwd->pw_uid != 0; - -#ifdef SU_ROOTAUTH - /* - * Allow those in group rootauth to su to root, by supplying - * their own password. - */ - if (!ok) { - if ((ok = check_ingroup(-1, SU_ROOTAUTH, username, 0))) { - pass = userpass; - user = username; - } - } -#endif - /* - * Only allow those in group SU_GROUP to su to root, - * but only if that group has any members. - * If SU_GROUP has no members, allow anyone to su root - */ - if (!ok) - ok = check_ingroup(-1, SU_GROUP, username, 1); - if (!ok) - errx(EXIT_FAILURE, - "you are not listed in the correct secondary group (%s) to su %s.", - SU_GROUP, user); - /* if target requires a password, verify it */ - if (*pass && pwd->pw_uid != ruid) { /* XXX - OK? */ - p = getpass("Password:"); -#ifdef SKEY - if (strcasecmp(p, "s/key") == 0) { - if (skey_haskey(user)) - errx(EXIT_FAILURE, - "Sorry, you have no s/key."); - else { - if (skey_authenticate(user)) { - goto badlogin; - } - } - - } else -#endif - if (strcmp(pass, crypt(p, pass)) != 0) { -#ifdef SKEY - badlogin: -#endif - (void)fprintf(stderr, "Sorry\n"); - syslog(LOG_WARNING, - "BAD SU %s to %s%s", username, - pwd->pw_name, ontty()); - exit(EXIT_FAILURE); - } - } - } - - if (asme) { - /* if asme and non-standard target shell, must be root */ - if (chshell(pwd->pw_shell) == 0 && ruid) - errx(EXIT_FAILURE, "permission denied (shell)."); - } else if (pwd->pw_shell && *pwd->pw_shell) { - shell = pwd->pw_shell; - iscsh = UNSET; - } else { - shell = _PATH_BSHELL; - iscsh = NO; - } - - if ((p = strrchr(shell, '/')) != NULL) - avshell = p+1; - else - avshell = shell; - - /* if we're forking a csh, we want to slightly muck the args */ - if (iscsh == UNSET) - iscsh = strstr(avshell, "csh") ? YES : NO; - - /* set permissions */ -#ifdef LOGIN_CAP -# ifdef ALLOW_GROUP_CHANGE - /* if we aren't changing users, keep the current group members */ - if (ruid != pwd->pw_uid && - setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) == -1) - err(EXIT_FAILURE, "setting user context"); - - addgroup(lc, gname, pwd, ruid, GROUP_PASSWORD); - - if (setusercontext(lc, pwd, pwd->pw_uid, - (u_int)(asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) | - LOGIN_SETRESOURCES | LOGIN_SETUSER) == -1) - err(EXIT_FAILURE, "setting user context"); -# else - if (setusercontext(lc, pwd, pwd->pw_uid, - (u_int)(asthem ? (LOGIN_SETPRIORITY | LOGIN_SETUMASK) : 0) | - LOGIN_SETRESOURCES | LOGIN_SETGROUP | LOGIN_SETUSER) == -1) - err(EXIT_FAILURE, "setting user context"); -# endif -#else - if (setgid(pwd->pw_gid) == -1) - err(EXIT_FAILURE, "setgid"); - /* if we aren't changing users, keep the current group members */ - if (ruid != pwd->pw_uid && initgroups(user, pwd->pw_gid) != 0) - errx(EXIT_FAILURE, "initgroups failed"); -# ifdef ALLOW_GROUP_CHANGE - addgroup(/*EMPTY*/, gname, pwd, ruid, GROUP_PASSWORD); -# endif - if (setuid(pwd->pw_uid) == -1) - err(EXIT_FAILURE, "setuid"); -#endif - - if (!asme) { - if (asthem) { - p = getenv("TERM"); - /* Create an empty environment */ - environ = emalloc(sizeof(char *)); - environ[0] = NULL; -#ifdef LOGIN_CAP - if (setusercontext(lc, pwd, pwd->pw_uid, - LOGIN_SETPATH) == -1) - err(EXIT_FAILURE, "setting user context"); -#else - (void)setenv("PATH", _PATH_DEFPATH, 1); -#endif - if (p) - (void)setenv("TERM", p, 1); - if (gohome && chdir(pwd->pw_dir) == -1) - errx(EXIT_FAILURE, "no directory"); - } - - if (asthem || pwd->pw_uid) { - (void)setenv("LOGNAME", pwd->pw_name, 1); - (void)setenv("USER", pwd->pw_name, 1); - } - (void)setenv("HOME", pwd->pw_dir, 1); - (void)setenv("SHELL", shell, 1); - } - (void)setenv("SU_FROM", username, 1); - - if (iscsh == YES) { - if (fastlogin) - *np-- = __UNCONST("-f"); - if (asme) - *np-- = __UNCONST("-m"); - } else { - if (fastlogin) - (void)unsetenv("ENV"); - } - - if (asthem) { - avshellbuf[0] = '-'; - (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1); - avshell = avshellbuf; - } else if (iscsh == YES) { - /* csh strips the first character... */ - avshellbuf[0] = '_'; - (void)estrlcpy(avshellbuf + 1, avshell, sizeof(avshellbuf) - 1); - avshell = avshellbuf; - } - *np = __UNCONST(avshell); - - if (ruid != 0) - syslog(LOG_NOTICE, "%s to %s%s", - username, pwd->pw_name, ontty()); - - /* Raise our priority back to what we had before */ - (void)setpriority(PRIO_PROCESS, 0, prio); - - (void)execv(shell, np); - err(EXIT_FAILURE, "%s", shell); - /* NOTREACHED */ -} - - -#ifdef KERBEROS5 -static int -kerberos5(char *username, const char *user, uid_t uid) -{ - krb5_error_code ret; - krb5_context context; - krb5_principal princ = NULL; - krb5_ccache ccache, ccache2; - char *cc_name; - const char *filename; - - ret = krb5_init_context(&context); - if (ret) - return 1; - - if (strcmp(user, "root") == 0) - ret = krb5_make_principal(context, &princ, - NULL, username, "root", NULL); - else - ret = krb5_make_principal(context, &princ, - NULL, user, NULL); - if (ret) - goto fail; - if (!krb5_kuserok(context, princ, user) && !uid) { - warnx("kerberos5: not in %s's ACL.", user); - goto fail; - } - ret = krb5_cc_new_unique(context, krb5_mcc_ops.prefix, NULL, &ccache); - if (ret) - goto fail; - ret = krb5_verify_user_lrealm(context, princ, ccache, NULL, TRUE, - NULL); - if (ret) { - krb5_cc_destroy(context, ccache); - switch (ret) { - case KRB5_LIBOS_PWDINTR : - break; - case KRB5KRB_AP_ERR_BAD_INTEGRITY: - case KRB5KRB_AP_ERR_MODIFIED: - krb5_warnx(context, "Password incorrect"); - break; - default : - krb5_warn(context, ret, "krb5_verify_user"); - break; - } - goto fail; - } - ret = krb5_cc_new_unique(context, krb5_mcc_ops.prefix, NULL, &ccache2); - if (ret) { - krb5_cc_destroy(context, ccache); - goto fail; - } - ret = krb5_cc_copy_cache(context, ccache, ccache2); - if (ret) { - krb5_cc_destroy(context, ccache); - krb5_cc_destroy(context, ccache2); - goto fail; - } - - filename = krb5_cc_get_name(context, ccache2); - (void)easprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), - filename); - if (chown(filename, uid, NOGROUP) == -1) { - warn("chown %s", filename); - free(cc_name); - krb5_cc_destroy(context, ccache); - krb5_cc_destroy(context, ccache2); - goto fail; - } - - (void)setenv("KRB5CCNAME", cc_name, 1); - free(cc_name); - krb5_cc_close(context, ccache2); - krb5_cc_destroy(context, ccache); - return 0; - - fail: - if (princ != NULL) - krb5_free_principal(context, princ); - krb5_free_context(context); - return 1; -} -#endif /* KERBEROS5 */ - -static int -check_ingroup(int gid, const char *gname, const char *user, int ifempty) -{ - struct group *gr; - const char * const*g; -#ifdef SU_INDIRECT_GROUP - char **gr_mem; - int n = 0; - int i = 0; -#endif - int ok = 0; - - if (gname == NULL) - gr = getgrgid((gid_t) gid); - else - gr = getgrnam(gname); - - /* - * XXX we are relying on the fact that we only set ifempty when - * calling to check for SU_GROUP and that is the only time a - * missing group is acceptable. - */ - if (gr == NULL) - return ifempty; - if (!*gr->gr_mem) /* empty */ - return ifempty; - - /* - * Ok, first see if user is in gr_mem - */ - for (g = gr->gr_mem; *g; ++g) { - if (strcmp(*g, user) == 0) - return 1; /* ok */ -#ifdef SU_INDIRECT_GROUP - ++n; /* count them */ -#endif - } -#ifdef SU_INDIRECT_GROUP - /* - * No. - * Now we need to duplicate the gr_mem list, and recurse for - * each member to see if it is a group, and if so whether user is - * in it. - */ - gr_mem = emalloc((n + 1) * sizeof (char *)); - for (g = gr->gr_mem, i = 0; *g; ++g) { - gr_mem[i] = estrdup(*g); - i++; - } - gr_mem[i++] = NULL; - - for (g = gr_mem; ok == 0 && *g; ++g) { - /* - * If we get this far we don't accept empty/missing groups. - */ - ok = check_ingroup(-1, *g, user, 0); - } - for (g = gr_mem; *g; ++g) { - free(*g); - } - free(gr_mem); -#endif - return ok; -} diff --git a/wiki/docs/running.md b/wiki/docs/running.md index 6cf6bc4545..5281f0e698 100644 --- a/wiki/docs/running.md +++ b/wiki/docs/running.md @@ -22,6 +22,8 @@ Some useful flags to the `launch` script: * `-d` - Starts simulation under a debugger. * `-b` - To specify emulated board (if different than Raspberry Pi 3). * `-g` - Opens a window with graphics display, if the platform supports it. +* `-k` - Save kft dump to file (`dump.kft`). Works only when compiled with + kernel function trace. Can be combined with `-d` flag. Any other argument is passed to the kernel as a kernel command-line argument. Some useful kernel arguments: