[patch] new applet nohup(1)

Bernhard Fischer rep.nop at aon.at
Wed Sep 7 19:55:28 UTC 2005


Hi,

I'll try to keep the volume served by this list down a bit..
Where should i put an imho stringently missing applet?

Currently, i did put it here:
http://members.aon.at/berny_f/busybox/busybox.applet-nohup.01.diff

because i as a submitter of a bug in the BTS cannot delete attachments
which are superseded by newer versions.
My hardquota on abovementioned uri is around 3MB so i'll delete it
if needed.

TIA for applying.

PS: maybe one should put the single free() into an if(ENABLE_CLEANUP)
clause. There may be more opportunities to reduce footprint, but i
haven't looked at replacing isatty by something more suitable for now.

PPS: $ size nohup.o*
   text    data     bss     dec     hex filename
    842       0       0     842     34a nohup.o
    869       0       0     869     365 nohup.o.oorig

-------------- next part --------------
diff -X excl -rduNp busybox.nohup.oorig/coreutils/Config.in busybox.nohup/coreutils/Config.in
--- busybox.nohup.oorig/coreutils/Config.in	2005-09-02 13:35:38.000000000 +0200
+++ busybox.nohup/coreutils/Config.in	2005-09-07 19:31:38.154461112 +0200
@@ -361,6 +361,12 @@ config CONFIG_NICE
 	help
 	  nice runs a program with modified scheduling priority.
 
+config CONFIG_NOHUP
+	bool "nohup"
+	default n
+	help
+	  run a command immune to hangups, with output to a non-tty.
+
 config CONFIG_OD
 	bool "od"
 	default n
diff -X excl -rduNp busybox.nohup.oorig/coreutils/Makefile.in busybox.nohup/coreutils/Makefile.in
--- busybox.nohup.oorig/coreutils/Makefile.in	2005-09-02 13:35:38.000000000 +0200
+++ busybox.nohup/coreutils/Makefile.in	2005-09-07 19:31:38.156460854 +0200
@@ -60,6 +60,7 @@ COREUTILS-$(CONFIG_MKFIFO)    += mkfifo.
 COREUTILS-$(CONFIG_MKNOD)     += mknod.o
 COREUTILS-$(CONFIG_MV)        += mv.o
 COREUTILS-$(CONFIG_NICE)      += nice.o
+COREUTILS-$(CONFIG_NOHUP)     += nohup.o
 COREUTILS-$(CONFIG_OD)        += od.o
 COREUTILS-$(CONFIG_PRINTENV)  += printenv.o
 COREUTILS-$(CONFIG_PRINTF)    += printf.o
diff -X excl -rduNp busybox.nohup.oorig/coreutils/nohup.c busybox.nohup/coreutils/nohup.c
--- busybox.nohup.oorig/coreutils/nohup.c	1970-01-01 01:00:00.000000000 +0100
+++ busybox.nohup/coreutils/nohup.c	2005-09-07 19:31:38.157460724 +0200
@@ -0,0 +1,200 @@
+/* vi: set sw=4 ts=4: */
+/* nohup -- run a command immune to hangups, with output to a non-tty
+   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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 this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Jim Meyering  */
+/* initial busybox port by Bernhard Fischer */
+
+#include <stdio_ext.h> /* __fpending */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "busybox.h"
+#define EXIT_CANNOT_INVOKE (126)
+#define NOHUP_FAILURE (127)
+#define EXIT_ENOENT NOHUP_FAILURE
+
+
+
+static inline int set_cloexec_flag (int desc)
+{
+#if defined F_GETFD && defined F_SETFD
+	int flags = fcntl (desc, F_GETFD, 0);
+	if (0 <= flags) {
+		int newflags = flags | FD_CLOEXEC;
+		if (flags == newflags || fcntl (desc, F_SETFD, newflags) != -1)
+			return 0;
+	}
+	return -1;
+#else
+	return 0;
+#endif
+}
+
+static int fd_reopen (int desired_fd, char const *file, int flags, mode_t mode)
+{
+	int fd;
+
+	close (desired_fd);
+	fd = open (file, flags, mode);
+	if (fd == desired_fd || fd < 0)
+		return fd;
+	/* else */
+	{
+		int fd2 = fcntl (fd, F_DUPFD, desired_fd);
+		int saved_errno = errno;
+		close (fd);
+		errno = saved_errno;
+		return fd2;
+	}
+}
+
+
+/* Close standard output, exiting with status 'exit_failure' on failure.
+   If a program writes *anything* to stdout, that program should close
+   stdout and make sure that it succeeds before exiting.  Otherwise,
+   suppose that you go to the extreme of checking the return status
+   of every function that does an explicit write to stdout.  The last
+   printf can succeed in writing to the internal stream buffer, and yet
+   the fclose(stdout) could still fail (due e.g., to a disk full error)
+   when it tries to write out that buffered data.  Thus, you would be
+   left with an incomplete output file and the offending program would
+   exit successfully.  Even calling fflush is not always sufficient,
+   since some file systems (NFS and CODA) buffer written/flushed data
+   until an actual close call.
+
+   Besides, it's wasteful to check the return value from every call
+   that writes to stdout -- just let the internal stream state record
+   the failure.  That's what the ferror test is checking below.
+
+   It's important to detect such failures and exit nonzero because many
+   tools (most notably `make' and other build-management systems) depend
+   on being able to detect failure in other tools via their exit status.  */
+
+static void close_stdout (void)
+{
+	short prev_fail = ferror (stdout);
+	short none_pending = (0 == __fpending (stdout));
+	short fclose_fail = fclose (stdout);
+
+	if (prev_fail || fclose_fail) {
+		int e = fclose_fail ? errno : 0;
+
+		/* If ferror returned zero, no data remains to be flushed, and we'd
+		otherwise fail with EBADF due to a failed fclose, then assume that
+		it's ok to ignore the fclose failure.  That can happen when a
+		program like cp is invoked like this `cp a b >&-' (i.e., with
+		stdout closed) and doesn't generate any output (hence no previous
+		error and nothing to be flushed).  */
+		if (e == EBADF && !prev_fail && none_pending)
+			return;
+
+		bb_perror_msg_and_die(bb_msg_write_error);
+	}
+}
+
+
+int nohup_main (int argc, char **argv)
+{
+	int saved_stderr_fd = STDERR_FILENO;
+
+	if (argc < 2)
+		bb_show_usage();
+
+	bb_default_error_retval = NOHUP_FAILURE;
+
+	atexit (close_stdout);
+
+	/* If standard input is a tty, replace it with /dev/null.
+	 Note that it is deliberately opened for *writing*,
+	 to ensure any read evokes an error.  */
+	if (isatty (STDIN_FILENO))
+		fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
+
+	/* If standard output is a tty, redirect it (appending) to a file.
+	 First try nohup.out, then $HOME/nohup.out.  */
+	if (isatty (STDOUT_FILENO)) {
+		char *in_home = NULL;
+		char const *file = "nohup.out";
+		int fd = fd_reopen (STDOUT_FILENO, file,
+				O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
+
+		if (fd < 0) {
+			if ((in_home = getenv ("HOME")) != NULL) {
+				in_home = concat_path_file(in_home, file);
+				fd = fd_reopen (STDOUT_FILENO, in_home,
+						O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);
+			}
+			if (fd < 0) {
+				bb_perror_msg("failed to open '%s'", file);
+				if (in_home)
+					bb_perror_msg("failed to open '%s'",in_home);
+				exit (NOHUP_FAILURE);
+			}
+			file = in_home;
+		}
+
+		umask (~(S_IRUSR | S_IWUSR));
+		bb_error_msg("appending output to '%s'", file);
+		free (in_home);
+	}
+
+	/* If standard error is a tty, redirect it to stdout.  */
+	if (isatty (STDERR_FILENO)) {
+	/* Save a copy of stderr before redirecting, so we can use the original
+	 if execve fails.  It's no big deal if this dup fails.  It might
+	 not change anything, and at worst, it'll lead to suppression of
+	 the post-failed-execve diagnostic.  */
+		saved_stderr_fd = dup (STDERR_FILENO);
+
+		if (0 <= saved_stderr_fd && set_cloexec_flag (saved_stderr_fd) != 0)
+			bb_perror_msg_and_die("failed to set the copy"
+					"of stderr to close on exec");
+
+		if (dup2 (STDOUT_FILENO, STDERR_FILENO) < 0) {
+			if (errno != EBADF)
+				bb_perror_msg_and_die("failed to redirect standard error");
+			close (STDERR_FILENO);
+		}
+	}
+
+	signal (SIGHUP, SIG_IGN);
+
+	{
+	int exit_status;
+	char **cmd = argv + 1;
+
+	execvp (*cmd, cmd);
+	exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
+
+	/* The execve failed.  Output a diagnostic to stderr only if:
+	   - stderr was initially redirected to a non-tty, or
+	   - stderr was initially directed to a tty, and we
+	   can dup2 it to point back to that same tty.
+	   In other words, output the diagnostic if possible, but only if
+	   it will go to the original stderr.  */
+	if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
+		bb_perror_msg("cannot run command '%s'",*cmd);
+
+	exit (exit_status);
+	}
+}
+
diff -X excl -rduNp busybox.nohup.oorig/include/applets.h busybox.nohup/include/applets.h
--- busybox.nohup.oorig/include/applets.h	2005-09-02 13:35:45.000000000 +0200
+++ busybox.nohup/include/applets.h	2005-09-07 19:31:38.159460466 +0200
@@ -459,6 +459,9 @@
 #ifdef CONFIG_NICE
 	APPLET(nice, nice_main, _BB_DIR_BIN, _BB_SUID_NEVER)
 #endif
+#ifdef CONFIG_NOHUP
+	APPLET(nohup, nohup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+#endif
 #ifdef CONFIG_NSLOOKUP
 	APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
 #endif
diff -X excl -rduNp busybox.nohup.oorig/include/usage.h busybox.nohup/include/usage.h
--- busybox.nohup.oorig/include/usage.h	2005-09-07 09:30:42.000000000 +0200
+++ busybox.nohup/include/usage.h	2005-09-07 19:31:38.161460207 +0200
@@ -2019,6 +2019,13 @@
 	"Options:\n" \
 	"\t-n ADJUST\tAdjust the scheduling priority by ADJUST"
 
+#define nohup_trivial_usage \
+	"COMMAND [ARGS]"
+#define nohup_full_usage \
+	"run a command immune to hangups, with output to a non-tty"
+#define nohup_example_usage \
+	"$ nohup make &"
+
 #define nslookup_trivial_usage \
 	"[HOST] [SERVER]"
 #define nslookup_full_usage \


More information about the busybox mailing list