Exemple #1
0
/* inlining + vfork -> bigger code */
static NOINLINE pid_t runsv(const char *name)
{
	pid_t pid;

	/* If we got signaled, stop spawning children at once! */
	if (bb_got_signal)
		return 0;

	pid = vfork();
	if (pid == -1) {
		warn2_cannot("vfork", "");
		return 0;
	}
	if (pid == 0) {
		/* child */
		if (option_mask32 & 1) /* -P option? */
			setsid();
/* man execv:
 * "Signals set to be caught by the calling process image
 *  shall be set to the default action in the new process image."
 * Therefore, we do not need this: */
#if 0
		bb_signals(0
			| (1 << SIGHUP)
			| (1 << SIGTERM)
			, SIG_DFL);
#endif
		execlp("runsv", "runsv", name, (char *) NULL);
		fatal2_cannot("start runsv ", name);
	}
	return pid;
}
Exemple #2
0
void FAST_FUNC launch_helper(const char **argv)
{
	// setup vanilla unidirectional pipes interchange
	int i;
	int pipes[4];

	xpipe(pipes);
	xpipe(pipes + 2);

	// NB: handler must be installed before vfork
	bb_signals(0
		+ (1 << SIGCHLD)
		+ (1 << SIGALRM)
		, signal_handler);

	G.helper_pid = xvfork();

	i = (!G.helper_pid) * 2; // for parent:0, for child:2
	close(pipes[i + 1]); // 1 or 3 - closing one write end
	close(pipes[2 - i]); // 2 or 0 - closing one read end
	xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end
	xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - other write end

	if (!G.helper_pid) {
		// child: try to execute connection helper
		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
		BB_EXECVP_or_die((char**)argv);
	}

	// parent
	// check whether child is alive
	//redundant:signal_handler(SIGCHLD);
	// child seems OK -> parent goes on
	atexit(kill_helper);
}
Exemple #3
0
int less_main(int argc, char **argv)
{
    int keypress;

    INIT_G();

    /* TODO: -x: do not interpret backspace, -xx: tab also */
    /* -xxx: newline also */
    /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
    getopt32(argv, "EMmN~");
    argc -= optind;
    argv += optind;
    num_files = argc;
    files = argv;

    /* Another popular pager, most, detects when stdout
     * is not a tty and turns into cat. This makes sense. */
    if (!isatty(STDOUT_FILENO))
        return bb_cat(argv);
    kbd_fd = open(CURRENT_TTY, O_RDONLY);
    if (kbd_fd < 0)
        return bb_cat(argv);
    ndelay_on(kbd_fd);

    if (!num_files) {
        if (isatty(STDIN_FILENO)) {
            /* Just "less"? No args and no redirection? */
            bb_error_msg("missing filename");
            bb_show_usage();
        }
    } else
        filename = xstrdup(files[0]);

    get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
    /* 20: two tabstops + 4 */
    if (width < 20 || max_displayed_line < 3)
        return bb_cat(argv);
    max_displayed_line -= 2;

    buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
    if (option_mask32 & FLAG_TILDE)
        empty_line_marker = "";

    tcgetattr(kbd_fd, &term_orig);
    term_less = term_orig;
    term_less.c_lflag &= ~(ICANON | ECHO);
    term_less.c_iflag &= ~(IXON | ICRNL);
    /*term_less.c_oflag &= ~ONLCR;*/
    term_less.c_cc[VMIN] = 1;
    term_less.c_cc[VTIME] = 0;

    /* We want to restore term_orig on exit */
    bb_signals(BB_FATAL_SIGS, sig_catcher);

    reinitialize();
    while (1) {
        keypress = less_getch(-1); /* -1: do not position cursor */
        keypress_process(keypress);
    }
}
/* Call this before doing anything else. Sets up the socket pair
 * and installs the signal handler */
void FAST_FUNC udhcp_sp_setup(void)
{
	/* was socketpair, but it needs AF_UNIX in kernel */
	xpiped_pair(signal_pipe);
	close_on_exec_on(signal_pipe.rd);
	close_on_exec_on(signal_pipe.wr);
	ndelay_on(signal_pipe.wr);
	bb_signals(0
		+ (1 << SIGUSR1)
		+ (1 << SIGUSR2)
		+ (1 << SIGTERM)
		, signal_handler);
}
static void startservice(struct svdir *s)
{
	int p;
	char *run[2];

	if (s->state == S_FINISH)
		run[0] = (char*)"./finish";
	else {
		run[0] = (char*)"./run";
		custom(s, 'u');
	}
	run[1] = NULL;

	if (s->pid != 0)
		stopservice(s); /* should never happen */
	while ((p = vfork()) == -1) {
		warn_cannot("vfork, sleeping");
		sleep(5);
	}
	if (p == 0) {
		/* child */
		if (haslog) {
			/* NB: bug alert! right order is close, then dup2 */
			if (s->islog) {
				xchdir("./log");
				close(logpipe.wr);
				xdup2(logpipe.rd, 0);
			} else {
				close(logpipe.rd);
				xdup2(logpipe.wr, 1);
			}
		}
		bb_signals(0
			+ (1 << SIGCHLD)
			+ (1 << SIGTERM)
			, SIG_DFL);
		sig_unblock(SIGCHLD);
		sig_unblock(SIGTERM);
		execvp(*run, run);
		fatal2_cannot(s->islog ? "start log/" : "start ", *run);
	}
	/* parent */
	if (s->state != S_FINISH) {
		gettimeofday_ns(&s->start);
		s->state = S_RUN;
	}
	s->pid = p;
	pidchanged = 1;
	s->ctrl = C_NOOP;
	update_status(s);
}
int watchdog_main(int argc, char **argv)
{
	unsigned opts;
	unsigned timer_duration = 30000; /* Userspace timer duration, in milliseconds */
	char *t_arg;

	opt_complementary = "=1"; /* must have 1 argument */
	opts = getopt32(argv, "Ft:", &t_arg);

	if (opts & OPT_TIMER) {
		static const struct suffix_mult suffixes[] = {
			{ "ms", 1 },
			{ "", 1000 },
			{ }
		};
		timer_duration = xatou_sfx(t_arg, suffixes);
	}

	if (!(opts & OPT_FOREGROUND)) {
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
	}

	bb_signals(BB_FATAL_SIGS, watchdog_shutdown);

	/* Use known fd # - avoid needing global 'int fd' */
	xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3);

// TODO?
//	if (!(opts & OPT_TIMER)) {
//		if (ioctl(fd, WDIOC_GETTIMEOUT, &timer_duration) == 0)
//			timer_duration *= 500;
//		else
//			timer_duration = 30000;
//	}

	while (1) {
		/*
		 * Make sure we clear the counter before sleeping, as the counter value
		 * is undefined at this point -- PFM
		 */
		write(3, "", 1); /* write zero byte */
		usleep(timer_duration * 1000L);
	}
	return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
}
Exemple #7
0
void FAST_FUNC launch_helper(const char **argv)
{
	// setup vanilla unidirectional pipes interchange
	int i;
	int pipes[4];

	xpipe(pipes);
	xpipe(pipes + 2);

	// NB: handler must be installed before vfork
	bb_signals(0
		+ (1 << SIGCHLD)
		+ (1 << SIGALRM)
		, signal_handler);

	G.helper_pid = xvfork();

	i = (!G.helper_pid) * 2; // for parent:0, for child:2
	close(pipes[i + 1]);     // 1 or 3 - closing one write end
	close(pipes[2 - i]);     // 2 or 0 - closing one read end
	xmove_fd(pipes[i], STDIN_FILENO);      // 0 or 2 - using other read end
	xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end
	// End result:
	// parent stdout [3] -> child stdin [2]
	// child stdout [1] -> parent stdin [0]

	if (!G.helper_pid) {
		// child
		// if parent dies, get SIGTERM
		prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
		// try to execute connection helper
		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
		BB_EXECVP_or_die((char**)argv);
	}

	// parent goes on
}
int klogd_main(int argc UNUSED_PARAM, char **argv)
{
	int i = 0;
	char *opt_c;
	int opt;
	int used = 0;

	opt = getopt32(argv, "c:n", &opt_c);
	if (opt & OPT_LEVEL) {
		/* Valid levels are between 1 and 8 */
		i = xatou_range(opt_c, 1, 8);
	}
	if (!(opt & OPT_FOREGROUND)) {
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
	}

	openlog("kernel", 0, LOG_KERN);

	bb_signals(BB_FATAL_SIGS, klogd_signal);
	signal(SIGHUP, SIG_IGN);

	/* "Open the log. Currently a NOP" */
	klogctl(1, NULL, 0);

	/* "printk() prints a message on the console only if it has a loglevel
	 * less than console_loglevel". Here we set console_loglevel = i. */
	if (i)
		klogctl(8, NULL, i);

	syslog(LOG_NOTICE, "klogd started: %s", bb_banner);

	while (1) {
		int n;
		int priority;
		char *start;

		/* "2 -- Read from the log." */
		start = log_buffer + used;
		n = klogctl(2, start, KLOGD_LOGBUF_SIZE-1 - used);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m",
					errno);
			break;
		}
		start[n] = '\0';

		/* klogctl buffer parsing modelled after code in dmesg.c */
		/* Process each newline-terminated line in the buffer */
		start = log_buffer;
		while (1) {
			char *newline = strchrnul(start, '\n');

			if (*newline == '\0') {
				/* This line is incomplete... */
				if (start != log_buffer) {
					/* move it to the front of the buffer */
					overlapping_strcpy(log_buffer, start);
					used = newline - start;
					/* don't log it yet */
					break;
				}
				/* ...but if buffer is full, log it anyway */
				used = 0;
				newline = NULL;
			} else {
				*newline++ = '\0';
			}

			/* Extract the priority */
			priority = LOG_INFO;
			if (*start == '<') {
				start++;
				if (*start) {
					/* kernel never generates multi-digit prios */
					priority = (*start - '0');
					start++;
				}
				if (*start == '>')
					start++;
			}
			/* Log (only non-empty lines) */
			if (*start)
				syslog(priority, "%s", start);

			if (!newline)
				break;
			start = newline;
		}
	}

	return EXIT_FAILURE;
}
Exemple #9
0
int watchdog_main(int argc, char **argv)
{
	static const struct suffix_mult suffixes[] = {
		{ "ms", 1 },
		{ "", 1000 },
		{ "", 0 }
	};

	unsigned opts;
	unsigned stimer_duration; /* how often to restart */
	unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
	char *st_arg;
	char *ht_arg;

	opt_complementary = "=1"; /* must have exactly 1 argument */
	opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg);

	/* We need to daemonize *before* opening the watchdog as many drivers
	 * will only allow one process at a time to do so.  Since daemonizing
	 * is not perfect (child may run before parent finishes exiting), we
	 * can't rely on parent exiting before us (let alone *cleanly* releasing
	 * the watchdog fd -- something else that may not even be allowed).
	 */
	if (!(opts & OPT_FOREGROUND))
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);

	if (opts & OPT_HTIMER)
		htimer_duration = xatou_sfx(ht_arg, suffixes);
	stimer_duration = htimer_duration / 2;
	if (opts & OPT_STIMER)
		stimer_duration = xatou_sfx(st_arg, suffixes);

	bb_signals(BB_FATAL_SIGS, watchdog_shutdown);

	/* Use known fd # - avoid needing global 'int fd' */
	xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3);

	/* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
	htimer_duration = htimer_duration / 1000;
#ifndef WDIOC_SETTIMEOUT
# error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet
#else
# if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD
	{
		static const int enable = WDIOS_ENABLECARD;
		ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
	}
# endif
	ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
#endif

#if 0
	ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
	printf("watchdog: SW timer is %dms, HW timer is %ds\n",
		stimer_duration, htimer_duration * 1000);
#endif

	while (1) {
		/*
		 * Make sure we clear the counter before sleeping,
		 * as the counter value is undefined at this point -- PFM
		 */
		write(3, "", 1); /* write zero byte */
		usleep(stimer_duration * 1000L);
	}
	return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
}
Exemple #10
0
int ifplugd_main(int argc UNUSED_PARAM, char **argv)
{
	int iface_status;
	int delay_time;
	const char *iface_status_str;
	struct pollfd netlink_pollfd[1];
	unsigned opts;
	const char *api_mode_found;
#if ENABLE_FEATURE_PIDFILE
	char *pidfile_name;
	pid_t pid_from_pidfile;
#endif

	INIT_G();

	opt_complementary = "t+:u+:d+";
	opts = getopt32(argv, OPTION_STR,
		&G.iface, &G.script_name, &G.poll_time, &G.delay_up,
		&G.delay_down, &G.api_mode, &G.extra_arg);
	G.poll_time *= 1000;

	applet_name = xasprintf("ifplugd(%s)", G.iface);

#if ENABLE_FEATURE_PIDFILE
	pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
	pid_from_pidfile = read_pid(pidfile_name);

	if (opts & FLAG_KILL) {
		if (pid_from_pidfile > 0)
			/* Upstream tool use SIGINT for -k */
			kill(pid_from_pidfile, SIGINT);
		return EXIT_SUCCESS;
	}

	if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
		bb_error_msg_and_die("daemon already running");
#endif

	api_mode_found = strchr(api_modes, G.api_mode[0]);
	if (!api_mode_found)
		bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
	G.api_method_num = api_mode_found - api_modes;

	if (!(opts & FLAG_NO_DAEMON))
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);

	xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
	if (opts & FLAG_MONITOR) {
		struct sockaddr_nl addr;
		int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);

		memset(&addr, 0, sizeof(addr));
		addr.nl_family = AF_NETLINK;
		addr.nl_groups = RTMGRP_LINK;
		addr.nl_pid = getpid();

		xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
		xmove_fd(fd, netlink_fd);
	}

	write_pidfile(pidfile_name);

	/* this can't be moved before socket creation */
	if (!(opts & FLAG_NO_SYSLOG)) {
		openlog(applet_name, 0, LOG_DAEMON);
		logmode |= LOGMODE_SYSLOG;
	}

	bb_signals(0
		| (1 << SIGINT )
		| (1 << SIGTERM)
		| (1 << SIGQUIT)
		| (1 << SIGHUP ) /* why we ignore it? */
		/* | (1 << SIGCHLD) - run_script does not use it anymore */
		, record_signo);

	bb_error_msg("started: %s", bb_banner);

	if (opts & FLAG_MONITOR) {
		struct ifreq ifrequest;
		set_ifreq_to_ifname(&ifrequest);
		G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
	}

	if (G.iface_exists)
		maybe_up_new_iface();

	iface_status = detect_link();
	if (iface_status == IFSTATUS_ERR)
		goto exiting;
	iface_status_str = strstatus(iface_status);

	if (opts & FLAG_MONITOR) {
		bb_error_msg("interface %s",
			G.iface_exists ? "exists"
			: "doesn't exist, waiting");
	}
	/* else we assume it always exists, but don't mislead user
	 * by potentially lying that it really exists */

	if (G.iface_exists) {
		bb_error_msg("link is %s", iface_status_str);
	}

	if ((!(opts & FLAG_NO_STARTUP)
	     && iface_status == IFSTATUS_UP
	    )
	 || (opts & FLAG_INITIAL_DOWN)
	) {
		if (run_script(iface_status_str) != 0)
			goto exiting;
	}

	/* Main loop */
	netlink_pollfd[0].fd = netlink_fd;
	netlink_pollfd[0].events = POLLIN;
	delay_time = 0;
	while (1) {
		int iface_status_old;
		int iface_exists_old;

		switch (bb_got_signal) {
		case SIGINT:
		case SIGTERM:
			bb_got_signal = 0;
			goto cleanup;
		case SIGQUIT:
			bb_got_signal = 0;
			goto exiting;
		default:
			bb_got_signal = 0;
			break;
		}

		if (poll(netlink_pollfd,
				(opts & FLAG_MONITOR) ? 1 : 0,
				G.poll_time
			) < 0
		) {
			if (errno == EINTR)
				continue;
			bb_perror_msg("poll");
			goto exiting;
		}

		iface_status_old = iface_status;
		iface_exists_old = G.iface_exists;

		if ((opts & FLAG_MONITOR)
		 && (netlink_pollfd[0].revents & POLLIN)
		) {
			G.iface_exists = check_existence_through_netlink();
			if (G.iface_exists < 0) /* error */
				goto exiting;
			if (iface_exists_old != G.iface_exists) {
				bb_error_msg("interface %sappeared",
						G.iface_exists ? "" : "dis");
				if (G.iface_exists)
					maybe_up_new_iface();
			}
		}

		/* note: if !G.iface_exists, returns DOWN */
		iface_status = detect_link();
		if (iface_status == IFSTATUS_ERR) {
			if (!(opts & FLAG_MONITOR))
				goto exiting;
			iface_status = IFSTATUS_DOWN;
		}
		iface_status_str = strstatus(iface_status);

		if (iface_status_old != iface_status) {
			bb_error_msg("link is %s", iface_status_str);

			if (delay_time) {
				/* link restored its old status before
				 * we run script. don't run the script: */
				delay_time = 0;
			} else {
				delay_time = monotonic_sec();
				if (iface_status == IFSTATUS_UP)
					delay_time += G.delay_up;
				if (iface_status == IFSTATUS_DOWN)
					delay_time += G.delay_down;
				if (delay_time == 0)
					delay_time++;
			}
		}

		if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
			delay_time = 0;
			if (run_script(iface_status_str) != 0)
				goto exiting;
		}
	} /* while (1) */

 cleanup:
	if (!(opts & FLAG_NO_SHUTDOWN)
	 && (iface_status == IFSTATUS_UP
	     || (iface_status == IFSTATUS_DOWN && delay_time)
	    )
	) {
		setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
		setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
		run_script("down\0up"); /* reusing string */
	}

 exiting:
	remove_pidfile(pidfile_name);
	bb_error_msg_and_die("exiting");
}
Exemple #11
0
int runsvdir_main(int argc UNUSED_PARAM, char **argv)
{
	struct stat s;
	dev_t last_dev = last_dev; /* for gcc */
	ino_t last_ino = last_ino; /* for gcc */
	time_t last_mtime = 0;
	int wstat;
	int curdir;
	pid_t pid;
	unsigned deadline;
	unsigned now;
	unsigned stampcheck;
	int i;
	int need_rescan = 1;
	char *opt_s_argv[3];

	INIT_G();

	opt_complementary = "-1";
	opt_s_argv[0] = NULL;
	opt_s_argv[2] = NULL;
	getopt32(argv, "Ps:", &opt_s_argv[0]);
	argv += optind;

	bb_signals(0
		| (1 << SIGTERM)
		| (1 << SIGHUP)
		/* For busybox's init, SIGTERM == reboot,
		 * SIGUSR1 == halt
		 * SIGUSR2 == poweroff
		 * so we need to intercept SIGUSRn too.
		 * Note that we do not implement actual reboot
		 * (killall(TERM) + umount, etc), we just pause
		 * respawing and avoid exiting (-> making kernel oops).
		 * The user is responsible for the rest. */
		| (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
		, record_signo);
	svdir = *argv++;

#if ENABLE_FEATURE_RUNSVDIR_LOG
	/* setup log */
	if (*argv) {
		rplog = *argv;
		rploglen = strlen(rplog);
		if (rploglen < 7) {
			warnx("log must have at least seven characters");
		} else if (piped_pair(logpipe)) {
			warnx("can't create pipe for log");
		} else {
			close_on_exec_on(logpipe.rd);
			close_on_exec_on(logpipe.wr);
			ndelay_on(logpipe.rd);
			ndelay_on(logpipe.wr);
			if (dup2(logpipe.wr, 2) == -1) {
				warnx("can't set filedescriptor for log");
			} else {
				pfd[0].fd = logpipe.rd;
				pfd[0].events = POLLIN;
				stamplog = monotonic_sec();
				goto run;
			}
		}
		rplog = NULL;
		warnx("log service disabled");
	}
 run:
#endif
	curdir = open(".", O_RDONLY|O_NDELAY);
	if (curdir == -1)
		fatal2_cannot("open current directory", "");
	close_on_exec_on(curdir);

	stampcheck = monotonic_sec();

	for (;;) {
		/* collect children */
		for (;;) {
			pid = wait_any_nohang(&wstat);
			if (pid <= 0)
				break;
			for (i = 0; i < svnum; i++) {
				if (pid == sv[i].pid) {
					/* runsv has died */
					sv[i].pid = 0;
					need_rescan = 1;
				}
			}
		}

		now = monotonic_sec();
		if ((int)(now - stampcheck) >= 0) {
			/* wait at least a second */
			stampcheck = now + 1;

			if (stat(svdir, &s) != -1) {
				if (need_rescan || s.st_mtime != last_mtime
				 || s.st_ino != last_ino || s.st_dev != last_dev
				) {
					/* svdir modified */
					if (chdir(svdir) != -1) {
						last_mtime = s.st_mtime;
						last_dev = s.st_dev;
						last_ino = s.st_ino;
						/* if the svdir changed this very second, wait until the
						 * next second, because we won't be able to detect more
						 * changes within this second */
						while (time(NULL) == last_mtime)
							usleep(100000);
						need_rescan = do_rescan();
						while (fchdir(curdir) == -1) {
							warn2_cannot("change directory, pausing", "");
							sleep(5);
						}
					} else {
						warn2_cannot("change directory to ", svdir);
					}
				}
			} else {
				warn2_cannot("stat ", svdir);
			}
		}

#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (rplog) {
			if ((int)(now - stamplog) >= 0) {
				write(logpipe.wr, ".", 1);
				stamplog = now + 900;
			}
		}
		pfd[0].revents = 0;
#endif
		deadline = (need_rescan ? 1 : 5);
		sig_block(SIGCHLD);
#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (rplog)
			poll(pfd, 1, deadline*1000);
		else
#endif
			sleep(deadline);
		sig_unblock(SIGCHLD);

#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (pfd[0].revents & POLLIN) {
			char ch;
			while (read(logpipe.rd, &ch, 1) > 0) {
				if (ch < ' ')
					ch = ' ';
				for (i = 6; i < rploglen; i++)
					rplog[i-1] = rplog[i];
				rplog[rploglen-1] = ch;
			}
		}
#endif
		if (!bb_got_signal)
			continue;

		/* -s SCRIPT: useful if we are init.
		 * In this case typically script never returns,
		 * it halts/powers off/reboots the system. */
		if (opt_s_argv[0]) {
			/* Single parameter: signal# */
			opt_s_argv[1] = utoa(bb_got_signal);
			pid = spawn(opt_s_argv);
			if (pid > 0) {
				/* Remembering to wait for _any_ children,
				 * not just pid */
				while (wait(NULL) != pid)
					continue;
			}
		}

		if (bb_got_signal == SIGHUP) {
			for (i = 0; i < svnum; i++)
				if (sv[i].pid)
					kill(sv[i].pid, SIGTERM);
		}
		/* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
		/* Exit unless we are init */
		if (getpid() != 1)
			return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;

		/* init continues to monitor services forever */
		bb_got_signal = 0;
	} /* for (;;) */
}
int inotifyd_main(int argc UNUSED_PARAM, char **argv)
{
	int n;
	unsigned mask = IN_ALL_EVENTS; // assume we want all events
	struct pollfd pfd;
	char **watched = ++argv; // watched name list
	const char *args[] = { *argv, NULL, NULL, NULL, NULL };

	// sanity check: agent and at least one watch must be given
	if (!argv[1])
		bb_show_usage();

	// open inotify
	pfd.fd = inotify_init();
	if (pfd.fd < 0)
		bb_perror_msg_and_die("no kernel support");

	// setup watched
	while (*++argv) {
		char *path = *argv;
		char *masks = strchr(path, ':');
		// if mask is specified ->
		if (masks) {
			*masks = '\0'; // split path and mask
			// convert mask names to mask bitset
			mask = 0;
			while (*++masks) {
				int i = strchr(mask_names, *masks) - mask_names;
				if (i >= 0) {
					mask |= (1 << i);
				}
			}
		}
		// add watch
		n = inotify_add_watch(pfd.fd, path, mask);
		if (n < 0)
			bb_perror_msg_and_die("add watch (%s) failed", path);
		//bb_error_msg("added %d [%s]:%4X", n, path, mask);
	}

	// setup signals
	bb_signals(BB_FATAL_SIGS, record_signo);

	// do watch
	pfd.events = POLLIN;
	while (1) {
		ssize_t len;
		void *buf;
		struct inotify_event *ie;

 again:
		if (bb_got_signal)
			break;
		n = poll(&pfd, 1, -1);
		/* Signal interrupted us? */
		if (n < 0 && errno == EINTR)
			goto again;
		// Under Linux, above if() is not necessary.
		// Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
		// are not interrupting poll().
		// Thus we can just break if n <= 0 (see below),
		// because EINTR will happen only on SIGTERM et al.
		// But this might be not true under other Unixes,
		// and is generally way too subtle to depend on.
		if (n <= 0) // strange error?
			break;

		// read out all pending events
		xioctl(pfd.fd, FIONREAD, &len);
#define eventbuf bb_common_bufsiz1
		ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
		len = full_read(pfd.fd, buf, len);
		// process events. N.B. events may vary in length
		while (len > 0) {
			int i;
			char events[sizeof(mask_names)];
			char *s = events;
			unsigned m = ie->mask;

			for (i = 0; i < sizeof(mask_names)-1; ++i, m >>= 1) {
				if (m & 1)
					*s++ = mask_names[i];
			}
			*s = '\0';
			//bb_error_msg("exec %s %08X\t%s\t%s\t%s", agent,
			// ie->mask, events, watched[ie->wd], ie->len ? ie->name : "");
			args[1] = events;
			args[2] = watched[ie->wd];
			args[3] = ie->len ? ie->name : NULL;
			wait4pid(xspawn((char **)args));
			// next event
			i = sizeof(struct inotify_event) + ie->len;
			len -= i;
			ie = (void*)((char*)ie + i);
		}
		if (eventbuf != buf)
			free(buf);
	}

	return EXIT_SUCCESS;
}
Exemple #13
0
int more_main(int argc UNUSED_PARAM, char **argv)
{
	int c = c; /* for compiler */
	int lines;
	int input = 0;
	int spaces = 0;
	int please_display_more_prompt;
	struct stat st;
	FILE *file;
	FILE *cin;
	int len;
	unsigned terminal_width;
	unsigned terminal_height;

	INIT_G();

	argv++;
	/* Another popular pager, most, detects when stdout
	 * is not a tty and turns into cat. This makes sense. */
	if (!isatty(STDOUT_FILENO))
		return bb_cat(argv);
	cin = fopen_for_read(CURRENT_TTY);
	if (!cin)
		return bb_cat(argv);

	if (ENABLE_FEATURE_USE_TERMIOS) {
		cin_fileno = fileno(cin);
		getTermSettings(cin_fileno, &initial_settings);
		new_settings = initial_settings;
		new_settings.c_lflag &= ~(ICANON | ECHO);
		new_settings.c_cc[VMIN] = 1;
		new_settings.c_cc[VTIME] = 0;
		setTermSettings(cin_fileno, &new_settings);
		bb_signals(0
			+ (1 << SIGINT)
			+ (1 << SIGQUIT)
			+ (1 << SIGTERM)
			, gotsig);
	}

	do {
		file = stdin;
		if (*argv) {
			file = fopen_or_warn(*argv, "r");
			if (!file)
				continue;
		}
		st.st_size = 0;
		fstat(fileno(file), &st);

		please_display_more_prompt = 0;
		/* never returns w, h <= 1 */
		get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
		terminal_height -= 1;

		len = 0;
		lines = 0;
		while (spaces || (c = getc(file)) != EOF) {
			int wrap;
			if (spaces)
				spaces--;
 loop_top:
			if (input != 'r' && please_display_more_prompt) {
				len = printf("--More-- ");
				if (st.st_size != 0) {
					uoff_t d = (uoff_t)st.st_size / 100;
					if (d == 0)
						d = 1;
					len += printf("(%u%% of %"OFF_FMT"u bytes)",
						(int) ((uoff_t)ftello(file) / d),
						st.st_size);
				}
				fflush_all();

				/*
				 * We've just displayed the "--More--" prompt, so now we need
				 * to get input from the user.
				 */
				for (;;) {
					input = getc(cin);
					input = tolower(input);
					if (!ENABLE_FEATURE_USE_TERMIOS)
						printf("\033[A"); /* cursor up */
					/* Erase the last message */
					printf("\r%*s\r", len, "");

					/* Due to various multibyte escape
					 * sequences, it's not ok to accept
					 * any input as a command to scroll
					 * the screen. We only allow known
					 * commands, else we show help msg. */
					if (input == ' ' || input == '\n' || input == 'q' || input == 'r')
						break;
					len = printf("(Enter:next line Space:next page Q:quit R:show the rest)");
				}
				len = 0;
				lines = 0;
				please_display_more_prompt = 0;

				if (input == 'q')
					goto end;

				/* The user may have resized the terminal.
				 * Re-read the dimensions. */
				if (ENABLE_FEATURE_USE_TERMIOS) {
					get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
					terminal_height -= 1;
				}
			}

			/* Crudely convert tabs into spaces, which are
			 * a bajillion times easier to deal with. */
			if (c == '\t') {
				spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE;
				c = ' ';
			}

			/*
			 * There are two input streams to worry about here:
			 *
			 * c    : the character we are reading from the file being "mored"
			 * input: a character received from the keyboard
			 *
			 * If we hit a newline in the _file_ stream, we want to test and
			 * see if any characters have been hit in the _input_ stream. This
			 * allows the user to quit while in the middle of a file.
			 */
			wrap = (++len > terminal_width);
			if (c == '\n' || wrap) {
				/* Then outputting this character
				 * will move us to a new line. */
				if (++lines >= terminal_height || input == '\n')
					please_display_more_prompt = 1;
				len = 0;
			}
			if (c != '\n' && wrap) {
				/* Then outputting this will also put a character on
				 * the beginning of that new line. Thus we first want to
				 * display the prompt (if any), so we skip the putchar()
				 * and go back to the top of the loop, without reading
				 * a new character. */
				putchar('\n');
				goto loop_top;
			}
			/* My small mind cannot fathom backspaces and UTF-8 */
			putchar(c);
			die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */
		}
		fclose(file);
		fflush_all();
	} while (*argv && *++argv);
 end:
	setTermSettings(cin_fileno, &initial_settings);
	return 0;
}
Exemple #14
0
int klogd_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
	int i = i; /* silence gcc */
	char *start;

	/* do normal option parsing */
	getopt32(argv, "c:n", &start);

	if (option_mask32 & OPT_LEVEL) {
		/* Valid levels are between 1 and 8 */
		i = xatoul_range(start, 1, 8);
	}

	if (!(option_mask32 & OPT_FOREGROUND)) {
		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
	}

	openlog("kernel", 0, LOG_KERN);

	/* Set up sig handlers */
	bb_signals(0
		+ (1 << SIGINT)
		+ (1 << SIGTERM)
		, klogd_signal);
	signal(SIGHUP, SIG_IGN);

	/* "Open the log. Currently a NOP." */
	klogctl(1, NULL, 0);

	/* Set level of kernel console messaging. */
	if (option_mask32 & OPT_LEVEL)
		klogctl(8, NULL, i);

	syslog(LOG_NOTICE, "klogd started: %s", bb_banner);

	/* Note: this code does not detect incomplete messages
	 * (messages not ending with '\n' or just when kernel
	 * generates too many messages for us to keep up)
	 * and will split them in two separate lines */
	while (1) {
		int n;
		int priority;

		n = klogctl(2, log_buffer, KLOGD_LOGBUF_SIZE - 1);
		if (n < 0) {
			if (errno == EINTR)
				continue;
			syslog(LOG_ERR, "klogd: error from klogctl(2): %d - %m",
					errno);
			break;
		}
		log_buffer[n] = '\n';
		i = 0;
		while (i < n) {
			priority = LOG_INFO;
			start = &log_buffer[i];
			if (log_buffer[i] == '<') {
				i++;
				// kernel never ganerates multi-digit prios
				//priority = 0;
				//while (log_buffer[i] >= '0' && log_buffer[i] <= '9') {
				//	priority = priority * 10 + (log_buffer[i] - '0');
				//	i++;
				//}
				if (isdigit(log_buffer[i])) {
					priority = (log_buffer[i] - '0');
					i++;
				}
				if (log_buffer[i] == '>')
					i++;
				start = &log_buffer[i];
			}
			while (log_buffer[i] != '\n')
				i++;
			log_buffer[i] = '\0';
			syslog(priority, "%s", start);
			i++;
		}
	}

	return EXIT_FAILURE;
}
int slattach_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
	/* Line discipline code table */
	static const char proto_names[] ALIGN1 =
		"slip\0"        /* 0 */
		"cslip\0"       /* 1 */
		"slip6\0"       /* 2 */
		"cslip6\0"      /* 3 */
		"adaptive\0"    /* 8 */
		;

	int i, encap, opt;
	struct termios state;
	const char *proto = "cslip";
	const char *extcmd;				/* Command to execute after hangup */
	const char *baud_str;
	int baud_code = -1;				/* Line baud rate (system code) */

	enum {
		OPT_p_proto  = 1 << 0,
		OPT_s_baud   = 1 << 1,
		OPT_c_extcmd = 1 << 2,
		OPT_e_quit   = 1 << 3,
		OPT_h_watch  = 1 << 4,
		OPT_m_nonraw = 1 << 5,
		OPT_L_local  = 1 << 6,
		OPT_F_noflow = 1 << 7
	};

	INIT_G();

	/* Parse command line options */
	opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd);
	/*argc -= optind;*/
	argv += optind;

	if (!*argv)
		bb_show_usage();

	encap = index_in_strings(proto_names, proto);

	if (encap < 0)
		invarg(proto, "protocol");
	if (encap > 3)
		encap = 8;

	/* We want to know if the baud rate is valid before we start touching the ttys */
	if (opt & OPT_s_baud) {
		baud_code = tty_value_to_baud(xatoi(baud_str));
		if (baud_code < 0)
			invarg(baud_str, "baud rate");
	}

	/* Trap signals in order to restore tty states upon exit */
	if (!(opt & OPT_e_quit)) {
		bb_signals(0
			+ (1 << SIGHUP)
			+ (1 << SIGINT)
			+ (1 << SIGQUIT)
			+ (1 << SIGTERM)
			, sig_handler);
	}

	/* Open tty */
	handle = open(*argv, O_RDWR | O_NDELAY);
	if (handle < 0) {
		char *buf = concat_path_file("/dev", *argv);
		handle = xopen(buf, O_RDWR | O_NDELAY);
		/* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
		free(buf);
	}

	/* Save current tty state */
	save_state();

	/* Configure tty */
	memcpy(&state, &saved_state, sizeof(state));
	if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
		memset(&state.c_cc, 0, sizeof(state.c_cc));
		state.c_cc[VMIN] = 1;
		state.c_iflag = IGNBRK | IGNPAR;
		state.c_oflag = 0;
		state.c_lflag = 0;
		state.c_cflag = CS8 | HUPCL | CREAD
		              | ((opt & OPT_L_local) ? CLOCAL : 0)
		              | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
	}

	if (opt & OPT_s_baud) {
		cfsetispeed(&state, baud_code);
		cfsetospeed(&state, baud_code);
	}

	set_state(&state, encap);

	/* Exit now if option -e was passed */
	if (opt & OPT_e_quit)
		return 0;

	/* If we're not requested to watch, just keep descriptor open
	 * until we are killed */
	if (!(opt & OPT_h_watch))
		while (1)
			sleep(24*60*60);

	/* Watch line for hangup */
	while (1) {
		if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR))
			goto no_carrier;
		sleep(15);
	}

 no_carrier:

	/* Execute command on hangup */
	if (opt & OPT_c_extcmd)
		system(extcmd);

	/* Restore states and exit */
	restore_state_and_exit(EXIT_SUCCESS);
}
Exemple #16
0
int tcpudpsvd_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
	char *str_C, *str_t;
	char *user;
	struct hcc *hccp;
	const char *instructs;
	char *msg_per_host = NULL;
	unsigned len_per_host = len_per_host; /* gcc */
#ifndef SSLSVD
	struct bb_uidgid_t ugid;
#endif
	bool tcp;
	uint16_t local_port;
	char *preset_local_hostname = NULL;
	char *remote_hostname = remote_hostname; /* for compiler */
	char *remote_addr = remote_addr; /* for compiler */
	len_and_sockaddr *lsa;
	len_and_sockaddr local, remote;
	socklen_t sa_len;
	int pid;
	int sock;
	int conn;
	unsigned backlog = 20;

	INIT_G();

	tcp = (applet_name[0] == 't');

	/* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
	opt_complementary = "-3:i--i:ph:vv:b+:c+";
#ifdef SSLSVD
	getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
		&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
		&backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
	);
#else
	/* "+": stop on first non-option */
	getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
		&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
		&backlog, &str_t, &verbose
	);
#endif
	if (option_mask32 & OPT_C) { /* -C n[:message] */
		max_per_host = bb_strtou(str_C, &str_C, 10);
		if (str_C[0]) {
			if (str_C[0] != ':')
				bb_show_usage();
			msg_per_host = str_C + 1;
			len_per_host = strlen(msg_per_host);
		}
	}
	if (max_per_host > cmax)
		max_per_host = cmax;
	if (option_mask32 & OPT_u) {
		if (!get_uidgid(&ugid, user, 1))
			bb_error_msg_and_die("unknown user/group: %s", user);
	}
#ifdef SSLSVD
	if (option_mask32 & OPT_U) ssluser = optarg;
	if (option_mask32 & OPT_slash) root = optarg;
	if (option_mask32 & OPT_Z) cert = optarg;
	if (option_mask32 & OPT_K) key = optarg;
#endif
	argv += optind;
	if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
		argv[0] = (char*)"0.0.0.0";

	/* Per-IP flood protection is not thought-out for UDP */
	if (!tcp)
		max_per_host = 0;

	bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */

#ifdef SSLSVD
	sslser = user;
	client = 0;
	if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
		xfunc_exitcode = 100;
		bb_error_msg_and_die("-U ssluser must be set when running as root");
	}
	if (option_mask32 & OPT_u)
		if (!uidgid_get(&sslugid, ssluser, 1)) {
			if (errno) {
				bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
			}
			bb_error_msg_and_die("unknown user/group '%s'", ssluser);
		}
	if (!cert) cert = "./cert.pem";
	if (!key) key = cert;
	if (matrixSslOpen() < 0)
		fatal("cannot initialize ssl");
	if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
		if (client)
			fatal("cannot read cert, key, or ca file");
		fatal("cannot read cert or key file");
	}
	if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
		fatal("cannot create ssl session");
#endif

	sig_block(SIGCHLD);
	signal(SIGCHLD, sig_child_handler);
	bb_signals(BB_FATAL_SIGS, sig_term_handler);
	signal(SIGPIPE, SIG_IGN);

	if (max_per_host)
		ipsvd_perhost_init(cmax);

	local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
	lsa = xhost2sockaddr(argv[0], local_port);
	argv += 2;

	sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
	setsockopt_reuseaddr(sock);
	sa_len = lsa->len; /* I presume sockaddr len stays the same */
	xbind(sock, &lsa->u.sa, sa_len);
	if (tcp)
		xlisten(sock, backlog);
	else /* udp: needed for recv_from_to to work: */
		socket_want_pktinfo(sock);
	/* ndelay_off(sock); - it is the default I think? */

#ifndef SSLSVD
	if (option_mask32 & OPT_u) {
		/* drop permissions */
		xsetgid(ugid.gid);
		xsetuid(ugid.uid);
	}
#endif

	if (verbose) {
		char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
		bb_error_msg("listening on %s, starting", addr);
		free(addr);
#ifndef SSLSVD
		if (option_mask32 & OPT_u)
			printf(", uid %u, gid %u",
				(unsigned)ugid.uid, (unsigned)ugid.gid);
#endif
	}

	/* Main accept() loop */

 again:
	hccp = NULL;

	while (cnum >= cmax)
		wait_for_any_sig(); /* expecting SIGCHLD */

	/* Accept a connection to fd #0 */
 again1:
	close(0);
 again2:
	sig_unblock(SIGCHLD);
	local.len = remote.len = sa_len;
	if (tcp) {
		conn = accept(sock, &remote.u.sa, &remote.len);
	} else {
		/* In case recv_from_to won't be able to recover local addr.
		 * Also sets port - recv_from_to is unable to do it. */
		local = *lsa;
		conn = recv_from_to(sock, NULL, 0, MSG_PEEK,
				&remote.u.sa, &local.u.sa, sa_len);
	}
	sig_block(SIGCHLD);
	if (conn < 0) {
		if (errno != EINTR)
			bb_perror_msg(tcp ? "accept" : "recv");
		goto again2;
	}
	xmove_fd(tcp ? conn : sock, 0);

	if (max_per_host) {
		/* Drop connection immediately if cur_per_host > max_per_host
		 * (minimizing load under SYN flood) */
		remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
		cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
		if (cur_per_host > max_per_host) {
			/* ipsvd_perhost_add detected that max is exceeded
			 * (and did not store ip in connection table) */
			free(remote_addr);
			if (msg_per_host) {
				/* don't block or test for errors */
				send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
			}
			goto again1;
		}
		/* NB: remote_addr is not leaked, it is stored in conn table */
	}

	if (!tcp) {
		/* Voodoo magic: making udp sockets each receive its own
		 * packets is not trivial, and I still not sure
		 * I do it 100% right.
		 * 1) we have to do it before fork()
		 * 2) order is important - is it right now? */

		/* Open new non-connected UDP socket for further clients... */
		sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
		setsockopt_reuseaddr(sock);
		/* Make plain write/send work for old socket by supplying default
		 * destination address. This also restricts incoming packets
		 * to ones coming from this remote IP. */
		xconnect(0, &remote.u.sa, sa_len);
	/* hole? at this point we have no wildcard udp socket...
	 * can this cause clients to get "port unreachable" icmp?
	 * Yup, time window is very small, but it exists (is it?) */
		/* ..."open new socket", continued */
		xbind(sock, &lsa->u.sa, sa_len);
		socket_want_pktinfo(sock);

		/* Doesn't work:
		 * we cannot replace fd #0 - we will lose pending packet
		 * which is already buffered for us! And we cannot use fd #1
		 * instead - it will "intercept" all following packets, but child
		 * does not expect data coming *from fd #1*! */
#if 0
		/* Make it so that local addr is fixed to localp->u.sa
		 * and we don't accidentally accept packets to other local IPs. */
		/* NB: we possibly bind to the _very_ same_ address & port as the one
		 * already bound in parent! This seems to work in Linux.
		 * (otherwise we can move socket to fd #0 only if bind succeeds) */
		close(0);
		set_nport(localp, htons(local_port));
		xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0);
		setsockopt_reuseaddr(0); /* crucial */
		xbind(0, &localp->u.sa, localp->len);
#endif
	}

	pid = vfork();
	if (pid == -1) {
		bb_perror_msg("vfork");
		goto again;
	}

	if (pid != 0) {
		/* Parent */
		cnum++;
		if (verbose)
			connection_status();
		if (hccp)
			hccp->pid = pid;
		/* clean up changes done by vforked child */
		undo_xsetenv();
		goto again;
	}

	/* Child: prepare env, log, and exec prog */

	/* Closing tcp listening socket */
	if (tcp)
		close(sock);

	{ /* vfork alert! every xmalloc in this block should be freed! */
		char *local_hostname = local_hostname; /* for compiler */
		char *local_addr = NULL;
		char *free_me0 = NULL;
		char *free_me1 = NULL;
		char *free_me2 = NULL;

		if (verbose || !(option_mask32 & OPT_E)) {
			if (!max_per_host) /* remote_addr is not yet known */
				free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
			if (option_mask32 & OPT_h) {
				free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
				if (!remote_hostname) {
					bb_error_msg("cannot look up hostname for %s", remote_addr);
					remote_hostname = remote_addr;
				}
			}
			/* Find out local IP peer connected to.
			 * Errors ignored (I'm not paranoid enough to imagine kernel
			 * which doesn't know local IP). */
			if (tcp)
				getsockname(0, &local.u.sa, &local.len);
			/* else: for UDP it is done earlier by parent */
			local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
			if (option_mask32 & OPT_h) {
				local_hostname = preset_local_hostname;
				if (!local_hostname) {
					free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
					if (!local_hostname)
						bb_error_msg_and_die("cannot look up hostname for %s", local_addr);
				}
				/* else: local_hostname is not NULL, but is NOT malloced! */
			}
		}
		if (verbose) {
			pid = getpid();
			if (max_per_host) {
				bb_error_msg("concurrency %s %u/%u",
					remote_addr,
					cur_per_host, max_per_host);
			}
			bb_error_msg((option_mask32 & OPT_h)
				? "start %u %s-%s (%s-%s)"
				: "start %u %s-%s",
				pid,
				local_addr, remote_addr,
				local_hostname, remote_hostname);
		}

		if (!(option_mask32 & OPT_E)) {
			/* setup ucspi env */
			const char *proto = tcp ? "TCP" : "UDP";

			/* Extract "original" destination addr:port
			 * from Linux firewall. Useful when you redirect
			 * an outbond connection to local handler, and it needs
			 * to know where it originally tried to connect */
			if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
				char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
				xsetenv_plain("TCPORIGDSTADDR", addr);
				free(addr);
			}
			xsetenv_plain("PROTO", proto);
			xsetenv_proto(proto, "LOCALADDR", local_addr);
			xsetenv_proto(proto, "REMOTEADDR", remote_addr);
			if (option_mask32 & OPT_h) {
				xsetenv_proto(proto, "LOCALHOST", local_hostname);
				xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
			}
			//compat? xsetenv_proto(proto, "REMOTEINFO", "");
			/* additional */
			if (cur_per_host > 0) /* can not be true for udp */
				xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
		}
		free(local_addr);
		free(free_me0);
		free(free_me1);
		free(free_me2);
	}

	xdup2(0, 1);

	signal(SIGTERM, SIG_DFL);
	signal(SIGPIPE, SIG_DFL);
	signal(SIGCHLD, SIG_DFL);
	sig_unblock(SIGCHLD);

#ifdef SSLSVD
	strcpy(id, utoa(pid));
	ssl_io(0, argv);
#else
	BB_EXECVP(argv[0], argv);
#endif
	bb_perror_msg_and_die("exec '%s'", argv[0]);
}
Exemple #17
0
int vlock_main(int argc UNUSED_PARAM, char **argv)
{
#ifdef __linux__
	struct vt_mode vtm;
	struct vt_mode ovtm;
#endif
	struct termios term;
	struct termios oterm;
	struct passwd *pw;

	pw = xgetpwuid(getuid());
	opt_complementary = "=0"; /* no params! */
	getopt32(argv, "a");

	/* Ignore some signals so that we don't get killed by them */
	bb_signals(0
		+ (1 << SIGTSTP)
		+ (1 << SIGTTIN)
		+ (1 << SIGTTOU)
		+ (1 << SIGHUP )
		+ (1 << SIGCHLD) /* paranoia :) */
		+ (1 << SIGQUIT)
		+ (1 << SIGINT )
		, SIG_IGN);

#ifdef __linux__
	/* We will use SIGUSRx for console switch control: */
	/* 1: set handlers */
	signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
	signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt);
	/* 2: unmask them */
	sig_unblock(SIGUSR1);
	sig_unblock(SIGUSR2);
#endif

	/* Revert stdin/out to our controlling tty
	 * (or die if we have none) */
	xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
	xdup2(STDIN_FILENO, STDOUT_FILENO);

#ifdef __linux__
	xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
	ovtm = vtm;
	/* "console switches are controlled by us, not kernel!" */
	vtm.mode = VT_PROCESS;
	vtm.relsig = SIGUSR1;
	vtm.acqsig = SIGUSR2;
	ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
#endif

//TODO: use set_termios_to_raw()
	tcgetattr(STDIN_FILENO, &oterm);
	term = oterm;
	term.c_iflag |= IGNBRK; /* ignore serial break (why? VTs don't have breaks, right?) */
	term.c_iflag &= ~BRKINT; /* redundant? "dont translate break to SIGINT" */
	term.c_lflag &= ~(ISIG | ECHO | ECHOCTL); /* ignore ^C ^Z, echo off */
	tcsetattr_stdin_TCSANOW(&term);

	while (1) {
		printf("Virtual console%s locked by %s.\n",
				/* "s" if -a, else "": */ "s" + !option_mask32,
				pw->pw_name
		);
		if (ask_and_check_password(pw) > 0) {
			break;
		}
		bb_do_delay(LOGIN_FAIL_DELAY);
		puts("Incorrect password");
	}

#ifdef __linux__
	ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
#endif
	tcsetattr_stdin_TCSANOW(&oterm);
	fflush_stdout_and_exit(EXIT_SUCCESS);
}
int vlock_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
	struct vt_mode vtm;
	struct termios term;
	struct termios oterm;
	struct vt_mode ovtm;
	uid_t uid;
	struct passwd *pw;

	uid = getuid();
	pw = getpwuid(uid);
	if (pw == NULL)
		bb_error_msg_and_die("unknown uid %d", (int)uid);

	opt_complementary = "=0"; /* no params! */
	getopt32(argv, "a");

	/* Ignore some signals so that we don't get killed by them */
	bb_signals(0
		+ (1 << SIGTSTP)
		+ (1 << SIGTTIN)
		+ (1 << SIGTTOU)
		+ (1 << SIGHUP )
		+ (1 << SIGCHLD) /* paranoia :) */
		+ (1 << SIGQUIT)
		+ (1 << SIGINT )
		, SIG_IGN);

	/* We will use SIGUSRx for console switch control: */
	/* 1: set handlers */
	signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
	signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt);
	/* 2: unmask them */
	sig_unblock(SIGUSR1);
	sig_unblock(SIGUSR2);

	/* Revert stdin/out to our controlling tty
	 * (or die if we have none) */
	xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
	xdup2(STDIN_FILENO, STDOUT_FILENO);

	xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
	ovtm = vtm;
	/* "console switches are controlled by us, not kernel!" */
	vtm.mode = VT_PROCESS;
	vtm.relsig = SIGUSR1;
	vtm.acqsig = SIGUSR2;
	ioctl(STDIN_FILENO, VT_SETMODE, &vtm);

	tcgetattr(STDIN_FILENO, &oterm);
	term = oterm;
	term.c_iflag &= ~BRKINT;
	term.c_iflag |= IGNBRK;
	term.c_lflag &= ~ISIG;
	term.c_lflag &= ~(ECHO | ECHOCTL);
	tcsetattr(STDIN_FILENO, TCSANOW, &term);

	do {
		printf("Virtual console%s locked by %s.\n",
				option_mask32 /*o_lock_all*/ ? "s" : "",
				pw->pw_name);
		if (correct_password(pw)) {
			break;
		}
		bb_do_delay(FAIL_DELAY);
		puts("Password incorrect");
	} while (1);

	ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
	tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
	fflush_stdout_and_exit(EXIT_SUCCESS);
}
Exemple #19
0
int microcom_main(int argc UNUSED_PARAM, char **argv)
{
	int sfd;
	int nfd;
	struct pollfd pfd[2];
	struct termios tio0, tiosfd, tio;
	char *device_lock_file;
	enum {
		OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
		OPT_s = 1 << 1, // baudrate
		OPT_d = 1 << 2, // wait for device response, ms
		OPT_t = 1 << 3, // timeout, ms
	};
	speed_t speed = 9600;
	int delay = -1;
	int timeout = -1;
	unsigned opts;

	// fetch options
	opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
	opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
//	argc -= optind;
	argv += optind;

	// try to create lock file in /var/lock
	device_lock_file = (char *)bb_basename(argv[0]);
	device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
	sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
	if (sfd < 0) {
		// device already locked -> bail out
		if (errno == EEXIST)
			bb_perror_msg_and_die("can't create %s", device_lock_file);
		// can't create lock -> don't care
		if (ENABLE_FEATURE_CLEAN_UP)
			free(device_lock_file);
		device_lock_file = NULL;
	} else {
		// %4d to make concurrent mgetty (if any) happy.
		// Mgetty treats 4-bytes lock files as binary,
		// not text, PID. Making 5+ char file. Brrr...
		fdprintf(sfd, "%4d\n", getpid());
		close(sfd);
	}

	// setup signals
	bb_signals(0
		+ (1 << SIGHUP)
		+ (1 << SIGINT)
		+ (1 << SIGTERM)
		+ (1 << SIGPIPE)
		, signal_handler);

	// error exit code if we fail to open the device
	signalled = 1;

	// open device
	sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (sfd < 0)
		goto done;
	fcntl(sfd, F_SETFL, 0);

	// put device to "raw mode"
	xget1(sfd, &tio, &tiosfd);
	// set device speed
	cfsetspeed(&tio, tty_value_to_baud(speed));
	if (xset1(sfd, &tio, argv[0]))
		goto done;

	// put stdin to "raw mode" (if stdin is a TTY),
	// handle one character at a time
	if (isatty(STDIN_FILENO)) {
		xget1(STDIN_FILENO, &tio, &tio0);
		if (xset1(STDIN_FILENO, &tio, "stdin"))
			goto done;
	}

	// main loop: check with poll(), then read/write bytes across
	pfd[0].fd = sfd;
	pfd[0].events = POLLIN;
	pfd[1].fd = STDIN_FILENO;
	pfd[1].events = POLLIN;

	signalled = 0;
	nfd = 2;
	while (!signalled && safe_poll(pfd, nfd, timeout) > 0) {
		if (nfd > 1 && pfd[1].revents) {
			char c;
			// read from stdin -> write to device
			if (safe_read(STDIN_FILENO, &c, 1) < 1) {
				// don't poll stdin anymore if we got EOF/error
				nfd--;
				goto skip_write;
			}
			// do we need special processing?
			if (!(opts & OPT_X)) {
				// ^@ sends Break
				if (VINTR == c) {
					tcsendbreak(sfd, 0);
					goto skip_write;
				}
				// ^X exits
				if (24 == c)
					break;
			}
			write(sfd, &c, 1);
			if (delay >= 0)
				safe_poll(pfd, 1, delay);
skip_write: ;
		}
		if (pfd[0].revents) {
#define iobuf bb_common_bufsiz1
			ssize_t len;
			// read from device -> write to stdout
			len = safe_read(sfd, iobuf, sizeof(iobuf));
			if (len > 0)
				full_write(STDOUT_FILENO, iobuf, len);
			else {
				// EOF/error -> bail out
				signalled = SIGHUP;
				break;
			}
		}
	}

	// restore device mode
	tcsetattr(sfd, TCSAFLUSH, &tiosfd);

	if (isatty(STDIN_FILENO))
		tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);

done:
	if (device_lock_file)
		unlink(device_lock_file);

	return signalled;
}
Exemple #20
0
int passwd_main(int argc UNUSED_PARAM, char **argv)
{
	enum {
		OPT_algo   = (1 << 0), /* -a - password algorithm */
		OPT_lock   = (1 << 1), /* -l - lock account */
		OPT_unlock = (1 << 2), /* -u - unlock account */
		OPT_delete = (1 << 3), /* -d - delete password */
		OPT_lud    = OPT_lock | OPT_unlock | OPT_delete,
	};
	unsigned opt;
	int rc;
	const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
	const char *filename;
	char *myname;
	char *name;
	char *newp;
	struct passwd *pw;
	uid_t myuid;
	struct rlimit rlimit_fsize;
	char c;
#if ENABLE_FEATURE_SHADOWPASSWDS
	/* Using _r function to avoid pulling in static buffers */
	struct spwd spw;
	char buffer[256];
#endif

	logmode = LOGMODE_BOTH;
	openlog(applet_name, 0, LOG_AUTH);
	opt = getopt32(argv, "a:lud", &opt_a);
	//argc -= optind;
	argv += optind;

	myuid = getuid();
	/* -l, -u, -d require root priv and username argument */
	if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
		bb_show_usage();

	/* Will complain and die if username not found */
	myname = xstrdup(xuid2uname(myuid));
	name = argv[0] ? argv[0] : myname;

	pw = xgetpwnam(name);
	if (myuid != 0 && pw->pw_uid != myuid) {
		/* LOGMODE_BOTH */
		bb_error_msg_and_die("%s can't change password for %s", myname, name);
	}

#if ENABLE_FEATURE_SHADOWPASSWDS
	{
		/* getspnam_r may return 0 yet set result to NULL.
		 * At least glibc 2.4 does this. Be extra paranoid here. */
		struct spwd *result = NULL;
		errno = 0;
		if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
		 || !result /* no error, but no record found either */
		 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
		) {
			if (errno != ENOENT) {
				/* LOGMODE_BOTH */
				bb_perror_msg("no record of %s in %s, using %s",
					name, bb_path_shadow_file,
					bb_path_passwd_file);
			}
			/* else: /etc/shadow does not exist,
			 * apparently we are on a shadow-less system,
			 * no surprise there */
		} else {
			pw->pw_passwd = result->sp_pwdp;
		}
	}
#endif

	/* Decide what the new password will be */
	newp = NULL;
	c = pw->pw_passwd[0] - '!';
	if (!(opt & OPT_lud)) {
		if (myuid != 0 && !c) { /* passwd starts with '!' */
			/* LOGMODE_BOTH */
			bb_error_msg_and_die("can't change "
					"locked password for %s", name);
		}
		printf("Changing password for %s\n", name);
		newp = new_password(pw, myuid, opt_a);
		if (!newp) {
			logmode = LOGMODE_STDIO;
			bb_error_msg_and_die("password for %s is unchanged", name);
		}
	} else if (opt & OPT_lock) {
		if (!c)
			goto skip; /* passwd starts with '!' */
		newp = xasprintf("!%s", pw->pw_passwd);
	} else if (opt & OPT_unlock) {
		if (c)
			goto skip; /* not '!' */
		/* pw->pw_passwd points to static storage,
		 * strdup'ing to avoid nasty surprizes */
		newp = xstrdup(&pw->pw_passwd[1]);
	} else if (opt & OPT_delete) {
		newp = (char*)"";
	}

	rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
	setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
	bb_signals(0
		+ (1 << SIGHUP)
		+ (1 << SIGINT)
		+ (1 << SIGQUIT)
		, SIG_IGN);
	umask(077);
	xsetuid(0);

#if ENABLE_FEATURE_SHADOWPASSWDS
	filename = bb_path_shadow_file;
	rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
	if (rc > 0)
		/* password in /etc/shadow was updated */
		newp = (char*) "x";
	if (rc >= 0)
		/* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
#endif
	{
		filename = bb_path_passwd_file;
		rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
	}
	/* LOGMODE_BOTH */
	if (rc < 0)
		bb_error_msg_and_die("can't update password file %s", filename);
	bb_error_msg("password for %s changed by %s", name, myname);

	/*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
 skip:
	if (!newp) {
		bb_error_msg_and_die("password for %s is already %slocked",
			name, (opt & OPT_unlock) ? "un" : "");
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		free(myname);
	return 0;
}
Exemple #21
0
int zcip_main(int argc UNUSED_PARAM, char **argv)
{
	int state;
	char *r_opt;
	unsigned opts;

	// ugly trick, but I want these zeroed in one go
	struct {
		const struct in_addr null_ip;
		const struct ether_addr null_addr;
		struct in_addr ip;
		struct ifreq ifr;
		int timeout_ms; /* must be signed */
		unsigned conflicts;
		unsigned nprobes;
		unsigned nclaims;
		int ready;
	} L;
#define null_ip    (L.null_ip   )
#define null_addr  (L.null_addr )
#define ip         (L.ip        )
#define ifr        (L.ifr       )
#define timeout_ms (L.timeout_ms)
#define conflicts  (L.conflicts )
#define nprobes    (L.nprobes   )
#define nclaims    (L.nclaims   )
#define ready      (L.ready     )

	memset(&L, 0, sizeof(L));
	INIT_G();

#define FOREGROUND (opts & 1)
#define QUIT       (opts & 2)
	// parse commandline: prog [options] ifname script
	// exactly 2 args; -v accumulates and implies -f
	opt_complementary = "=2:vv:vf";
	opts = getopt32(argv, "fqr:p:v", &r_opt, &pidfile, &verbose);
#if !BB_MMU
	// on NOMMU reexec early (or else we will rerun things twice)
	if (!FOREGROUND)
		bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
#endif
	// open an ARP socket
	// (need to do it before openlog to prevent openlog from taking
	// fd 3 (sock_fd==3))
	xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
	if (!FOREGROUND) {
		// do it before all bb_xx_msg calls
		openlog(applet_name, 0, LOG_DAEMON);
		logmode |= LOGMODE_SYSLOG;
	}
	if (opts & 4) { // -r n.n.n.n
		if (inet_aton(r_opt, &ip) == 0
		 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
		) {
			bb_error_msg_and_die("invalid link address");
		}
	}
	argv += optind - 1;

	/* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */
	/* We need to make space for script argument: */
	argv[0] = argv[1];
	argv[1] = argv[2];
	/* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */
#define argv_intf (argv[0])

	xsetenv("interface", argv_intf);

	// initialize the interface (modprobe, ifup, etc)
	if (run(argv, "init", NULL))
		return EXIT_FAILURE;

	// initialize saddr
	// saddr is: { u16 sa_family; u8 sa_data[14]; }
	//memset(&saddr, 0, sizeof(saddr));
	//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
	safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data));

	// bind to the interface's ARP socket
	xbind(sock_fd, &saddr, sizeof(saddr));

	// get the interface's ethernet address
	//memset(&ifr, 0, sizeof(ifr));
	strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
	xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
	memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);

	// start with some stable ip address, either a function of
	// the hardware address or else the last address we used.
	// we are taking low-order four bytes, as top-order ones
	// aren't random enough.
	// NOTE: the sequence of addresses we try changes only
	// depending on when we detect conflicts.
	{
		uint32_t t;
		move_from_unaligned32(t, ((char *)&eth_addr + 2));
		srand(t);
	}
	if (ip.s_addr == 0)
		ip.s_addr = pick();

	// FIXME cases to handle:
	//  - zcip already running!
	//  - link already has local address... just defend/update

	// daemonize now; don't delay system startup
	if (!FOREGROUND) {
#if BB_MMU
		bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
#endif
		if (verbose)
			bb_info_msg("start, interface %s", argv_intf);
	}

	write_pidfile(pidfile);
	bb_signals(BB_FATAL_SIGS, cleanup);

	// run the dynamic address negotiation protocol,
	// restarting after address conflicts:
	//  - start with some address we want to try
	//  - short random delay
	//  - arp probes to see if another host uses it
	//  - arp announcements that we're claiming it
	//  - use it
	//  - defend it, within limits
	// exit if:
	// - address is successfully obtained and -q was given:
	//   run "<script> config", then exit with exitcode 0
	// - poll error (when does this happen?)
	// - read error (when does this happen?)
	// - sendto error (in arp()) (when does this happen?)
	// - revents & POLLERR (link down). run "<script> deconfig" first
	state = PROBE;
	while (1) {
		struct pollfd fds[1];
		unsigned deadline_us;
		struct arp_packet p;
		int source_ip_conflict;
		int target_ip_conflict;

		fds[0].fd = sock_fd;
		fds[0].events = POLLIN;
		fds[0].revents = 0;

		// poll, being ready to adjust current timeout
		if (!timeout_ms) {
			timeout_ms = random_delay_ms(PROBE_WAIT);
			// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
			// make the kernel filter out all packets except
			// ones we'd care about.
		}
		// set deadline_us to the point in time when we timeout
		deadline_us = MONOTONIC_US() + timeout_ms * 1000;

		VDBG("...wait %d %s nprobes=%u, nclaims=%u\n",
				timeout_ms, argv_intf, nprobes, nclaims);

		switch (safe_poll(fds, 1, timeout_ms)) {

		default:
			//bb_perror_msg("poll"); - done in safe_poll
			cleanup(EXIT_FAILURE);

		// timeout
		case 0:
			VDBG("state = %d\n", state);
			switch (state) {
			case PROBE:
				// timeouts in the PROBE state mean no conflicting ARP packets
				// have been received, so we can progress through the states
				if (nprobes < PROBE_NUM) {
					nprobes++;
					VDBG("probe/%u %s@%s\n",
							nprobes, argv_intf, inet_ntoa(ip));
					arp(/* ARPOP_REQUEST, */
							/* &eth_addr, */ null_ip,
							&null_addr, ip);
					timeout_ms = PROBE_MIN * 1000;
					timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
				}
				else {
					// Switch to announce state.
					state = ANNOUNCE;
					nclaims = 0;
					VDBG("announce/%u %s@%s\n",
							nclaims, argv_intf, inet_ntoa(ip));
					arp(/* ARPOP_REQUEST, */
							/* &eth_addr, */ ip,
							&eth_addr, ip);
					timeout_ms = ANNOUNCE_INTERVAL * 1000;
				}
				break;
			case RATE_LIMIT_PROBE:
				// timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
				// have been received, so we can move immediately to the announce state
				state = ANNOUNCE;
				nclaims = 0;
				VDBG("announce/%u %s@%s\n",
						nclaims, argv_intf, inet_ntoa(ip));
				arp(/* ARPOP_REQUEST, */
						/* &eth_addr, */ ip,
						&eth_addr, ip);
				timeout_ms = ANNOUNCE_INTERVAL * 1000;
				break;
			case ANNOUNCE:
				// timeouts in the ANNOUNCE state mean no conflicting ARP packets
				// have been received, so we can progress through the states
				if (nclaims < ANNOUNCE_NUM) {
					nclaims++;
					VDBG("announce/%u %s@%s\n",
							nclaims, argv_intf, inet_ntoa(ip));
					arp(/* ARPOP_REQUEST, */
							/* &eth_addr, */ ip,
							&eth_addr, ip);
					timeout_ms = ANNOUNCE_INTERVAL * 1000;
				}
				else {
					// Switch to monitor state.
					state = MONITOR;
					// link is ok to use earlier
					// FIXME update filters
					run(argv, "config", &ip);
					ready = 1;
					conflicts = 0;
					timeout_ms = -1; // Never timeout in the monitor state.

					// NOTE: all other exit paths
					// should deconfig ...
					if (QUIT)
						cleanup(EXIT_SUCCESS);
				}
				break;
			case DEFEND:
				// We won!  No ARP replies, so just go back to monitor.
				state = MONITOR;
				timeout_ms = -1;
				conflicts = 0;
				break;
			default:
				// Invalid, should never happen.  Restart the whole protocol.
				state = PROBE;
				ip.s_addr = pick();
				timeout_ms = 0;
				nprobes = 0;
				nclaims = 0;
				break;
			} // switch (state)
			break; // case 0 (timeout)

		// packets arriving, or link went down
		case 1:
			// We need to adjust the timeout in case we didn't receive
			// a conflicting packet.
			if (timeout_ms > 0) {
				unsigned diff = deadline_us - MONOTONIC_US();
				if ((int)(diff) < 0) {
					// Current time is greater than the expected timeout time.
					// Should never happen.
					VDBG("missed an expected timeout\n");
					timeout_ms = 0;
				} else {
					VDBG("adjusting timeout\n");
					timeout_ms = (diff / 1000) | 1; /* never 0 */
				}
			}

			if ((fds[0].revents & POLLIN) == 0) {
				if (fds[0].revents & POLLERR) {
					// FIXME: links routinely go down;
					// this shouldn't necessarily exit.
					bb_error_msg("iface %s is down", argv_intf);
					if (ready) {
						run(argv, "deconfig", &ip);
					}
					cleanup(EXIT_FAILURE);
				}
				continue;
			}

			// read ARP packet
			if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
				bb_perror_msg(bb_msg_read_error);
				cleanup(EXIT_FAILURE);
			}
			if (p.eth.ether_type != htons(ETHERTYPE_ARP))
				continue;
#ifdef DEBUG
			{
				struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
				struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
				struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
				struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
				VDBG("%s recv arp type=%d, op=%d,\n",
					argv_intf, ntohs(p.eth.ether_type),
					ntohs(p.arp.arp_op));
				VDBG("\tsource=%s %s\n",
					ether_ntoa(sha),
					inet_ntoa(*spa));
				VDBG("\ttarget=%s %s\n",
					ether_ntoa(tha),
					inet_ntoa(*tpa));
			}
#endif
			if (p.arp.arp_op != htons(ARPOP_REQUEST)
			 && p.arp.arp_op != htons(ARPOP_REPLY))
				continue;

			source_ip_conflict = 0;
			target_ip_conflict = 0;

			if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0
			 && memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0
			) {
				source_ip_conflict = 1;
			}
			if (p.arp.arp_op == htons(ARPOP_REQUEST)
			 && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
			 && memcmp(&p.arp.arp_tha, &eth_addr, ETH_ALEN) != 0
			) {
				target_ip_conflict = 1;
			}

			VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
				state, source_ip_conflict, target_ip_conflict);
			switch (state) {
			case PROBE:
			case ANNOUNCE:
				// When probing or announcing, check for source IP conflicts
				// and other hosts doing ARP probes (target IP conflicts).
				if (source_ip_conflict || target_ip_conflict) {
					conflicts++;
					if (conflicts >= MAX_CONFLICTS) {
						VDBG("%s ratelimit\n", argv_intf);
						timeout_ms = RATE_LIMIT_INTERVAL * 1000;
						state = RATE_LIMIT_PROBE;
					}

					// restart the whole protocol
					ip.s_addr = pick();
					timeout_ms = 0;
					nprobes = 0;
					nclaims = 0;
				}
				break;
			case MONITOR:
				// If a conflict, we try to defend with a single ARP probe.
				if (source_ip_conflict) {
					VDBG("monitor conflict -- defending\n");
					state = DEFEND;
					timeout_ms = DEFEND_INTERVAL * 1000;
					arp(/* ARPOP_REQUEST, */
						/* &eth_addr, */ ip,
						&eth_addr, ip);
				}
				break;
			case DEFEND:
				// Well, we tried.  Start over (on conflict).
				if (source_ip_conflict) {
					state = PROBE;
					VDBG("defend conflict -- starting over\n");
					ready = 0;
					run(argv, "deconfig", &ip);

					// restart the whole protocol
					ip.s_addr = pick();
					timeout_ms = 0;
					nprobes = 0;
					nclaims = 0;
				}
				break;
			default:
				// Invalid, should never happen.  Restart the whole protocol.
				VDBG("invalid state -- starting over\n");
				state = PROBE;
				ip.s_addr = pick();
				timeout_ms = 0;
				nprobes = 0;
				nclaims = 0;
				break;
			} // switch state
			break; // case 1 (packets arriving)
		} // switch poll
	} // while (1)
#undef argv_intf
}
int chat_main(int argc UNUSED_PARAM, char **argv)
{
	int record_fd = -1;
	bool echo = 0;
	// collection of device replies which cause unconditional termination
	llist_t *aborts = NULL;
	// inactivity period
	int timeout = DEFAULT_CHAT_TIMEOUT;
	// maximum length of abort string
#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
	size_t max_abort_len = 0;
#else
#define max_abort_len MAX_ABORT_LEN
#endif
#if ENABLE_FEATURE_CHAT_TTY_HIFI
	struct termios tio0, tio;
#endif
	// directive names
	enum {
		DIR_HANGUP = 0,
		DIR_ABORT,
#if ENABLE_FEATURE_CHAT_CLR_ABORT
		DIR_CLR_ABORT,
#endif
		DIR_TIMEOUT,
		DIR_ECHO,
		DIR_SAY,
		DIR_RECORD,
	};

	// make x* functions fail with correct exitcode
	xfunc_error_retval = ERR_IO;

	// trap vanilla signals to prevent process from being killed suddenly
	bb_signals(0
		+ (1 << SIGHUP)
		+ (1 << SIGINT)
		+ (1 << SIGTERM)
		+ (1 << SIGPIPE)
		, signal_handler);

#if ENABLE_FEATURE_CHAT_TTY_HIFI
	tcgetattr(STDIN_FILENO, &tio);
	tio0 = tio;
	cfmakeraw(&tio);
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
#endif

#if ENABLE_FEATURE_CHAT_SWALLOW_OPTS
	getopt32(argv, "vVsSE");
	argv += optind;
#else
	argv++; // goto first arg
#endif
	// handle chat expect-send pairs
	while (*argv) {
		// directive given? process it
		int key = index_in_strings(
			"HANGUP\0" "ABORT\0"
#if ENABLE_FEATURE_CHAT_CLR_ABORT
			"CLR_ABORT\0"
#endif
			"TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0"
			, *argv
		);
		if (key >= 0) {
			// cache directive value
			char *arg = *++argv;
			// OFF -> 0, anything else -> 1
			bool onoff = (0 != strcmp("OFF", arg));
			// process directive
			if (DIR_HANGUP == key) {
				// turn SIGHUP on/off
				signal(SIGHUP, onoff ? signal_handler : SIG_IGN);
			} else if (DIR_ABORT == key) {
				// append the string to abort conditions
#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
				size_t len = strlen(arg);
				if (len > max_abort_len)
					max_abort_len = len;
#endif
				llist_add_to_end(&aborts, arg);
#if ENABLE_FEATURE_CHAT_CLR_ABORT
			} else if (DIR_CLR_ABORT == key) {
				// remove the string from abort conditions
				// N.B. gotta refresh maximum length too...
#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
				max_abort_len = 0;
#endif
				for (llist_t *l = aborts; l; l = l->link) {
#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
					size_t len = strlen(l->data);
#endif
					if (!strcmp(arg, l->data)) {
						llist_unlink(&aborts, l);
						continue;
					}
#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
					if (len > max_abort_len)
						max_abort_len = len;
#endif
				}
#endif
			} else if (DIR_TIMEOUT == key) {
				// set new timeout
				// -1 means OFF
				timeout = atoi(arg) * 1000;
				// 0 means default
				// >0 means value in msecs
				if (!timeout)
					timeout = DEFAULT_CHAT_TIMEOUT;
			} else if (DIR_ECHO == key) {
				// turn echo on/off
				// N.B. echo means dumping device input/output to stderr
				echo = onoff;
			} else if (DIR_RECORD == key) {
				// turn record on/off
				// N.B. record means dumping device input to a file
					// close previous record_fd
				if (record_fd > 0)
					close(record_fd);
				// N.B. do we have to die here on open error?
				record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1;
			} else if (DIR_SAY == key) {
				// just print argument verbatim
				// TODO: should we use full_write() to avoid unistd/stdio conflict?
				bb_error_msg("%s", arg);
			}
			// next, please!
			argv++;
		// ordinary expect-send pair!
		} else {
			//-----------------------
			// do expect
			//-----------------------
			int expect_len;
			size_t buf_len = 0;
			size_t max_len = max_abort_len;

			struct pollfd pfd;
#if ENABLE_FEATURE_CHAT_NOFAIL
			int nofail = 0;
#endif
			char *expect = *argv++;

			// sanity check: shall we really expect something?
			if (!expect)
				goto expect_done;

#if ENABLE_FEATURE_CHAT_NOFAIL
			// if expect starts with -
			if ('-' == *expect) {
				// swallow -
				expect++;
				// and enter nofail mode
				nofail++;
			}
#endif

#ifdef ___TEST___BUF___ // test behaviour with a small buffer
#	undef COMMON_BUFSIZE
#	define COMMON_BUFSIZE 6
#endif
			// expand escape sequences in expect
			expect_len = unescape(expect, &expect_len /*dummy*/);
			if (expect_len > max_len)
				max_len = expect_len;
			// sanity check:
			// we should expect more than nothing but not more than input buffer
			// TODO: later we'll get rid of fixed-size buffer
			if (!expect_len)
				goto expect_done;
			if (max_len >= COMMON_BUFSIZE) {
				exitcode = ERR_MEM;
				goto expect_done;
			}

			// get reply
			pfd.fd = STDIN_FILENO;
			pfd.events = POLLIN;
			while (!exitcode
			    && poll(&pfd, 1, timeout) > 0
			    && (pfd.revents & POLLIN)
			) {
#define buf bb_common_bufsiz1
				llist_t *l;
				ssize_t delta;

				// read next char from device
				if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) {
					// dump device input if RECORD fname
					if (record_fd > 0) {
						full_write(record_fd, buf+buf_len, 1);
					}
					// dump device input if ECHO ON
					if (echo > 0) {
//						if (buf[buf_len] < ' ') {
//							full_write(STDERR_FILENO, "^", 1);
//							buf[buf_len] += '@';
//						}
						full_write(STDERR_FILENO, buf+buf_len, 1);
					}
					buf_len++;
					// move input frame if we've reached higher bound
					if (buf_len > COMMON_BUFSIZE) {
						memmove(buf, buf+buf_len-max_len, max_len);
						buf_len = max_len;
					}
				}
				// N.B. rule of thumb: values being looked for can
				// be found only at the end of input buffer
				// this allows to get rid of strstr() and memmem()

				// TODO: make expect and abort strings processed uniformly
				// abort condition is met? -> bail out
				for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) {
					size_t len = strlen(l->data);
					delta = buf_len-len;
					if (delta >= 0 && !memcmp(buf+delta, l->data, len))
						goto expect_done;
				}
				exitcode = ERR_OK;

				// expected reply received? -> goto next command
				delta = buf_len - expect_len;
				if (delta >= 0 && !memcmp(buf+delta, expect, expect_len))
					goto expect_done;
#undef buf
			} /* while (have data) */

			// device timed out or unexpected reply received
			exitcode = ERR_TIMEOUT;
 expect_done:
#if ENABLE_FEATURE_CHAT_NOFAIL
			// on success and when in nofail mode
			// we should skip following subsend-subexpect pairs
			if (nofail) {
				if (!exitcode) {
					// find last send before non-dashed expect
					while (*argv && argv[1] && '-' == argv[1][0])
						argv += 2;
					// skip the pair
					// N.B. do we really need this?!
					if (!*argv++ || !*argv++)
						break;
				}
				// nofail mode also clears all but IO errors (or signals)
				if (ERR_IO != exitcode)
					exitcode = ERR_OK;
			}
#endif
			// bail out unless we expected successfully
			if (exitcode)
				break;

			//-----------------------
			// do send
			//-----------------------
			if (*argv) {
#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
				int nocr = 0; // inhibit terminating command with \r
#endif
				char *loaded = NULL; // loaded command
				size_t len;
				char *buf = *argv++;

				// if command starts with @
				// load "real" command from file named after @
				if ('@' == *buf) {
					// skip the @ and any following white-space
					trim(++buf);
					buf = loaded = xmalloc_xopen_read_close(buf, NULL);
				}
				// expand escape sequences in command
				len = unescape(buf, &nocr);

				// send command
				alarm(timeout);
				pfd.fd = STDOUT_FILENO;
				pfd.events = POLLOUT;
				while (len && !exitcode
				    && poll(&pfd, 1, -1) > 0
				    && (pfd.revents & POLLOUT)
				) {
#if ENABLE_FEATURE_CHAT_SEND_ESCAPES
					// "\\d" means 1 sec delay, "\\p" means 0.01 sec delay
					// "\\K" means send BREAK
					char c = *buf;
					if ('\\' == c) {
						c = *++buf;
						if ('d' == c) {
							sleep(1);
							len--;
							continue;
						}
						if ('p' == c) {
							usleep(10000);
							len--;
							continue;
						}
						if ('K' == c) {
							tcsendbreak(STDOUT_FILENO, 0);
							len--;
							continue;
						}
						buf--;
					}
					if (safe_write(STDOUT_FILENO, buf, 1) != 1)
						break;
					len--;
					buf++;
#else
					len -= full_write(STDOUT_FILENO, buf, len);
#endif
				} /* while (can write) */
				alarm(0);

				// report I/O error if there still exists at least one non-sent char
				if (len)
					exitcode = ERR_IO;

				// free loaded command (if any)
				if (loaded)
					free(loaded);
#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
				// or terminate command with \r (if not inhibited)
				else if (!nocr)
					xwrite(STDOUT_FILENO, "\r", 1);
#endif
				// bail out unless we sent command successfully
				if (exitcode)
					break;
			} /* if (*argv) */
		}
	} /* while (*argv) */

#if ENABLE_FEATURE_CHAT_TTY_HIFI
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
#endif

	return exitcode;
}
Exemple #23
0
int less_main(int argc, char **argv)
{
	int keypress;

	INIT_G();

	/* TODO: -x: do not interpret backspace, -xx: tab also */
	/* -xxx: newline also */
	/* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
	getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S"));
	argc -= optind;
	argv += optind;
	num_files = argc;
	files = argv;

	/* Another popular pager, most, detects when stdout
	 * is not a tty and turns into cat. This makes sense. */
	if (!isatty(STDOUT_FILENO))
		return bb_cat(argv);

	if (!num_files) {
		if (isatty(STDIN_FILENO)) {
			/* Just "less"? No args and no redirection? */
			bb_error_msg("missing filename");
			bb_show_usage();
		}
	} else {
		filename = xstrdup(files[0]);
	}

	if (option_mask32 & FLAG_TILDE)
		empty_line_marker = "";

	kbd_fd = open(CURRENT_TTY, O_RDONLY);
	if (kbd_fd < 0)
		return bb_cat(argv);
	ndelay_on(kbd_fd);

	tcgetattr(kbd_fd, &term_orig);
	term_less = term_orig;
	term_less.c_lflag &= ~(ICANON | ECHO);
	term_less.c_iflag &= ~(IXON | ICRNL);
	/*term_less.c_oflag &= ~ONLCR;*/
	term_less.c_cc[VMIN] = 1;
	term_less.c_cc[VTIME] = 0;

	get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
	/* 20: two tabstops + 4 */
	if (width < 20 || max_displayed_line < 3)
		return bb_cat(argv);
	max_displayed_line -= 2;

	/* We want to restore term_orig on exit */
	bb_signals(BB_FATAL_SIGS, sig_catcher);
#if ENABLE_FEATURE_LESS_WINCH
	signal(SIGWINCH, sigwinch_handler);
#endif

	buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
	reinitialize();
	while (1) {
#if ENABLE_FEATURE_LESS_WINCH
		while (WINCH_COUNTER) {
 again:
			winch_counter--;
			get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
			/* 20: two tabstops + 4 */
			if (width < 20)
				width = 20;
			if (max_displayed_line < 3)
				max_displayed_line = 3;
			max_displayed_line -= 2;
			free(buffer);
			buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
			/* Avoid re-wrap and/or redraw if we already know
			 * we need to do it again. These ops are expensive */
			if (WINCH_COUNTER)
				goto again;
			re_wrap();
			if (WINCH_COUNTER)
				goto again;
			buffer_fill_and_print();
			/* This took some time. Loop back and check,
			 * were there another SIGWINCH? */
		}
#endif
		keypress = less_getch(-1); /* -1: do not position cursor */
		keypress_process(keypress);
	}
}
Exemple #24
0
int zcip_main(int argc UNUSED_PARAM, char **argv)
{
	char *r_opt;
	const char *l_opt = "169.254.0.0";
	int state;
	int nsent;
	unsigned opts;

	// Ugly trick, but I want these zeroed in one go
	struct {
		const struct ether_addr null_ethaddr;
		struct ifreq ifr;
		uint32_t chosen_nip;
		int conflicts;
		int timeout_ms; // must be signed
	} L;
#define null_ethaddr (L.null_ethaddr)
#define ifr          (L.ifr         )
#define chosen_nip   (L.chosen_nip  )
#define conflicts    (L.conflicts   )
#define timeout_ms   (L.timeout_ms  )

	memset(&L, 0, sizeof(L));
	INIT_G();

#define FOREGROUND (opts & 1)
#define QUIT       (opts & 2)
	// Parse commandline: prog [options] ifname script
	// exactly 2 args; -v accumulates and implies -f
	opt_complementary = "=2:vv:vf";
	opts = getopt32(argv, "fqr:l:p:v", &r_opt, &l_opt, &G.pidfile, &G.verbose);
#if !BB_MMU
	// on NOMMU reexec early (or else we will rerun things twice)
	if (!FOREGROUND)
		bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
#endif
	// Open an ARP socket
	// (need to do it before openlog to prevent openlog from taking
	// fd 3 (sock_fd==3))
	xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
	if (!FOREGROUND) {
		// do it before all bb_xx_msg calls
		openlog(applet_name, 0, LOG_DAEMON);
		logmode |= LOGMODE_SYSLOG;
	}
	bb_logenv_override();

	{ // -l n.n.n.n
		struct in_addr net;
		if (inet_aton(l_opt, &net) == 0
		 || (net.s_addr & htonl(IN_CLASSB_NET)) != net.s_addr
		) {
			bb_error_msg_and_die("invalid network address");
		}
		G.localnet_ip = ntohl(net.s_addr);
	}
	if (opts & 4) { // -r n.n.n.n
		struct in_addr ip;
		if (inet_aton(r_opt, &ip) == 0
		 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != G.localnet_ip
		) {
			bb_error_msg_and_die("invalid link address");
		}
		chosen_nip = ip.s_addr;
	}
	argv += optind - 1;

	/* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */
	/* We need to make space for script argument: */
	argv[0] = argv[1];
	argv[1] = argv[2];
	/* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */
#define argv_intf (argv[0])

	xsetenv("interface", argv_intf);

	// Initialize the interface (modprobe, ifup, etc)
	if (run(argv, "init", 0))
		return EXIT_FAILURE;

	// Initialize G.iface_sockaddr
	// G.iface_sockaddr is: { u16 sa_family; u8 sa_data[14]; }
	//memset(&G.iface_sockaddr, 0, sizeof(G.iface_sockaddr));
	//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
	safe_strncpy(G.iface_sockaddr.sa_data, argv_intf, sizeof(G.iface_sockaddr.sa_data));

	// Bind to the interface's ARP socket
	xbind(sock_fd, &G.iface_sockaddr, sizeof(G.iface_sockaddr));

	// Get the interface's ethernet address
	//memset(&ifr, 0, sizeof(ifr));
	strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
	xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
	memcpy(&G.our_ethaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);

	// Start with some stable ip address, either a function of
	// the hardware address or else the last address we used.
	// we are taking low-order four bytes, as top-order ones
	// aren't random enough.
	// NOTE: the sequence of addresses we try changes only
	// depending on when we detect conflicts.
	{
		uint32_t t;
		move_from_unaligned32(t, ((char *)&G.our_ethaddr + 2));
		srand(t);
	}
	// FIXME cases to handle:
	//  - zcip already running!
	//  - link already has local address... just defend/update

	// Daemonize now; don't delay system startup
	if (!FOREGROUND) {
#if BB_MMU
		bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
#endif
		if (G.verbose)
			bb_info_msg("start, interface %s", argv_intf);
	}

	write_pidfile(G.pidfile);
	die_func = cleanup;
	bb_signals(BB_FATAL_SIGS, term_handler);

	// Run the dynamic address negotiation protocol,
	// restarting after address conflicts:
	//  - start with some address we want to try
	//  - short random delay
	//  - arp probes to see if another host uses it
	//    00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 tell 0.0.0.0
	//  - arp announcements that we're claiming it
	//    00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 (00:04:e2:64:23:c2) tell 169.254.194.171
	//  - use it
	//  - defend it, within limits
	// exit if:
	// - address is successfully obtained and -q was given:
	//   run "<script> config", then exit with exitcode 0
	// - poll error (when does this happen?)
	// - read error (when does this happen?)
	// - sendto error (in send_arp_request()) (when does this happen?)
	// - revents & POLLERR (link down). run "<script> deconfig" first
	if (chosen_nip == 0) {
 new_nip_and_PROBE:
		chosen_nip = pick_nip();
	}
	nsent = 0;
	state = PROBE;
	while (1) {
		struct pollfd fds[1];
		unsigned deadline_us;
		struct arp_packet p;
		int ip_conflict;
		int n;

		fds[0].fd = sock_fd;
		fds[0].events = POLLIN;
		fds[0].revents = 0;

		// Poll, being ready to adjust current timeout
		if (!timeout_ms) {
			timeout_ms = random_delay_ms(PROBE_WAIT);
			// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
			// make the kernel filter out all packets except
			// ones we'd care about.
		}
		// Set deadline_us to the point in time when we timeout
		deadline_us = MONOTONIC_US() + timeout_ms * 1000;

		VDBG("...wait %d %s nsent=%u\n",
				timeout_ms, argv_intf, nsent);

		n = safe_poll(fds, 1, timeout_ms);
		if (n < 0) {
			//bb_perror_msg("poll"); - done in safe_poll
			xfunc_die();
		}
		if (n == 0) { // timed out?
			VDBG("state:%d\n", state);
			switch (state) {
			case PROBE:
				// No conflicting ARP packets were seen:
				// we can progress through the states
				if (nsent < PROBE_NUM) {
					nsent++;
					VDBG("probe/%u %s@%s\n",
							nsent, argv_intf, nip_to_a(chosen_nip));
					timeout_ms = PROBE_MIN * 1000;
					timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
					send_arp_request(0, &null_ethaddr, chosen_nip);
					continue;
				}
  				// Switch to announce state
				nsent = 0;
				state = ANNOUNCE;
				goto send_announce;
			case ANNOUNCE:
				// No conflicting ARP packets were seen:
				// we can progress through the states
				if (nsent < ANNOUNCE_NUM) {
 send_announce:
					nsent++;
					VDBG("announce/%u %s@%s\n",
							nsent, argv_intf, nip_to_a(chosen_nip));
					timeout_ms = ANNOUNCE_INTERVAL * 1000;
					send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
					continue;
				}
				// Switch to monitor state
				// FIXME update filters
				run(argv, "config", chosen_nip);
				// NOTE: all other exit paths should deconfig...
				if (QUIT) {
					xfunc_error_retval = EXIT_SUCCESS;
					xfunc_die();
				}
				// fall through: switch to MONITOR
			default:
			// case DEFEND:
			// case MONITOR: (shouldn't happen, MONITOR timeout is infinite)
				// Defend period ended with no ARP replies - we won
				timeout_ms = -1; // never timeout in monitor state
				state = MONITOR;
				continue;
			}
		}

		// Packet arrived, or link went down.
		// We need to adjust the timeout in case we didn't receive
		// a conflicting packet.
		if (timeout_ms > 0) {
			unsigned diff = deadline_us - MONOTONIC_US();
			if ((int)(diff) < 0) {
				// Current time is greater than the expected timeout time.
				diff = 0;
			}
			VDBG("adjusting timeout\n");
			timeout_ms = (diff / 1000) | 1; // never 0
		}

		if ((fds[0].revents & POLLIN) == 0) {
			if (fds[0].revents & POLLERR) {
				// FIXME: links routinely go down;
				// this shouldn't necessarily exit.
				bb_error_msg("iface %s is down", argv_intf);
				if (state >= MONITOR) {
					// Only if we are in MONITOR or DEFEND
					run(argv, "deconfig", chosen_nip);
				}
				xfunc_die();
			}
			continue;
		}

		// Read ARP packet
		if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
			bb_perror_msg_and_die(bb_msg_read_error);
		}

		if (p.eth.ether_type != htons(ETHERTYPE_ARP))
			continue;
		if (p.arp.arp_op != htons(ARPOP_REQUEST)
		 && p.arp.arp_op != htons(ARPOP_REPLY)
		) {
			continue;
		}
#ifdef DEBUG
		{
			struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
			struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
			struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
			struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
			VDBG("source=%s %s\n", ether_ntoa(sha),	inet_ntoa(*spa));
			VDBG("target=%s %s\n", ether_ntoa(tha),	inet_ntoa(*tpa));
		}
#endif
		ip_conflict = 0;
		if (memcmp(&p.arp.arp_sha, &G.our_ethaddr, ETH_ALEN) != 0) {
			if (memcmp(p.arp.arp_spa, &chosen_nip, 4) == 0) {
				// A probe or reply with source_ip == chosen ip
				ip_conflict = 1;
			}
			if (p.arp.arp_op == htons(ARPOP_REQUEST)
			 && memcmp(p.arp.arp_spa, &const_int_0, 4) == 0
			 && memcmp(p.arp.arp_tpa, &chosen_nip, 4) == 0
			) {
				// A probe with source_ip == 0.0.0.0, target_ip == chosen ip:
				// another host trying to claim this ip!
				ip_conflict |= 2;
			}
		}
		VDBG("state:%d ip_conflict:%d\n", state, ip_conflict);
		if (!ip_conflict)
			continue;

		// Either src or target IP conflict exists
		if (state <= ANNOUNCE) {
			// PROBE or ANNOUNCE
			conflicts++;
			timeout_ms = PROBE_MIN * 1000
				+ CONFLICT_MULTIPLIER * random_delay_ms(conflicts);
			goto new_nip_and_PROBE;
		}

		// MONITOR or DEFEND: only src IP conflict is a problem
		if (ip_conflict & 1) {
			if (state == MONITOR) {
				// Src IP conflict, defend with a single ARP probe
				VDBG("monitor conflict - defending\n");
				timeout_ms = DEFEND_INTERVAL * 1000;
				state = DEFEND;
				send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
				continue;
			}
			// state == DEFEND
			// Another src IP conflict, start over
			VDBG("defend conflict - starting over\n");
			run(argv, "deconfig", chosen_nip);
			conflicts = 0;
			timeout_ms = 0;
			goto new_nip_and_PROBE;
		}
		// Note: if we only have a target IP conflict here (ip_conflict & 2),
		// IOW: if we just saw this sort of ARP packet:
		//  aa:bb:cc:dd:ee:ff > xx:xx:xx:xx:xx:xx arp who-has <chosen_nip> tell 0.0.0.0
		// we expect _kernel_ to respond to that, because <chosen_nip>
		// is (expected to be) configured on this iface.
	} // while (1)
#undef argv_intf
}
Exemple #25
0
int inotifyd_main(int argc, char **argv)
{
	int n;
	unsigned mask;
	struct pollfd pfd;
	char **watches; // names of files being watched
	const char *args[5];

	// sanity check: agent and at least one watch must be given
	if (!argv[1] || !argv[2])
		bb_show_usage();

	argv++;
	// inotify_add_watch will number watched files
	// starting from 1, thus watches[0] is unimportant,
	// and 1st file name is watches[1].
	watches = argv;
	args[0] = *argv;
	args[4] = NULL;
	argc -= 2; // number of files we watch

	// open inotify
	pfd.fd = inotify_init();
	if (pfd.fd < 0)
		bb_perror_msg_and_die("no kernel support");

	// setup watches
	while (*++argv) {
		char *path = *argv;
		char *masks = strchr(path, ':');

		mask = 0x0fff; // assuming we want all non-kernel events
		// if mask is specified ->
		if (masks) {
			*masks = '\0'; // split path and mask
			// convert mask names to mask bitset
			mask = 0;
			while (*++masks) {
				const char *found;
				found = memchr(mask_names, *masks, MASK_BITS);
				if (found)
					mask |= (1 << (found - mask_names));
			}
		}
		// add watch
		n = inotify_add_watch(pfd.fd, path, mask);
		if (n < 0)
			bb_perror_msg_and_die("add watch (%s) failed", path);
		//bb_error_msg("added %d [%s]:%4X", n, path, mask);
	}

	// setup signals
	bb_signals(BB_FATAL_SIGS, record_signo);

	// do watch
	pfd.events = POLLIN;
	while (1) {
		int len;
		void *buf;
		struct inotify_event *ie;
 again:
		if (bb_got_signal)
			break;
		n = poll(&pfd, 1, -1);
		// Signal interrupted us?
		if (n < 0 && errno == EINTR)
			goto again;
		// Under Linux, above if() is not necessary.
		// Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
		// are not interrupting poll().
		// Thus we can just break if n <= 0 (see below),
		// because EINTR will happen only on SIGTERM et al.
		// But this might be not true under other Unixes,
		// and is generally way too subtle to depend on.
		if (n <= 0) // strange error?
			break;

		// read out all pending events
		// (NB: len must be int, not ssize_t or long!)
#define eventbuf bb_common_bufsiz1
		setup_common_bufsiz();
		xioctl(pfd.fd, FIONREAD, &len);
		ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len);
		len = full_read(pfd.fd, buf, len);
		// process events. N.B. events may vary in length
		while (len > 0) {
			int i;
			// cache relevant events mask
			unsigned m = ie->mask & ((1 << MASK_BITS) - 1);
			if (m) {
				char events[MASK_BITS + 1];
				char *s = events;
				for (i = 0; i < MASK_BITS; ++i, m >>= 1) {
					if ((m & 1) && (mask_names[i] != '\0'))
						*s++ = mask_names[i];
				}
				*s = '\0';
				if (LONE_CHAR(args[0], '-')) {
					/* "inotifyd - FILE": built-in echo */
					printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events,
							watches[ie->wd],
							ie->name);
					fflush(stdout);
				} else {
//					bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
//						ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
					args[1] = events;
					args[2] = watches[ie->wd];
					args[3] = ie->len ? ie->name : NULL;
					spawn_and_wait((char **)args);
				}
				// we are done if all files got final x event
				if (ie->mask & 0x8000) {
					if (--argc <= 0)
						goto done;
					inotify_rm_watch(pfd.fd, ie->wd);
				}
			}
			// next event
			i = sizeof(struct inotify_event) + ie->len;
			len -= i;
			ie = (void*)((char*)ie + i);
		}
		if (eventbuf != buf)
			free(buf);
	} // while (1)
 done:
	return bb_got_signal;
}
Exemple #26
0
int acpid_main(int argc UNUSED_PARAM, char **argv)
{
	int nfd;
	int opts;
	struct pollfd *pfd;
	const char *opt_dir = "/etc/acpi";
	const char *opt_input = "/dev/input/event";
	const char *opt_logfile = "/var/log/acpid.log";
	const char *opt_action = "/etc/acpid.conf";
	const char *opt_map = "/etc/acpi.map";
#if ENABLE_FEATURE_PIDFILE
	const char *opt_pidfile = "/var/run/acpid.pid";
#endif

	INIT_G();

	opt_complementary = "df:e--e";
	opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
		&opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
		IF_FEATURE_PIDFILE(, &opt_pidfile)
		IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
	);

	if (!(opts & OPT_f)) {
		/* No -f "Foreground", we go to background */
		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
	}

	if (!(opts & OPT_d)) {
		/* No -d "Debug", we log to log file.
		 * This includes any output from children.
		 */
		xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
		xdup2(STDOUT_FILENO, STDERR_FILENO);
		/* Also, acpid's messages (but not children) will go to syslog too */
		openlog(applet_name, LOG_PID, LOG_DAEMON);
		logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
	}
	/* else: -d "Debug", log is not redirected */

	parse_conf_file(opt_action);
	parse_map_file(opt_map);

	xchdir(opt_dir);

	bb_signals((1 << SIGCHLD), SIG_IGN);
	bb_signals(BB_FATAL_SIGS, record_signo);

	pfd = NULL;
	nfd = 0;
	while (1) {
		int fd;
		char *dev_event;

		dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
		fd = open(dev_event, O_RDONLY | O_NONBLOCK);
		if (fd < 0) {
			if (nfd == 0)
				bb_simple_perror_msg_and_die(dev_event);
			break;
		}
		free(dev_event);
		pfd = xrealloc_vector(pfd, 1, nfd);
		pfd[nfd].fd = fd;
		pfd[nfd].events = POLLIN;
		nfd++;
	}

	write_pidfile(opt_pidfile);

	while (safe_poll(pfd, nfd, -1) > 0) {
		int i;
		for (i = 0; i < nfd; i++) {
			const char *event;

			if (!(pfd[i].revents & POLLIN)) {
				if (pfd[i].revents == 0)
					continue; /* this fd has nothing */

				/* Likely POLLERR, POLLHUP, POLLNVAL.
				 * Do not listen on this fd anymore.
				 */
				close(pfd[i].fd);
				nfd--;
				for (; i < nfd; i++)
					pfd[i].fd = pfd[i + 1].fd;
				break; /* do poll() again */
			}

			event = NULL;
			if (option_mask32 & OPT_e) {
				char *buf;
				int len;

				buf = xmalloc_reads(pfd[i].fd, NULL);
				/* buf = "button/power PWRB 00000080 00000000" */
				len = strlen(buf) - 9;
				if (len >= 0)
					buf[len] = '\0';
				event = find_action(NULL, buf);
				free(buf);
			} else {
				struct input_event ev;

				if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
					continue;

				if (ev.value != 1 && ev.value != 0)
					continue;

				event = find_action(&ev, NULL);
			}
			if (!event)
				continue;
			// spawn event handler
			process_event(event);
		}
	}

	if (ENABLE_FEATURE_CLEAN_UP) {
		while (nfd--)
			close(pfd[nfd].fd);
		free(pfd);
	}
	remove_pidfile(opt_pidfile);

	return EXIT_SUCCESS;
}