From: Daan De Meyer Date: Sun, 20 Aug 2023 09:42:51 +0000 (+0200) Subject: Add new setpgid utility X-Git-Tag: v2.40-rc1~258^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f092076fecc7ada2911df1b34f75091573ac08a8;p=thirdparty%2Futil-linux.git Add new setpgid utility This program allows running a command in a new process group and optionally makes the new process group the foreground process group of the ctty. This is useful when running programs through wrappers programs (think bubblewrap, ...) and wanting to make sure that SIGINT is only sent to the innermost process. This is possible by putting the innermost process in a new process group and making that process group the foreground process group of the controlling terminal. By adding a separate utility to util-linux, we can apply this to any program even if the program itself doesn't implement this functionality. --- diff --git a/bash-completion/Makemodule.am b/bash-completion/Makemodule.am index 2763895e89..c836d30b81 100644 --- a/bash-completion/Makemodule.am +++ b/bash-completion/Makemodule.am @@ -99,6 +99,9 @@ endif if BUILD_SCRIPTLIVE dist_bashcompletion_DATA += bash-completion/scriptlive endif +if BUILD_SETPGID +dist_bashcompletion_DATA += bash-completion/setpgid +endif if BUILD_SETSID dist_bashcompletion_DATA += bash-completion/setsid endif diff --git a/bash-completion/setpgid b/bash-completion/setpgid new file mode 100644 index 0000000000..5ad9be564f --- /dev/null +++ b/bash-completion/setpgid @@ -0,0 +1,23 @@ +_setpgid_module() +{ + local cur prev OPTS + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + case $cur in + -*) + OPTS="--foreground --help --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 + ;; + esac + compopt -o bashdefault + COMPREPLY=( $(compgen -c -- $cur) ) + return 0 +} +complete -F _setpgid_module setpgid diff --git a/configure.ac b/configure.ac index 738b369ee8..af58cb20e2 100644 --- a/configure.ac +++ b/configure.ac @@ -2077,6 +2077,9 @@ UL_REQUIRES_BUILD([rfkill], [libsmartcols]) AM_CONDITIONAL([BUILD_RFKILL], [test "x$build_rfkill" = xyes]) +UL_BUILD_INIT([setpgid], [yes]) +AM_CONDITIONAL([BUILD_SETPGID], [test "x$build_setpgid" = xyes]) + UL_BUILD_INIT([setsid], [yes]) AM_CONDITIONAL([BUILD_SETSID], [test "x$build_setsid" = xyes]) diff --git a/meson.build b/meson.build index 221ae373b6..cfb76aafff 100644 --- a/meson.build +++ b/meson.build @@ -1371,6 +1371,20 @@ if not is_disabler(exe) bashcompletions += ['renice'] endif +exe = executable( + 'setpgid', + setpgid_sources, + include_directories: includes, + link_with : [lib_common, + lib_smartcols], + install_dir : usrbin_exec_dir, + install : true) +if opt and not is_disabler(exe) + exes += exe + manadocs += ['sys-utils/setpgid.1.adoc'] + bashcompletions += ['setpgid'] +endif + exe = executable( 'setsid', setsid_sources, diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index 00f7b51ca3..4d2728c191 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -113,6 +113,13 @@ rfkill_LDADD = $(LDADD) libcommon.la libsmartcols.la rfkill_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) endif +if BUILD_SETPGID +usrbin_exec_PROGRAMS += setpgid +MANPAGES += sys-utils/setpgid.1 +dist_noinst_DATA += sys-utils/setpgid.1.adoc +setpgid_SOURCES = sys-utils/setpgid.c +endif + if BUILD_SETSID usrbin_exec_PROGRAMS += setsid MANPAGES += sys-utils/setsid.1 diff --git a/sys-utils/meson.build b/sys-utils/meson.build index ee00d1d39e..0e9857349e 100644 --- a/sys-utils/meson.build +++ b/sys-utils/meson.build @@ -36,6 +36,10 @@ renice_sources = files( 'renice.c', ) +setpgid_sources = files( + 'setpgid.c', +) + setsid_sources = files( 'setsid.c', ) diff --git a/sys-utils/setpgid.1.adoc b/sys-utils/setpgid.1.adoc new file mode 100644 index 0000000000..c0c53bf7bb --- /dev/null +++ b/sys-utils/setpgid.1.adoc @@ -0,0 +1,45 @@ +//po4a: entry man manual +// Daan De Meyer +// In the public domain. += setpgid(1) +:doctype: manpage +:man manual: User Commands +:man source: util-linux {release-version} +:page-layout: base +:command: setpgid + +== NAME + +setpgid - run a program in a new process group + +== SYNOPSIS + +*setpgid* [options] _program_ [_arguments_] + +== DESCRIPTION + +*setpgid* runs a program in a new process group. + +== OPTIONS + +*-f*, *--foreground*:: +Make the new process group the foreground process group of the controlling +terminal if there is a controlling terminal. + +include::man-common/help-version.adoc[] + +== AUTHORS + +mailto:daan.j.demeyer@gmail.com[Daan De Meyer] + +== SEE ALSO + +*setpgid*(2) + +include::man-common/bugreports.adoc[] + +include::man-common/footer.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/sys-utils/setpgid.c b/sys-utils/setpgid.c new file mode 100644 index 0000000000..d5806a49e8 --- /dev/null +++ b/sys-utils/setpgid.c @@ -0,0 +1,91 @@ +/* + * setpgid.c -- execute a command in a new process group + * Daan De Meyer + * In the public domain. + */ + +#include +#include +#include +#include +#include + +#include "closestream.h" + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, _( + " %s [options] [arguments ...]\n"), + program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Run a program in a new process group.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -f, --foregound Make a foreground process group\n"), out); + + printf(USAGE_HELP_OPTIONS(16)); + + printf(USAGE_MAN_TAIL("setpgid(1)")); + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int ch, foreground = 0, fd; + sigset_t s, old; + + static const struct option longopts[] = { + {"foreground", no_argument, NULL, 'f'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + close_stdout_atexit(); + + while ((ch = getopt_long(argc, argv, "+Vh", longopts, NULL)) != -1) + switch (ch) { + case 'f': + foreground = 1; + break; + case 'h': + usage(); + case 'V': + print_version(EXIT_SUCCESS); + default: + errtryhelp(EXIT_FAILURE); + } + + if (argc - optind < 1) { + warnx(_("no command specified")); + errtryhelp(EXIT_FAILURE); + } + + if (setpgid(0, 0) < 0) + err(EXIT_FAILURE, _("setpgid failed")); + + if (foreground) { + fd = open("/dev/tty", O_RDONLY|O_CLOEXEC); + if (fd >= 0) { + if (sigemptyset(&s) < 0) + err(EXIT_FAILURE, _("sigemptyset failed")); + if (sigaddset(&s, SIGTTOU) < 0) + err(EXIT_FAILURE, _("sigaddset failed")); + if (sigprocmask(SIG_BLOCK, &s, &old) < 0) + err(EXIT_FAILURE, _("sigprocmask failed")); + if (tcsetpgrp(fd, getpgid(0)) < 0) + err(EXIT_FAILURE, _("tcsetpgrp failed")); + if (sigprocmask(SIG_SETMASK, &old, NULL) < 0) + err(EXIT_FAILURE, _("sigprocmask failed")); + } + } + + execvp(argv[optind], argv + optind); + errexec(argv[optind]); +} diff --git a/tests/commands.sh b/tests/commands.sh index 41c0ee98ce..7dbe45a3e8 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -108,6 +108,7 @@ TS_CMD_SCRIPT=${TS_CMD_SCRIPT-"${ts_commandsdir}script"} TS_CMD_SCRIPTREPLAY=${TS_CMD_SCRIPTREPLAY-"${ts_commandsdir}scriptreplay"} TS_CMD_SCRIPTLIVE=${TS_CMD_SCRIPTLIVE-"${ts_commandsdir}scriptlive"} TS_CMD_SETARCH=${TS_CMD_SETARCH-"${ts_commandsdir}setarch"} +TS_CMD_SETPGID=${TS_CMD_SETPGID-"${ts_commandsdir}setpgid"} TS_CMD_SETSID=${TS_CMD_SETSID-"${ts_commandsdir}setsid"} TS_CMD_SWAPLABEL=${TS_CMD_SWAPLABEL:-"${ts_commandsdir}swaplabel"} TS_CMD_SWAPOFF=${TS_CMD_SWAPOFF:-"${ts_commandsdir}swapoff"} diff --git a/tests/expected/build-sys/config-all b/tests/expected/build-sys/config-all index 0574f23d1d..87e5f624f6 100644 --- a/tests/expected/build-sys/config-all +++ b/tests/expected/build-sys/config-all @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-all-devel b/tests/expected/build-sys/config-all-devel index 3e9a72072d..da9afc1bef 100644 --- a/tests/expected/build-sys/config-all-devel +++ b/tests/expected/build-sys/config-all-devel @@ -89,6 +89,7 @@ readprofile: renice: rtcwake: setarch: +setpgid: setsid: switch_root: tunelp: diff --git a/tests/expected/build-sys/config-all-non-nls b/tests/expected/build-sys/config-all-non-nls index 0574f23d1d..87e5f624f6 100644 --- a/tests/expected/build-sys/config-all-non-nls +++ b/tests/expected/build-sys/config-all-non-nls @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-audit b/tests/expected/build-sys/config-audit index bdcd0872df..6dab91fb01 100644 --- a/tests/expected/build-sys/config-audit +++ b/tests/expected/build-sys/config-audit @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-chfnsh-libuser b/tests/expected/build-sys/config-chfnsh-libuser index 97e1ae6056..61e4e62216 100644 --- a/tests/expected/build-sys/config-chfnsh-libuser +++ b/tests/expected/build-sys/config-chfnsh-libuser @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-chfnsh-no-password b/tests/expected/build-sys/config-chfnsh-no-password index 734113e3aa..441e6c56b6 100644 --- a/tests/expected/build-sys/config-chfnsh-no-password +++ b/tests/expected/build-sys/config-chfnsh-no-password @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-chfnsh-pam b/tests/expected/build-sys/config-chfnsh-pam index 532013c993..d247f8db29 100644 --- a/tests/expected/build-sys/config-chfnsh-pam +++ b/tests/expected/build-sys/config-chfnsh-pam @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-core b/tests/expected/build-sys/config-core index a20517cc26..132ee2418b 100644 --- a/tests/expected/build-sys/config-core +++ b/tests/expected/build-sys/config-core @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-cryptsetup b/tests/expected/build-sys/config-cryptsetup index c4aae45490..2a16c393f9 100644 --- a/tests/expected/build-sys/config-cryptsetup +++ b/tests/expected/build-sys/config-cryptsetup @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-devel b/tests/expected/build-sys/config-devel index 568241341e..c4c4b37223 100644 --- a/tests/expected/build-sys/config-devel +++ b/tests/expected/build-sys/config-devel @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-devel-new-mount b/tests/expected/build-sys/config-devel-new-mount index 9ac8272576..8befe4ccc1 100644 --- a/tests/expected/build-sys/config-devel-new-mount +++ b/tests/expected/build-sys/config-devel-new-mount @@ -94,6 +94,7 @@ readprofile: renice: rtcwake: setarch: +setpgid: setsid: switch_root: tunelp: diff --git a/tests/expected/build-sys/config-devel-non-asan b/tests/expected/build-sys/config-devel-non-asan index 568241341e..c4c4b37223 100644 --- a/tests/expected/build-sys/config-devel-non-asan +++ b/tests/expected/build-sys/config-devel-non-asan @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-devel-non-docs b/tests/expected/build-sys/config-devel-non-docs index 568241341e..c4c4b37223 100644 --- a/tests/expected/build-sys/config-devel-non-docs +++ b/tests/expected/build-sys/config-devel-non-docs @@ -110,6 +110,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-libblkid b/tests/expected/build-sys/config-non-libblkid index ed38eb5c56..191dd3a069 100644 --- a/tests/expected/build-sys/config-non-libblkid +++ b/tests/expected/build-sys/config-non-libblkid @@ -83,6 +83,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-libmount b/tests/expected/build-sys/config-non-libmount index 45ff711759..23931e3467 100644 --- a/tests/expected/build-sys/config-non-libmount +++ b/tests/expected/build-sys/config-non-libmount @@ -92,6 +92,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-libs b/tests/expected/build-sys/config-non-libs index b88a6a1833..17556267e7 100644 --- a/tests/expected/build-sys/config-non-libs +++ b/tests/expected/build-sys/config-non-libs @@ -60,6 +60,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-libsmartcols b/tests/expected/build-sys/config-non-libsmartcols index 28ff817b2b..f5cb24d7ef 100644 --- a/tests/expected/build-sys/config-non-libsmartcols +++ b/tests/expected/build-sys/config-non-libsmartcols @@ -76,6 +76,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-libuuid b/tests/expected/build-sys/config-non-libuuid index 3e5a295e4c..37626d88ee 100644 --- a/tests/expected/build-sys/config-non-libuuid +++ b/tests/expected/build-sys/config-non-libuuid @@ -98,6 +98,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-non-nls b/tests/expected/build-sys/config-non-nls index a20517cc26..132ee2418b 100644 --- a/tests/expected/build-sys/config-non-nls +++ b/tests/expected/build-sys/config-non-nls @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-selinux b/tests/expected/build-sys/config-selinux index 4d2e5f84b4..c3950b2401 100644 --- a/tests/expected/build-sys/config-selinux +++ b/tests/expected/build-sys/config-selinux @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-slang b/tests/expected/build-sys/config-slang index 5a3ac996c1..735f119b90 100644 --- a/tests/expected/build-sys/config-slang +++ b/tests/expected/build-sys/config-slang @@ -106,6 +106,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo su: libpam libpam_misc diff --git a/tests/expected/build-sys/config-static b/tests/expected/build-sys/config-static index 92de9d78f5..5ba0433b05 100644 --- a/tests/expected/build-sys/config-static +++ b/tests/expected/build-sys/config-static @@ -111,6 +111,7 @@ scriptlive: scriptreplay: setarch: setpriv: libcap-ng +setpgid: setsid: setterm: libtinfo sfdisk.static: STATIC diff --git a/tests/expected/misc/setpgid b/tests/expected/misc/setpgid new file mode 100644 index 0000000000..3e4e0554be --- /dev/null +++ b/tests/expected/misc/setpgid @@ -0,0 +1,2 @@ +success +not equal diff --git a/tests/ts/misc/setpgid b/tests/ts/misc/setpgid new file mode 100755 index 0000000000..40c475443d --- /dev/null +++ b/tests/ts/misc/setpgid @@ -0,0 +1,33 @@ +#!/bin/bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +TS_TOPDIR="${0%/*}/../.." +TS_DESC="setpgid" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" + +ts_check_test_command "$TS_CMD_SETPGID" + +$TS_CMD_SETPGID echo "success" >> $TS_OUTPUT 2>> $TS_ERRLOG + +# qemu-user always reports '0' for the pgid field which prevents the test from +# working so we skip it. +ts_skip_qemu_user + +PGID1="$(awk '{print $5}' /proc/self/stat)" +PGID2="$($TS_CMD_SETPGID awk '{print $5}' /proc/self/stat)" +test ! "$PGID1" = "$PGID2" && echo "not equal" >> $TS_OUTPUT + +ts_finalize