libbb: rework NOMMU helper API so that it makes more sense
authorDenis Vlasenko <vda.linux@googlemail.com>
Mon, 26 Mar 2007 13:20:04 +0000 (13:20 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Mon, 26 Mar 2007 13:20:04 +0000 (13:20 -0000)
and easier to use. Doesn't compile - need two more commits.

include/libbb.h
libbb/vfork_daemon_rexec.c
libbb/xfuncs.c

index aba9316d12bfad4a9c0e7b4ec8cde837da981080..ff7d3bf1a9deedc013f5deba6398a400162a0a48 100644 (file)
@@ -263,17 +263,9 @@ char *xrealloc_getcwd_or_warn(char *cwd);
 char *xmalloc_readlink_or_warn(const char *path);
 char *xmalloc_realpath(const char *path);
 extern void xstat(const char *filename, struct stat *buf);
-extern pid_t spawn(char **argv);
-extern pid_t xspawn(char **argv);
 extern int wait4pid(int pid);
 extern void xsetgid(gid_t gid);
 extern void xsetuid(uid_t uid);
-extern void xdaemon(int nochdir, int noclose);
-/* More clever/thorough xdaemon */
-extern void bb_sanitize_stdio_maybe_daemonize(int daemonize);
-extern void bb_sanitize_stdio(void);
-/* NB: be careful: dont open syslog/network sockets before bb_daemonize */
-extern void bb_daemonize(void);
 extern void xchdir(const char *path);
 extern void xsetenv(const char *key, const char *value);
 extern int xopen(const char *pathname, int flags);
@@ -460,6 +452,62 @@ void clear_username_cache(void);
 enum { USERNAME_MAX_SIZE = 16 - sizeof(int) };
 
 
+int execable_file(const char *name);
+char *find_execable(const char *filename);
+int exists_execable(const char *filename);
+
+#if ENABLE_FEATURE_EXEC_PREFER_APPLETS
+int bb_execvp(const char *file, char *const argv[]);
+#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
+#define BB_EXECLP(prog,cmd,...) \
+       execlp((find_applet_by_name(prog)) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
+               cmd, __VA_ARGS__)
+#else
+#define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
+#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
+#endif
+
+/* NOMMU friendy fork+exec */
+pid_t spawn(char **argv);
+pid_t xspawn(char **argv);
+/* Helpers for daemonization.
+ * bb_daemonize(flags) = daemonize, does not compile on NOMMU
+ * bb_daemonize_or_rexec(flags, argv) = daemonizes on MMU (and ignores argv),
+ *      rexec's itself on NOMMU with argv passed as command line.
+ * Thus bb_daemonize_or_rexec may cause your <applet>_main() to be re-executed
+ * from the start. (It will detect it and not reexec again second time).
+ * You have to audit carefully that you don't do something twice as a result
+ * (opening files/sockets, parsing config files etc...)!
+ *
+ * Both of the above will redirect fd 0,1,2 to /dev/null and drop ctty
+ * (will do setsid()).
+ *
+ * Helper for network daemons in foreground mode:
+ * bb_sanitize_stdio() = make sure that fd 0,1,2 are opened by opening them
+ * to /dev/null if they are not.
+ */
+enum {
+       DAEMON_CHDIR_ROOT = 1,
+       DAEMON_DEVNULL_STDIO = /* 2 */ 0, /* no users so far */
+       DAEMON_CLOSE_EXTRA_FDS = 4,
+       DAEMON_ONLY_SANITIZE = 8, /* internal use */
+};
+#ifndef BB_NOMMU
+#define bb_daemonize_or_rexec(flags, argv) bb_daemonize_or_rexec(flags)
+#define bb_daemonize(flags)                bb_daemonize_or_rexec(flags, bogus)
+#else
+extern smallint re_execed;
+pid_t BUG_fork_is_unavailable_on_nommu(void);
+pid_t BUG_daemon_is_unavailable_on_nommu(void);
+pid_t BUG_bb_daemonize_is_unavailable_on_nommu(void);
+#define fork()          BUG_fork_is_unavailable_on_nommu()
+#define daemon(a,b)     BUG_daemon_is_unavailable_on_nommu()
+#define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu()
+#endif
+void bb_daemonize_or_rexec(int flags, char **argv);
+void bb_sanitize_stdio(void);
+
+
 enum { BB_GETOPT_ERROR = 0x80000000 };
 extern const char *opt_complementary;
 #if ENABLE_GETOPT_LONG
@@ -569,20 +617,6 @@ char *concat_path_file(const char *path, const char *filename);
 char *concat_subpath_file(const char *path, const char *filename);
 char *last_char_is(const char *s, int c);
 
-int execable_file(const char *name);
-char *find_execable(const char *filename);
-int exists_execable(const char *filename);
-
-#if ENABLE_FEATURE_EXEC_PREFER_APPLETS
-int bb_execvp(const char *file, char *const argv[]);
-#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
-#define BB_EXECLP(prog,cmd,...) \
-       execlp((find_applet_by_name(prog)) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
-               cmd, __VA_ARGS__)
-#else
-#define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
-#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
-#endif
 
 USE_DESKTOP(long long) int uncompress(int fd_in, int fd_out);
 int inflate(int in, int out);
@@ -617,12 +651,8 @@ extern int index_in_str_array(const char * const string_array[], const char *key
 extern int index_in_substr_array(const char * const string_array[], const char *key);
 extern void print_login_issue(const char *issue_file, const char *tty);
 extern void print_login_prompt(void);
-#ifdef BB_NOMMU
-extern pid_t BUG_fork_is_unavailable_on_nommu(void);
-#define fork() BUG_fork_is_unavailable_on_nommu()
-extern void vfork_daemon_rexec(int nochdir, int noclose, char **argv);
-extern smallint re_execed;
-#endif
+
+
 extern int get_terminal_width_height(const int fd, int *width, int *height);
 
 char *is_in_ino_dev_hashtable(const struct stat *statbuf);
index 3185f2d396d9dc5f14d6fd06606b9ea724ba2f3d..c59b0b6fdb458fcc1e2d304918363396f4418239 100644 (file)
 #include <paths.h>
 #include "libbb.h"
 
-#ifdef BB_NOMMU
+/* This does a fork/exec in one call, using vfork().  Returns PID of new child,
+ * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
+pid_t spawn(char **argv)
+{
+       /* Compiler should not optimize stores here */
+       volatile int failed;
+       pid_t pid;
+
+       // Be nice to nommu machines.
+       failed = 0;
+       pid = vfork();
+       if (pid < 0) /* error */
+               return pid;
+       if (!pid) { /* child */
+               /* Don't use BB_EXECVP tricks here! */
+               execvp(argv[0], argv);
+
+               /* We are (maybe) sharing a stack with blocked parent,
+                * let parent know we failed and then exit to unblock parent
+                * (but don't run atexit() stuff, which would screw up parent.)
+                */
+               failed = errno;
+               _exit(0);
+       }
+       /* parent */
+       /* Unfortunately, this is not reliable: vfork()
+        * can be equivalent to fork() according to standards */
+       if (failed) {
+               errno = failed;
+               return -1;
+       }
+       return pid;
+}
+
+/* Die with an error message if we can't spawn a child process. */
+pid_t xspawn(char **argv)
+{
+       pid_t pid = spawn(argv);
+       if (pid < 0) bb_perror_msg_and_die("%s", *argv);
+       return pid;
+}
+
+
+
+#if 0 //ndef BB_NOMMU
+// Die with an error message if we can't daemonize.
+void xdaemon(int nochdir, int noclose)
+{
+       if (daemon(nochdir, noclose))
+               bb_perror_msg_and_die("daemon");
+}
+#endif
+
+#if 0 // def BB_NOMMU
 void vfork_daemon_rexec(int nochdir, int noclose, char **argv)
 {
        int fd;
 
+       /* Maybe we are already re-execed and come here again? */
+       if (re_execed)
+               return;
+
        setsid();
 
        if (!nochdir)
@@ -56,3 +113,78 @@ void vfork_daemon_rexec(int nochdir, int noclose, char **argv)
        }
 }
 #endif /* BB_NOMMU */
+
+#ifdef BB_NOMMU
+static void daemon_or_rexec(char **argv)
+{
+       pid_t pid;
+       /* Maybe we are already re-execed and come here again? */
+       if (re_execed)
+               return;
+
+       pid = vfork();
+       if (pid < 0) /* wtf? */
+               bb_perror_msg_and_die("vfork");
+       if (pid) /* parent */
+               exit(0);
+       /* child - re-exec ourself */
+       /* high-order bit of first char in argv[0] is a hidden
+        * "we have (alrealy) re-execed, don't do it again" flag */
+       argv[0][0] |= 0x80;
+       execv(CONFIG_BUSYBOX_EXEC_PATH, argv);
+       bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH);
+}
+#else
+static void daemon_or_rexec(void)
+{
+       pid_t pid;
+       pid = fork();
+       if (pid < 0) /* wtf? */
+               bb_perror_msg_and_die("fork");
+       if (pid) /* parent */
+               exit(0);
+       /* child */
+}
+#define daemon_or_rexec(argv) daemon_or_rexec()
+#endif
+
+
+/* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
+ * char **argv "vanishes" */
+void bb_daemonize_or_rexec(int flags, char **argv)
+{
+       int fd;
+
+       fd = xopen(bb_dev_null, O_RDWR);
+
+       if (flags & DAEMON_CHDIR_ROOT)
+               xchdir("/");
+
+       if (flags & DAEMON_DEVNULL_STDIO) {
+               close(0);
+               close(1);
+               close(2);
+       }
+
+       while ((unsigned)fd < 2)
+               fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+
+       if (!(flags & DAEMON_ONLY_SANITIZE)) {
+               daemon_or_rexec(argv);
+               /* if daemonizing, make sure we detach from stdio */
+               setsid();
+               dup2(fd, 0);
+               dup2(fd, 1);
+               dup2(fd, 2);
+       }
+       if (fd > 2)
+               close(fd--);
+       if (flags & DAEMON_CLOSE_EXTRA_FDS)
+               while (fd > 2)
+                       close(fd--); /* close everything after fd#2 */
+}
+
+void bb_sanitize_stdio(void)
+{
+       bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
+}
index 1dcdbc065d54f9f870547e230a9d3dc90da684ed..14bd62a152d6a9b877dc034b649c6fee54184125 100644 (file)
@@ -187,43 +187,6 @@ void xfflush_stdout(void)
        }
 }
 
-// This does a fork/exec in one call, using vfork().  Return PID of new child,
-// -1 for failure.  Runs argv[0], searching path if that has no / in it.
-pid_t spawn(char **argv)
-{
-       /* Why static? */
-       static int failed;
-       pid_t pid;
-
-       // Be nice to nommu machines.
-       failed = 0;
-       pid = vfork();
-       if (pid < 0) return pid;
-       if (!pid) {
-               BB_EXECVP(argv[0], argv);
-
-               // We're sharing a stack with blocked parent, let parent know we failed
-               // and then exit to unblock parent (but don't run atexit() stuff, which
-               // would screw up parent.)
-
-               failed = errno;
-               _exit(0);
-       }
-       if (failed) {
-               errno = failed;
-               return -1;
-       }
-       return pid;
-}
-
-// Die with an error message if we can't spawn a child process.
-pid_t xspawn(char **argv)
-{
-       pid_t pid = spawn(argv);
-       if (pid < 0) bb_perror_msg_and_die("%s", *argv);
-       return pid;
-}
-
 // Wait for the specified child PID to exit, returning child's error return.
 int wait4pid(int pid)
 {
@@ -510,47 +473,6 @@ DIR *xopendir(const char *path)
        return dp;
 }
 
-#ifndef BB_NOMMU
-// Die with an error message if we can't daemonize.
-void xdaemon(int nochdir, int noclose)
-{
-       if (daemon(nochdir, noclose))
-               bb_perror_msg_and_die("daemon");
-}
-#endif
-
-void bb_sanitize_stdio_maybe_daemonize(int daemonize)
-{
-       int fd;
-       /* Mega-paranoid */
-       fd = xopen(bb_dev_null, O_RDWR);
-       while ((unsigned)fd < 2)
-               fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
-       if (daemonize) {
-               pid_t pid = fork();
-               if (pid < 0) /* wtf? */
-                       bb_perror_msg_and_die("fork");
-               if (pid) /* parent */
-                       exit(0);
-               /* child */
-               /* if daemonizing, make sure we detach from stdio */
-               setsid();
-               dup2(fd, 0);
-               dup2(fd, 1);
-               dup2(fd, 2);
-       }
-       while (fd > 2)
-               close(fd--); /* close everything after fd#2 */
-}
-void bb_sanitize_stdio(void)
-{
-       bb_sanitize_stdio_maybe_daemonize(0);
-}
-void bb_daemonize(void)
-{
-       bb_sanitize_stdio_maybe_daemonize(1);
-}
-
 // Die with an error message if we can't open a new socket.
 int xsocket(int domain, int type, int protocol)
 {