diff --git a/.builds/alpine.yml b/.builds/alpine.yml new file mode 100644 index 0000000..bbf26fb --- /dev/null +++ b/.builds/alpine.yml @@ -0,0 +1,11 @@ +image: alpine/edge +sources: + - https://github.com/martanne/abduco +tasks: + - build: | + cd abduco + ./configure + make + - test: | + cd abduco + ./testsuite.sh diff --git a/.builds/debian.yml b/.builds/debian.yml new file mode 100644 index 0000000..a4f42a0 --- /dev/null +++ b/.builds/debian.yml @@ -0,0 +1,11 @@ +image: debian/stable +sources: + - https://github.com/martanne/abduco +tasks: + - build: | + cd abduco + ./configure + make + - test: | + cd abduco + ./testsuite.sh diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 0000000..0927677 --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,11 @@ +image: freebsd/latest +sources: + - https://github.com/martanne/abduco +tasks: + - build: | + cd abduco + ./configure + make + - test: | + cd abduco + ./testsuite.sh diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml new file mode 100644 index 0000000..59d1b5f --- /dev/null +++ b/.builds/openbsd.yml @@ -0,0 +1,11 @@ +image: openbsd/latest +sources: + - https://github.com/martanne/abduco +tasks: + - build: | + cd abduco + ./configure + make + - test: | + cd abduco + ./testsuite.sh diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml new file mode 100644 index 0000000..0f2b2bb --- /dev/null +++ b/.github/workflows/coverity-scan.yml @@ -0,0 +1,46 @@ +name: Coverity Scan + +env: + PROJECT: abduco + +on: + schedule: + - cron: '0 0 * * 0' # once a week + +jobs: + scan: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Download Coverity Build Tool + run: | + wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=martanne/${PROJECT}" -O cov-analysis-linux64.tar.gz + mkdir cov-analysis-linux64 + tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + + - name: Configure + run: ./configure + + - name: Build with cov-build + run: | + export PATH=$(pwd)/cov-analysis-linux64/bin:$PATH + cov-build --dir cov-int make + + - name: Submit the result to Coverity Scan + run: | + tar czvf ${PROJECT}.tgz cov-int + curl \ + --form project=martanne/${PROJECT} \ + --form token=$TOKEN \ + --form email=mat@brain-dump.org \ + --form file=@${PROJECT}.tgz \ + --form version=trunk \ + --form description="`./${PROJECT} -v`" \ + https://scan.coverity.com/builds?project=martanne/${PROJECT} + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/.github/workflows/man.yml b/.github/workflows/man.yml new file mode 100644 index 0000000..9c3b020 --- /dev/null +++ b/.github/workflows/man.yml @@ -0,0 +1,44 @@ +name: Manual + +env: + PROJECT: abduco + +on: + push: + paths: + - '*.1' + +jobs: + man: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Dependency + run: sudo apt install mandoc + + - name: Manual generation + run: | + mkdir man + sed -e "s/VERSION/$(git describe --always)/" ${PROJECT}.1 | \ + mandoc -W warning -T utf8 -T html -O man=%N.%S.html -O style=mandoc.css 1> \ + "man/${PROJECT}.1.html" || true + wget 'https://cvsweb.bsd.lv/~checkout~/mandoc/mandoc.css?rev=1.46&content-type=text/plain' -O man/mandoc.css + ln -sf "${PROJECT}.1.html" man/index.html + + - name: Upload + env: + DEPLOY_TOKEN: ${{ secrets.GIT_DEPLOY_TOKEN }} + run: | + git clone --depth=1 --single-branch --branch gh-pages "https://x-access-token:${DEPLOY_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" /tmp/gh-pages + git config --global user.name "${GITHUB_ACTOR}" + git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" + mkdir -p /tmp/gh-pages/man + rm -f /tmp/gh-pages/man/* + cp -av man/*.html /tmp/gh-pages/man/ + cp -av man/*.css /tmp/gh-pages/man/ + cd /tmp/gh-pages + git add -A && git commit --allow-empty -am "Publishing from ${GITHUB_REPOSITORY} ${GITHUB_SHA}" + git push origin gh-pages diff --git a/.gitignore b/.gitignore index 2ae5602..a859e4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -# normal ignores -.* -*.[ao] -*.lo -*.so -tags -!.gitignore +/config.h +/config.mk +/abduco +*.css +*.gcda +*.gcno +*.gcov +*.html +*.o diff --git a/Makefile b/Makefile index 88a5df8..d59909b 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,10 @@ CFLAGS_STD += -DVERSION=\"${VERSION}\" LDFLAGS_STD ?= -lc -lutil STRIP ?= strip +INSTALL ?= install + +PREFIX ?= /usr/local +SHAREDIR ?= ${PREFIX}/share SRC = abduco.c @@ -33,22 +37,31 @@ dist: clean @echo creating dist tarball @git archive --prefix=abduco-${VERSION}/ -o abduco-${VERSION}.tar.gz HEAD -install: abduco - @echo stripping executable - @${STRIP} abduco +installdirs: + @${INSTALL} -d ${DESTDIR}${PREFIX}/bin \ + ${DESTDIR}${MANPREFIX}/man1 + +install: abduco installdirs @echo installing executable file to ${DESTDIR}${PREFIX}/bin - @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f abduco ${DESTDIR}${PREFIX}/bin - @chmod 755 ${DESTDIR}${PREFIX}/bin/abduco + @${INSTALL} -m 0755 abduco ${DESTDIR}${PREFIX}/bin @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1 @sed "s/VERSION/${VERSION}/g" < abduco.1 > ${DESTDIR}${MANPREFIX}/man1/abduco.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/abduco.1 +install-strip: install + ${STRIP} ${DESTDIR}${PREFIX}/bin/abduco + +install-completion: + @echo installing zsh completion file to ${DESTDIR}${SHAREDIR}/zsh/site-functions + @install -Dm644 contrib/abduco.zsh ${DESTDIR}${SHAREDIR}/zsh/site-functions/_abduco + uninstall: @echo removing executable file from ${DESTDIR}${PREFIX}/bin @rm -f ${DESTDIR}${PREFIX}/bin/abduco @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 @rm -f ${DESTDIR}${MANPREFIX}/man1/abduco.1 + @echo removing zsh completion file from ${DESTDIR}${SHAREDIR}/zsh/site-functions + @rm -f ${DESTDIR}${SHAREDIR}/zsh/site-functions/_abduco -.PHONY: all clean dist install uninstall debug +.PHONY: all clean dist install installdirs install-strip install-completion uninstall debug diff --git a/README.md b/README.md index e0250f9..fdcc92b 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ run in the background - and then later reattached. Together with [dvtm](http://www.brain-dump.org/projects/dvtm) it provides a simpler and cleaner alternative to tmux or screen. -![abduco+dvtm demo](https://raw.githubusercontent.com/martanne/abduco/gh-pages/screencast.gif) +![abduco+dvtm demo](https://raw.githubusercontent.com/martanne/abduco/gh-pages/screencast.gif#center) -abduco is in many ways very similar to [dtach]("http://dtach.sf.net) +abduco is in many ways very similar to [dtach](http://dtach.sf.net) but is a completely independent implementation which is actively maintained, contains no legacy code, provides a few additional features, has a cleaner, more robust implementation and is distributed under the @@ -34,23 +34,13 @@ cleaner, more robust implementation and is distributed under the ## Download -Either download the latest source tarball -[abduco-0.5.tar.gz](http://www.brain-dump.org/projects/abduco/abduco-0.5.tar.gz) -with sha1sum - - 37c51a0d5c3dd216251d84d5c1b550f119ad53c9 abduco-0.5.tar.gz - +Either download the latest [source tarball](https://github.com/martanne/abduco/releases), compile and install it ./configure && make && sudo make install -or use one of the distribution provided binary packages: - - * [Debian](https://packages.debian.org/search?keywords=abduco) - * [Fedora](https://admin.fedoraproject.org/pkgdb/package/abduco/) - * [Gentoo](http://packages.gentoo.org/package/app-misc/abduco/) - * [Ubuntu](http://packages.ubuntu.com/search?keywords=abduco) - * [Mac OS X](http://www.braumeister.org/formula/abduco) via homebrew +or use one of the distribution provided +[binary packages](https://repology.org/project/abduco/packages). ## Quickstart @@ -167,21 +157,14 @@ command line options. ## Development -You can always fetch the current code base from the git repository. +You can always fetch the current code base from the git repository +located at [Github](https://github.com/martanne/abduco/) or +[Sourcehut](https://git.sr.ht/~martanne/abduco). - git clone https://github.com/martanne/abduco.git - -or - - git clone git://repo.or.cz/abduco.git - -If you have comments, suggestions, ideas, a bug report, a patch or something -else related to abduco then write to the +If you have comments, suggestions, ideas, a bug report, a patch or +something else related to abduco then write to the [suckless developer mailing list](http://suckless.org/community) -or contact me directly mat[at]brain-dump.org. - -[![Build Status](https://travis-ci.org/martanne/abduco.svg?branch=master)](https://travis-ci.org/martanne/abduco) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/4285/badge.svg)](https://scan.coverity.com/projects/4285) +or contact me directly. ### Debugging @@ -194,8 +177,8 @@ to temporary files as follows: If you want to run client and server with one command (e.g. using the `-c` option) then within `gdb` the option `set follow-fork-mode {child,parent}` -might be useful. Similarly to get a syscall trace `strace -o abduco -ff [abduco-cmd]` -proved to be handy. +might be useful. Similarly to get a syscall trace `strace -o abduco -ff +[abduco-cmd]` proved to be handy. ## License diff --git a/abduco.1 b/abduco.1 index b4c4f4b..187643a 100644 --- a/abduco.1 +++ b/abduco.1 @@ -60,16 +60,17 @@ are relayed to the command supervised by the server. .Pp .Nm operates on the raw I/O byte stream without interpreting any terminal -escape sequences. As a consequence the terminal state is not preserved -across sessions. If this functionality is desired, it should be provided -by another utility such as +escape sequences. +As a consequence the terminal state is not preserved across sessions. +If this functionality is desired, it should be provided by another +utility such as .Xr dvtm 1 . . .Ss ACTIONS . If no command line arguments are given, all currently active sessions are -listed sorted by their respective creation date. Lines starting with an -asterisk +listed sorted by their respective creation date. +Lines starting with an asterisk .Pq * indicate that at least one client is currently connected. A plus sign @@ -101,7 +102,8 @@ Additionally the following options can be provided to further tweak the behavior. .Bl -tag -width indent .It Fl e Ar detachkey -Set the key to detach. Defaults to +Set the key to detach. +Defaults to .Aq Ctrl+\e which is specified as ^\\ i.e. Ctrl is represented as a caret .Pq ^ . @@ -111,7 +113,8 @@ after showing its exit status. .It Fl l Attach with the lowest priority, meaning this client will be the last to control the size. .It Fl p -Pass through content of standard input to the session. Implies the +Pass through content of standard input to the session. +Implies the .Fl q and .Fl l @@ -225,7 +228,7 @@ Or in a slightly more interactive fashion. .Xr tmux 1 , .Xr screen 1 . -.Sh AUTHOR +.Sh AUTHORS .Nm is written by .An Marc André Tanner Aq mat at brain-dump.org diff --git a/abduco.c b/abduco.c index 5bf4dde..f1f7b86 100644 --- a/abduco.c +++ b/abduco.c @@ -463,7 +463,7 @@ static bool create_session(const char *name, char * const argv[]) { sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); sigaction(SIGHUP, &sa, NULL); - if (chdir("/") == -1) /* should not happen, just to make the compiler happy */ + if (chdir("/") == -1) _exit(EXIT_FAILURE); #ifdef NDEBUG int fd = open("/dev/null", O_RDWR); @@ -501,8 +501,7 @@ static bool create_session(const char *name, char * const argv[]) { return false; default: /* parent = client process */ close(client_pipe[1]); - int status; - wait(&status); /* wait for first fork */ + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR); ssize_t len = read_all(client_pipe[0], errormsg, sizeof(errormsg)); if (len > 0) { write_all(STDERR_FILENO, errormsg, len); @@ -758,6 +757,7 @@ int main(int argc, char *argv[]) { die("create-session"); if (action == 'n') break; + /* fall through */ case 'a': if (!attach_session(server.session_name, true)) die("attach-session"); diff --git a/contrib/abduco.zsh b/contrib/abduco.zsh new file mode 100644 index 0000000..e8e94d1 --- /dev/null +++ b/contrib/abduco.zsh @@ -0,0 +1,35 @@ +#compdef abduco + +typeset -A opt_args + +_abduco_sessions() { + declare -a sessions + sessions=( $(abduco | sed '1d;s/.*\t[0-9][0-9]*\t//') ) + _describe -t session 'session' sessions +} + +_abduco_firstarg() { + if (( $+opt_args[-a] || $+opt_args[-A] )); then + _abduco_sessions + elif (( $+opt_args[-c] || $+opt_args[-n] )); then + _guard "^-*" 'session name' + elif [[ -z $words[CURRENT] ]]; then + compadd "$@" -S '' -- - + fi +} + +_arguments -s \ + '(-a -A -c -n -f)-a[attach to an existing session]' \ + '(-a -A -c -n)-A[attach to a session, create if does not exist]' \ + '(-a -A -c -n -l)-c[create a new session and attach to it]' \ + '(-a -A -c -n -l)-n[create a new session but do not attach to it]' \ + '-e[set the detachkey (default: ^\\)]:detachkey' \ + '(-a)-f[force create the session]' \ + '(-q)-p[pass-through mode]' \ + '-q[be quiet]' \ + '-r[read-only session, ignore user input]' \ + '(-c -n)-l[attach with the lowest priority]' \ + '(-)-v[show version information and exit]' \ + '1: :_abduco_firstarg' \ + '2:command:_path_commands' \ + '*:: :{ shift $((CURRENT-3)) words; _precommand; }' diff --git a/testsuite.sh b/testsuite.sh index ccb86fa..d6c73be 100755 --- a/testsuite.sh +++ b/testsuite.sh @@ -7,6 +7,9 @@ ABDUCO_OPTS="-e ^\\" [ ! -z "$1" ] && ABDUCO="$1" [ ! -x "$ABDUCO" ] && echo "usage: $0 /path/to/abduco" && exit 1 +TESTS_OK=0 +TESTS_RUN=0 + detach() { sleep 1 printf "" @@ -74,12 +77,14 @@ run_test_attached() { local output="$name.out" local output_expected="$name.expected" + TESTS_RUN=$((TESTS_RUN + 1)) echo -n "Running test attached: $name " expected_abduco_attached_output "$name" "$cmd" > "$output_expected" 2>&1 - $ABDUCO -c "$name" $cmd 2>&1 | sed 's/.$//' > "$output" - if diff -u "$output_expected" "$output" && check_environment; then + if $ABDUCO -c "$name" $cmd 2>&1 | sed 's/.$//' > "$output" && sleep 1 && + diff -u "$output_expected" "$output" && check_environment; then rm "$output" "$output_expected" + TESTS_OK=$((TESTS_OK + 1)) echo "OK" return 0 else @@ -97,6 +102,7 @@ run_test_detached() { local output="$name.out" local output_expected="$name.expected" + TESTS_RUN=$((TESTS_RUN + 1)) echo -n "Running test detached: $name " expected_abduco_detached_output "$name" "$cmd" > "$output_expected" 2>&1 @@ -104,6 +110,7 @@ run_test_detached() { $ABDUCO -a "$name" 2>&1 | sed 's/.$//' > "$output" && diff -u "$output_expected" "$output" && check_environment; then rm "$output" "$output_expected" + TESTS_OK=$((TESTS_OK + 1)) echo "OK" return 0 else @@ -121,6 +128,7 @@ run_test_attached_detached() { local output="$name.out" local output_expected="$name.expected" + TESTS_RUN=$((TESTS_RUN + 1)) echo -n "Running test: $name " $cmd >/dev/null 2>&1 expected_abduco_epilog "$name" $? > "$output_expected" 2>&1 @@ -129,6 +137,7 @@ run_test_attached_detached() { $ABDUCO -a "$name" 2>&1 | tail -1 | sed 's/.$//' > "$output" && diff -u "$output_expected" "$output" && check_environment; then rm "$output" "$output_expected" + TESTS_OK=$((TESTS_OK + 1)) echo "OK" return 0 else @@ -144,6 +153,7 @@ run_test_dvtm() { return 0; fi + TESTS_RUN=$((TESTS_RUN + 1)) local name="dvtm" local output="$name.out" local output_expected="$name.expected" @@ -152,6 +162,7 @@ run_test_dvtm() { if dvtm_session | $ABDUCO -c "$name" > "$output" 2>&1 && diff -u "$output_expected" "$output" && check_environment; then rm "$output" "$output_expected" + TESTS_OK=$((TESTS_OK + 1)) echo "OK" return 0 else @@ -162,8 +173,8 @@ run_test_dvtm() { test_non_existing_command || echo "Execution of non existing command FAILED" -run_test_attached "seq" "seq 1 1000" -run_test_detached "seq" "seq 1 1000" +run_test_attached "awk" "awk 'BEGIN {for(i=1;i<=1000;i++) print i}'" +run_test_detached "awk" "awk 'BEGIN {for(i=1;i<=1000;i++) print i}'" run_test_attached "false" "false" run_test_detached "false" "false" @@ -200,3 +211,5 @@ run_test_attached_detached "attach-detach" "./long-running.sh" rm ./long-running.sh run_test_dvtm + +[ $TESTS_OK -eq $TESTS_RUN ]