On Fri, Mar 13, 2015 at 08:23:49AM +0100, Natanael Copa wrote:
> On Thu, 12 Mar 2015 22:38:10 +0100
> Harald Becker <ra...@gmx.de> wrote:
<snip>
> > So my question to this:
> > 
> > What is the sense of this?
> > What do you want to express with this?
> 
> Show with an example the idea a possible way to solve the hotplug
> forkbomb problem with a minimal long lived netlink listener + short
> lived event handler.
> 
> Instead of just using words to tell how to make a, express it with
> example code.
> 
> > What was your intention to do that code hacking?
> 
> To show that a long-lived netlink daemon can be very small and simple -
> probably much smaller than a fifo manager would be.
> 
> If someone have a better idea how to solve it, I would love to hear it.
> (No, I don't fifos will make it smaller/simpler)

Well, this evening I wrote a knockoff of the short-lived fifo hotplugger
I mentioned (sort of a cross between the netlink hotplugger Nataneal
Copa mentioned as probably being impossible to do right and the FIFO daemon
and hotplugger that Harald Becker was mentioning).

It takes about 120 lines, total.
Most of that is probably the attempts to retry.

Having never written anything with signal, poll, atexit, or fifos before,
I probably left quite a few holes in it.

Basic concept is that it creates a fifo, and treats the success of mkfifo
as a lock to determine whether it's the daemon or a writer.
If it's the daemon, it will 
fork; 
in the child:
        exec the parser with the fifo as stdin
in the parent:
        set a SIGCHLD handler that respawns the parser if the fifo is not empty,
        open the fifo to write,
        dump the environment into the fifo
        sleep

If it isn't the daemon, it dumps the environment into the fifo and re-runs
itself if it gets a SIGPIPE (so that if you dump the environment after the
parser dies, it will retry).

If you set up a fifo supervisor separately, this will let it manage the fifo.

While I don't think this approach should replace mdev, or that a long-running
fifo supervisor is a *good* solution for hotplug, it would certainly be 
possible to do what Harald proposes - likely in 200-300 LOC.

Thanks,
Isaac Dunham
/* shp - stupid hotplugger 
 *
 * This is a "hotplugger" that (a) opens a fifo,
 * spawns a helper, and dumps the environment to the fifo;
 * or (b) dumps the environment to the fifo.
 *
 * The safety (such as it is) of most of the code relies on blocking I/O.
 * If you make things non-blocking, this will break in many ways.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>

extern char **environ;

#define HPPATH "/tmp/hotplug"
#define HPPARSE "/bin/cat"

int fifro = -1, fifwr = -1;

void spawnparse(void)
{
	if (fifro < 0)
		fifro = open(HPPATH, O_RDONLY);

	if (fifro < 0) {
		exit(1);
	} else if (fifro) {
		if (fifwr == 0)
			close(fifwr);
		fifro = dup2(fifro, 0);
	}

	execl(HPPARSE, HPPARSE, (char *)0);
}

void cleanup (void)
{
	unlink(HPPATH);
}

void redo(int gotsig)
{	
	main(0, (char **)0, environ);
	exit(0);
}

void exit_or_respawn(int gotsig)
{
	/* poll fifwr, if empty exit */
	struct pollfd fds;
	pid_t pid;

	if (fifro < 0)
		fifro = open(HPPATH, O_RDONLY);
	fds.fd = fifro;
	fds.events = POLLIN;
	fds.revents = 0;
	poll(&fds, 1, 5000);

	if (fds.revents|POLLIN) {
		pid = fork();
		/* Yes, this is intentional. 
		 * If we can't fork, better to exec() the helper than let
		 * hotplug events get lost. 
		 */
		if (pid < 1) {
			spawnparse();
			exit(5);
		}	
	} else {
		exit(0);
	}
}

int dumpenv(char **env)
{
	signal(SIGPIPE, redo);
	flock(fifwr, LOCK_EX);
	while (env && *env) {
		if (env[0][0]) { 
			write(fifwr, *env, strlen(*env));
			write(fifwr, "\n", 1);
		}
		env++;
	}
	write(fifwr, "", 1);
	flock(fifwr, LOCK_UN);
}

int main(int argc, char **argv, char **env)
{
	int fifohandle;

	if (mkfifo(HPPATH, 0600)) {
		if (errno == EEXIST) {
			fifwr = open(HPPATH, O_WRONLY|O_CLOEXEC);
			dumpenv(env);
		}
	} else {
		// we running the show, man
		pid_t pid = fork();
		if (pid < 0) exit(2);
		
		if (!pid) {
			// child process: the parser
			spawnparse();
		} else {
			// original process, supervising everything
			atexit(cleanup);
			fifwr = open(HPPATH, O_WRONLY|O_CLOEXEC);
			signal(SIGCHLD, exit_or_respawn);
			dumpenv(env);
			sleep(5);
		}
	}
}
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to