logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git
commit: e8c67c47e2f5696768f3c7d044225aeb0febf070
parent 30848fea2e3e9c9020b89ef70920f251afb153ff
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Thu,  1 Dec 2022 02:01:27 +0100

cmd/time: New utility

Diffstat:

M.builds/freebsd.yml1+
M.builds/netbsd.yml5+++--
M.gitignore1+
MMakefile1+
MREADME.md1+
Acmd/time.148++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/time.c112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mconfigure19+++++++++++++++++++
Mlsb_commands.txt2+-
Mtest-cmd/Kyuafile2+-
Atest-cmd/time.t31+++++++++++++++++++++++++++++++
11 files changed, 219 insertions(+), 4 deletions(-)

diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml @@ -6,6 +6,7 @@ packages: - pkgconf - kyua - gcc + - devel/cram sources: - https://hacktivis.me/git/utils.git tasks: diff --git a/.builds/netbsd.yml b/.builds/netbsd.yml @@ -6,17 +6,18 @@ packages: - pkg-config - kyua - clang +# FIXME: cram/prysk on NetBSD, where? sources: - https://hacktivis.me/git/utils.git tasks: - clang: | cd utils CC=clang ./configure - DESTDIR=/tmp/clang-destdir make clean test + CRAM=true DESTDIR=/tmp/clang-destdir make clean test - gcc: | cd utils CC=gcc ./configure - DESTDIR=/tmp/gcc-destdir make clean test + CRAM=true DESTDIR=/tmp/gcc-destdir make clean test triggers: - action: email condition: failure diff --git a/.gitignore b/.gitignore @@ -7,6 +7,7 @@ !/cmd/*.c !/cmd/*.ha !/cmd/*.1 +*.t.err # Kyua /html/ diff --git a/Makefile b/Makefile @@ -16,6 +16,7 @@ all: $(EXE) .PHONY: test test: all MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 kyua test || (kyua report --verbose --results-filter=broken,failed; false) + MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 $(CRAM) test-cmd/*.t .PHONY: lint lint: diff --git a/README.md b/README.md @@ -16,6 +16,7 @@ Tested on Linux(musl), FreeBSD, NetBSD, OpenBSD: - (optional, test) ATF: <https://github.com/jmmv/atf> - (optional, test) Kyua: <https://github.com/jmmv/kyua> - (optional, test) bwrap: <https://github.com/containers/bubblewrap/> For safely overlaying false files on the root filesystem +- (optional, test) [cram](https://bitheap.org/cram) or [prysk](https://www.prysk.net/) - (optional, lint) mandoc: <https://mdocml.bsd.lv/> For linting the manual pages - (optional, lint) shellcheck: <https://www.shellcheck.net/> For linting `./configure` and shell scripts diff --git a/cmd/time.1 b/cmd/time.1 @@ -0,0 +1,48 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only +.Dd 2022-12-01 +.Dt TIME 1 +.Os +.Sh NAME +.Nm time +.Nd measure time used by a command +.Sh SYNOPSIS +.Nm +.Op Fl p +.Ar command +.Op Ar argument... +.Sh DESCRIPTION +.Nm +measures the wall-clock time, user CPU time, system/kernel CPU time, used by +.Ar command +and outputs it to stderr. +.Sh OPTIONS +.Fl p +is ignored for compatibility reasons, the output is always in the POSIX format. +.Sh EXIT STATUS +If +.Ar command +is invoked, the exit status should be the one given by +.Ar command . +Otherwise, it will exit with the following values: +.Bl -tag -width 1-125 +.It 1-125 +An error occured in +.Ar command +.It 126 +.Ar command +was found but couldn't be invoked +.It 127 +.Ar command +could not be found +.El +.Sh SEE ALSO +.Xr times 3 +.Sh STANDARDS +.Nm +is compliant with the +.St -p1003.1-2008 +specification. +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/cmd/time.c b/cmd/time.c @@ -0,0 +1,112 @@ +// Collection of Unix tools, comparable to coreutils +// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + +#define _POSIX_C_SOURCE 200809L +#include <errno.h> // errno +#include <stdio.h> // perror, fprintf +#include <stdlib.h> // abort +#include <sys/times.h> // times +#include <sys/wait.h> // waitpid +#include <unistd.h> // sysconf, fork, execvp, getopt + +void +usage() +{ + fprintf(stderr, "Usage: time command [argument ...]\n"); +} + +int +main(int argc, char *argv[]) +{ + struct tms tms; + int ret = 0; + + if(argc <= 1) + { + usage(); + return 0; + } + + int c = -1; + while((c = getopt(argc, argv, ":p")) != -1) + { + switch(c) + { + case 'p': // POSIX format (default) + break; + case ':': + fprintf(stderr, "time: Error: Missing operand for option: '-%c'\n", optopt); + usage(); + return 1; + case '?': + fprintf(stderr, "time: Error: Unrecognised option: '-%c'\n", optopt); + usage(); + return 1; + } + } + + argc -= optind; + argv += optind; + (void)argc; + + long ticks = sysconf(_SC_CLK_TCK); + if(ticks <= 0) + { + perror("time: sysconf(_SC_CLK_TCK)"); + return 1; + } + + clock_t t0 = times(&tms); + if(t0 == (clock_t)-1) + { + perror("time: times"); + return 1; + } + + // Note: posix_spawnp seems to lack enough error information + pid_t pid = fork(); + switch(pid) + { + case -1: + perror("time: fork"); + return 1; + case 0: + /* flawfinder: ignore. No restrictions on commands is intended */ + execvp(argv[0], argv); + ret = 126 + (errno == ENOENT); + perror("time: execvp"); + return ret; + default: + break; + } + + int status = 0; + waitpid(pid, &status, 0); + + int t1 = times(&tms); + if(t1 == (clock_t)-1) + { + perror("time: times"); + return 1; + } + + if(WIFSIGNALED(status)) + { + fprintf(stderr, "time: Command terminated by signal %d\n", WTERMSIG(status)); + ret = 128 + WTERMSIG(status); + } + + fprintf(stderr, + "real %f\nuser %f\nsys %f\n", + (t1 - t0) / (double)ticks, + tms.tms_cutime / (double)ticks, + tms.tms_cstime / (double)ticks); + + if(WIFEXITED(status)) + { + ret = WEXITSTATUS(status); + } + + return ret; +} diff --git a/configure b/configure @@ -23,6 +23,7 @@ Variables: SHELLCHECK=BIN FLAWFINDER=BIN GCOV=BIN + CRAM=BIN CFLAGS=OPTIONS LDFLAGS=OPTIONS @@ -92,6 +93,7 @@ CFLAGS="${CFLAGS:--g -O2}" MANDOC="${MANDOC:-mandoc}" SHELLCHECK="${SHELLCHECK:-shellcheck}" FLAWFINDER="${FLAWFINDER:-flawfinder}" +CRAM="${CRAM:-cram}" # Also allow variables through arguments for i; do @@ -168,6 +170,22 @@ else FLAWFINDER="true" fi +if check_cmd CRAM "$CRAM" +then + : +else + echo "Notice: cram not found, trying prysk" + + CRAM="prysk" + if check_cmd CRAM "$CRAM" + then + : + else + CRAM="false" + echo 'Notice: Testsuite depending on cram/psyk disabled' + fi +fi + echo # pkg-config @@ -217,6 +235,7 @@ FLAWFINDER = ${FLAWFINDER} MSGFMT = ${MSGFMT} DBG = ${DBG} GCOV = ${GCOV} +CRAM = ${CRAM} CFLAGS = ${CFLAGS} LDFLAGS = ${LDFLAGS} diff --git a/lsb_commands.txt b/lsb_commands.txt @@ -122,7 +122,7 @@ tar: out of scope tee: Todo test: ? tic: out of scope -time: ? +time: Done touch: Todo (futimens(3p)) tput: out of scope tr: Todo diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile @@ -6,7 +6,7 @@ test_suite("utils") basedir = fs.dirname(fs.dirname(current_kyuafile())) --- 7,$|LC_ALL=C.UTF-8 sort +-- 9,$|LC_ALL=C.UTF-8 sort atf_test_program{name="args", required_files=basedir.."/cmd/args", timeout=1} atf_test_program{name="basename", required_files=basedir.."/cmd/basename", timeout=1} atf_test_program{name="cat", required_files=basedir.."/cmd/cat", timeout=1} diff --git a/test-cmd/time.t b/test-cmd/time.t @@ -0,0 +1,31 @@ +#!/usr/bin/env cram +# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only + + $ cd $TESTDIR/../cmd + + $ ./time + Usage: time command [argument ...] + + $ ./time -f + time: Error: Unrecognised option: '-f' + Usage: time command [argument ...] + [1] + + $ ./time /var/empty/e/no/ent + time: execvp: No such file or directory + real 0.[0-9]* (re) + user 0.[0-9]* (re) + sys 0.[0-9]* (re) + [127] + + $ ./time false + real 0.[0-9]* (re) + user 0.[0-9]* (re) + sys 0.[0-9]* (re) + [1] + + $ ./time true + real 0.[0-9]* (re) + user 0.[0-9]* (re) + sys 0.[0-9]* (re)