libbb: rework NOMMU helper API so that it makes more sense and easier to use. Doesn't compile - need two more commits.
No binary (build failed)
—
(log yok)
| @@ -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); | |
| @@ -18,11 +18,68 @@ | |
| #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); |
| + | } |
| @@ -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) | |
| { | |