From bb7fcb4229fd5ff583039f26ca1c06340e3f09ea Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Mon, 26 Mar 2007 13:20:04 +0000 Subject: [PATCH] libbb: rework NOMMU helper API so that it makes more sense and easier to use. Doesn't compile - need two more commits. --- include/libbb.h | 86 ++++++++++++++++-------- libbb/vfork_daemon_rexec.c | 134 ++++++++++++++++++++++++++++++++++++- libbb/xfuncs.c | 78 --------------------- 3 files changed, 191 insertions(+), 107 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index aba9316d1..ff7d3bf1a 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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 _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); diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 3185f2d39..c59b0b6fd 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -18,11 +18,68 @@ #include #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); +} diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index 1dcdbc065..14bd62a15 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -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) { -- 2.25.1