libbb: rework NOMMU helper API so that it makes more sense

and easier to use. Doesn't compile - need two more commits.
This commit is contained in:
Denis Vlasenko 2007-03-26 13:20:04 +00:00
parent f62c6fa1ca
commit bb7fcb4229
3 changed files with 191 additions and 107 deletions

View 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);

View File

@ -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);
}

View 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)
{