diff --git a/Makefile.am b/Makefile.am index f0f4350..964dedf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,7 @@ # along with termux-tools. If not, see # . -SUBDIRS = . scripts doc mirrors motds +SUBDIRS = . scripts doc mirrors motds src CONFFILES = \ etc/motd \ diff --git a/configure.ac b/configure.ac index c50690d..1770d84 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,8 @@ AC_INIT([termux-tools], [1.43.3], [support@termux.dev]) AM_INIT_AUTOMAKE([foreign]) AC_PROG_MAKE_SET +AC_PROG_CC +AC_LANG(C) copyright="Copyright (C) 2022-2024 Termux." if test "${TERMUX_APP_PACKAGE+set}" = set; then @@ -93,6 +95,6 @@ AC_SUBST(termux_package_manager) AC_PROG_LN_S AC_CONFIG_FILES([Makefile scripts/Makefile doc/Makefile -mirrors/Makefile motds/Makefile]) +mirrors/Makefile motds/Makefile src/Makefile]) AC_OUTPUT diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 9dca981..34d0fde 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -24,7 +24,7 @@ termux-setup-package-manager termux-setup-storage termux-wake-lock \ termux-wake-unlock # wrappers around tools in /system/bin: -bin_SCRIPTS += cmd df getprop logcat ping ping6 pm settings top +bin_SCRIPTS += df getprop logcat ping ping6 pm settings top CLEANFILES = $(bin_SCRIPTS) @@ -90,4 +90,3 @@ $(eval $(call wrapper-rule,pm)) $(eval $(call wrapper-rule,settings)) $(eval $(call wrapper-rule,top)) $(eval $(call wrapper-rule,umount)) -$(eval $(call wrapper-rule,cmd)) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..d2b3d76 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,23 @@ +# Copyright (C) 2024 Termux + +# This file is part of termux-tools. + +# termux-tools 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 3 of the License, or +# (at your option) any later version. + +# termux-tools 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. + +# You should have received a copy of the GNU General Public License +# along with termux-tools. If not, see +# . + +AM_CFLAGS = -Wall -Wextra -pedantic + +bin_PROGRAMS = cmd + +cmd_SOURCES = cmd.c diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 0000000..c2c6af5 --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,112 @@ +/* cmd.c +Copyright (C) 2024 5ec1cff +This file is part of termux-tools. +termux-tools 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 3 of the License, or (at +your option) any later version. +termux-tools 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. +You should have received a copy of the GNU General Public License +along with termux-tools. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void pump(int in_fd, int out_fd) { + char buf[4096]; + ssize_t sz, t; + for (;;) { + sz = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf))); + if (sz <= 0) return; + while (sz) { + t = TEMP_FAILURE_RETRY(write(out_fd, buf, sz)); + if (t <= 0) return; + sz -= t; + } + } +} + +int p_std_in[2], p_std_out[2], p_std_err[2]; + +void *pump_stdin(void *ignore) { + pump(STDIN_FILENO, p_std_in[1]); + close(p_std_in[1]); + return NULL; +} + +void *pump_stdout(void *ignore) { + pump(p_std_out[0], STDOUT_FILENO); + close(p_std_out[0]); + return NULL; +} + +void *pump_stderr(void *ignore) { + pump(p_std_err[0], STDERR_FILENO); + close(p_std_err[0]); + return NULL; +} + +void replace_fd(int fd, int target_fd) { + if (dup2(fd, target_fd) == -1) err(EXIT_FAILURE, "dup"); + close(fd); + if (fcntl(target_fd, F_SETFD, fcntl(target_fd, F_GETFD) & ~FD_CLOEXEC) == -1) + err(EXIT_FAILURE, "replace_fd"); +} + +int main(int argc, char **argv) { + if (pipe(p_std_in) == -1) err(EXIT_FAILURE, "pipe"); + if (pipe(p_std_out) == -1) err(EXIT_FAILURE, "pipe"); + if (pipe(p_std_err) == -1) err(EXIT_FAILURE, "pipe"); + + pid_t pid = fork(); + + if (pid < 0) { + err(EXIT_FAILURE, "fork"); + } else if (pid > 0) { + close(p_std_in[0]); + close(p_std_out[1]); + close(p_std_err[1]); + + signal(SIGPIPE, SIG_IGN); + + pthread_t t_stdin; + pthread_create(&t_stdin, NULL, pump_stdin, NULL); + pthread_detach(t_stdin); + + pthread_t t_stdout; + pthread_create(&t_stdout, NULL, pump_stdout, NULL); + + pthread_t t_stderr; + pthread_create(&t_stderr, NULL, pump_stderr, NULL); + + int status; + if (TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)) < 0) err(EXIT_FAILURE, "wait"); + + pthread_join(t_stdout, NULL); + pthread_join(t_stderr, NULL); + + if (WIFEXITED(status)) + exit(WEXITSTATUS(status)); + else + exit(EXIT_FAILURE); + } else { + replace_fd(p_std_in[0], STDIN_FILENO); + replace_fd(p_std_out[1], STDOUT_FILENO); + replace_fd(p_std_err[1], STDERR_FILENO); + + execv("/system/bin/cmd", argv); + err(EXIT_FAILURE, "exec"); + } +}