/* Run command CMD and return statistics on it.
   Put the statistics in *RESP.  */
static void run_command(char *const *cmd, resource_t *resp)
{
	pid_t pid;			/* Pid of child.  */
	void (*interrupt_signal)(int);
	void (*quit_signal)(int);

	resp->elapsed_ms = monotonic_ms();
	pid = vfork();		/* Run CMD as child process.  */
	if (pid < 0)
		bb_perror_msg_and_die("fork");
	if (pid == 0) {	/* If child.  */
		/* Don't cast execvp arguments; that causes errors on some systems,
		   versus merely warnings if the cast is left off.  */
		BB_EXECVP(cmd[0], cmd);
		xfunc_error_retval = (errno == ENOENT ? 127 : 126);
		bb_error_msg_and_die("can't run %s", cmd[0]);
	}

	/* Have signals kill the child but not self (if possible).  */
//TODO: just block all sigs? and reenable them in the very end in main?
	interrupt_signal = signal(SIGINT, SIG_IGN);
	quit_signal = signal(SIGQUIT, SIG_IGN);

	resuse_end(pid, resp);

	/* Re-enable signals.  */
	signal(SIGINT, interrupt_signal);
	signal(SIGQUIT, quit_signal);
}
Exemple #2
0
/*
 * Check expiration for each timer. If a timer is expired,
 * call the expire function for the timer and update the timer.
 * Return the next interval for select() call.
 */
struct timeval *dhcp6_timer_check(void)
{
    unsigned long long now = monotonic_ms();
    struct dhcp6_timer *tm, *tm_next;

    client6_config.tm_sentinel = ULLONG_MAX;
    for (tm = LIST_FIRST(&client6_config.timer_head); tm; tm = tm_next) {
        tm_next = LIST_NEXT(tm, link);

        if ((long long)(now - tm->t) >= 0) {
            if ((*tm->expire)(tm->expire_data) == NULL)
                continue; /* timer has been freed */
        }

        if (tm->t < client6_config.tm_sentinel)
            client6_config.tm_sentinel = tm->t;
    }

    if (ULLONG_MAX == client6_config.tm_sentinel) {
        /* no need to timeout */
        return NULL;
    } else if (client6_config.tm_sentinel < now) {
        /* this may occur when the interval is too small */
        client6_config.tm_check.tv_sec = client6_config.tm_check.tv_usec = 0;
    } else {
        client6_config.tm_check.tv_sec =
            (client6_config.tm_sentinel - now) / 1000ULL;
        client6_config.tm_check.tv_usec =
            ((client6_config.tm_sentinel - now) % 1000ULL) * 1000ULL;
    }
    return (&client6_config.tm_check);
}
Exemple #3
0
void dhcp6_timer_set(unsigned long long t, struct dhcp6_timer *timer)
{
    timer->t = monotonic_ms() + t;

    /* update the next expiration time */
    if (timer->t < client6_config.tm_sentinel)
        client6_config.tm_sentinel = timer->t;
}
Exemple #4
0
unsigned long long dhcp6_timer_rest(struct dhcp6_timer *timer)
{
    unsigned long long now = monotonic_ms();

    if (timer->t - now <= 0) {
        log2("a timer must be expired, but not yet");
        return 0;
    } else {
        return (timer->t - now);
    }
}
Exemple #5
0
/* pid_t is short on BSDI, so don't try to promote it.  */
static void resuse_end(pid_t pid, resource_t *resp)
{
	pid_t caught;

	/* Ignore signals, but don't ignore the children.  When wait3
	   returns the child process, set the time the command finished. */
	while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
		if (caught == -1 && errno != EINTR) {
			bb_perror_msg("wait");
			return;
		}
	}
	resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
}
Exemple #6
0
/* Run command CMD and return statistics on it.
   Put the statistics in *RESP.  */
static void run_command(char *const *cmd, resource_t *resp)
{
	pid_t pid;
	void (*interrupt_signal)(int);
	void (*quit_signal)(int);

	resp->elapsed_ms = monotonic_ms();
	pid = xvfork();
	if (pid == 0) {
		/* Child */
		BB_EXECVP_or_die((char**)cmd);
	}

	/* Have signals kill the child but not self (if possible).  */
//TODO: just block all sigs? and reenable them in the very end in main?
	interrupt_signal = signal(SIGINT, SIG_IGN);
	quit_signal = signal(SIGQUIT, SIG_IGN);

	resuse_end(pid, resp);

	/* Re-enable signals.  */
	signal(SIGINT, interrupt_signal);
	signal(SIGQUIT, quit_signal);
}
//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
//string. hush naturally has it, and ash has setvareq().
//Here we can simply store "VAR=" at buffer start and store read data directly
//after "=", then pass buffer to setvar() to consume.
const char* FAST_FUNC
shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
	char       **argv,
	const char *ifs,
	int        read_flags,
	const char *opt_n,
	const char *opt_p,
	const char *opt_t,
	const char *opt_u
)
{
	unsigned end_ms; /* -t TIMEOUT */
	int fd; /* -u FD */
	int nchars; /* -n NUM */
	char **pp;
	char *buffer;
	struct termios tty, old_tty;
	const char *retval;
	int bufpos; /* need to be able to hold -1 */
	int startword;
	smallint backslash;

	pp = argv;
	while (*pp) {
		if (!is_well_formed_var_name(*pp, '\0')) {
			/* Mimic bash message */
			bb_error_msg("read: '%s': not a valid identifier", *pp);
			return (const char *)(uintptr_t)1;
		}
		pp++;
	}

	nchars = 0; /* if != 0, -n is in effect */
	if (opt_n) {
		nchars = bb_strtou(opt_n, NULL, 10);
		if (nchars < 0 || errno)
			return "invalid count";
		/* note: "-n 0": off (bash 3.2 does this too) */
	}
	end_ms = 0;
	if (opt_t) {
		end_ms = bb_strtou(opt_t, NULL, 10);
		if (errno || end_ms > UINT_MAX / 2048)
			return "invalid timeout";
		end_ms *= 1000;
#if 0 /* even bash has no -t N.NNN support */
		ts.tv_sec = bb_strtou(opt_t, &p, 10);
		ts.tv_usec = 0;
		/* EINVAL means number is ok, but not terminated by NUL */
		if (*p == '.' && errno == EINVAL) {
			char *p2;
			if (*++p) {
				int scale;
				ts.tv_usec = bb_strtou(p, &p2, 10);
				if (errno)
					return "invalid timeout";
				scale = p2 - p;
				/* normalize to usec */
				if (scale > 6)
					return "invalid timeout";
				while (scale++ < 6)
					ts.tv_usec *= 10;
			}
		} else if (ts.tv_sec < 0 || errno) {
			return "invalid timeout";
		}
		if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
			return "invalid timeout";
		}
#endif /* if 0 */
	}
	fd = STDIN_FILENO;
	if (opt_u) {
		fd = bb_strtou(opt_u, NULL, 10);
		if (fd < 0 || errno)
			return "invalid file descriptor";
	}

	if (opt_p && isatty(fd)) {
		fputs(opt_p, stderr);
		fflush_all();
	}

	if (ifs == NULL)
		ifs = defifs;

	if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
		tcgetattr(fd, &tty);
		old_tty = tty;
		if (nchars) {
			tty.c_lflag &= ~ICANON;
			tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
		}
		if (read_flags & BUILTIN_READ_SILENT) {
			tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
		}
		/* This forces execution of "restoring" tcgetattr later */
		read_flags |= BUILTIN_READ_SILENT;
		/* if tcgetattr failed, tcsetattr will fail too.
		 * Ignoring, it's harmless. */
		tcsetattr(fd, TCSANOW, &tty);
	}

	retval = (const char *)(uintptr_t)0;
	startword = 1;
	backslash = 0;
	if (end_ms) /* NB: end_ms stays nonzero: */
		end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
	buffer = NULL;
	bufpos = 0;
	do {
		char c;

		if (end_ms) {
			int timeout;
			struct pollfd pfd[1];

			pfd[0].fd = fd;
			pfd[0].events = POLLIN;
			timeout = end_ms - (unsigned)monotonic_ms();
			if (timeout <= 0 /* already late? */
			 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
			) { /* timed out! */
				retval = (const char *)(uintptr_t)1;
				goto ret;
			}
		}

		if ((bufpos & 0xff) == 0)
			buffer = xrealloc(buffer, bufpos + 0x100);
		if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
			retval = (const char *)(uintptr_t)1;
			break;
		}
		c = buffer[bufpos];
		if (c == '\0')
			continue;
		if (backslash) {
			backslash = 0;
			if (c != '\n')
				goto put;
			continue;
		}
		if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
			backslash = 1;
			continue;
		}
		if (c == '\n')
			break;

		/* $IFS splitting. NOT done if we run "read"
		 * without variable names (bash compat).
		 * Thus, "read" and "read REPLY" are not the same.
		 */
		if (argv[0]) {
/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
			const char *is_ifs = strchr(ifs, c);
			if (startword && is_ifs) {
				if (isspace(c))
					continue;
				/* it is a non-space ifs char */
				startword--;
				if (startword == 1) /* first one? */
					continue; /* yes, it is not next word yet */
			}
			startword = 0;
			if (argv[1] != NULL && is_ifs) {
				buffer[bufpos] = '\0';
				bufpos = 0;
				setvar(*argv, buffer);
				argv++;
				/* can we skip one non-space ifs char? (2: yes) */
				startword = isspace(c) ? 2 : 1;
				continue;
			}
		}
 put:
		bufpos++;
	} while (--nchars);

	if (argv[0]) {
		/* Remove trailing space $IFS chars */
		while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
			continue;
		buffer[bufpos + 1] = '\0';
		/* Use the remainder as a value for the next variable */
		setvar(*argv, buffer);
		/* Set the rest to "" */
		while (*++argv)
			setvar(*argv, "");
	} else {
		/* Note: no $IFS removal */
		buffer[bufpos] = '\0';
		setvar("REPLY", buffer);
	}

 ret:
	free(buffer);
	if (read_flags & BUILTIN_READ_SILENT)
		tcsetattr(fd, TCSANOW, &old_tty);
	return retval;
}
Exemple #8
0
/* Returns 1 if no reply received */
int FAST_FUNC arpping(uint32_t test_nip,
		const uint8_t *safe_mac,
		uint32_t from_ip,
		uint8_t *from_mac,
		const char *interface,
		unsigned timeo)
{
	int timeout_ms;
	struct pollfd pfd[1];
#define s (pfd[0].fd)           /* socket */
	int rv = 1;             /* "no reply received" yet */
	struct sockaddr addr;   /* for interface name */
	struct arpMsg arp;

	if (!timeo)
		return 1;

	s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
	if (s == -1) {
		bb_perror_msg(bb_msg_can_not_create_raw_socket);
		return -1;
	}

	if (setsockopt_broadcast(s) == -1) {
		bb_perror_msg("can't enable bcast on raw socket");
		goto ret;
	}

	/* send arp request */
	memset(&arp, 0, sizeof(arp));
	memset(arp.h_dest, 0xff, 6);                    /* MAC DA */
	memcpy(arp.h_source, from_mac, 6);              /* MAC SA */
	arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
	arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
	arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
	arp.hlen = 6;                                   /* hardware address length */
	arp.plen = 4;                                   /* protocol address length */
	arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
	memcpy(arp.sHaddr, from_mac, 6);                /* source hardware address */
	memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
	/* tHaddr is zero-filled */                     /* target hardware address */
	memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */

	memset(&addr, 0, sizeof(addr));
	safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
	if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
		// TODO: error message? caller didn't expect us to fail,
		// just returning 1 "no reply received" misleads it.
		goto ret;
	}

	/* wait for arp reply, and check it */
	timeout_ms = (int)timeo;
	do {
		typedef uint32_t aliased_uint32_t FIX_ALIASING;
		int r;
		unsigned prevTime = monotonic_ms();

		pfd[0].events = POLLIN;
		r = safe_poll(pfd, 1, timeout_ms);
		if (r < 0)
			break;
		if (r) {
			r = safe_read(s, &arp, sizeof(arp));
			if (r < 0)
				break;

			//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
			//	arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
			//	arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);

			if (r >= ARP_MSG_SIZE
			 && arp.operation == htons(ARPOP_REPLY)
			 /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
			 /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
			 && *(aliased_uint32_t*)arp.sInaddr == test_nip
			) {
				/* if ARP source MAC matches safe_mac
				 * (which is client's MAC), then it's not a conflict
				 * (client simply already has this IP and replies to ARPs!)
				 */
				if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
					rv = 0;
				//else log2("sHaddr == safe_mac");
				break;
			}
		}
		timeout_ms -= (unsigned)monotonic_ms() - prevTime + 1;

		/* We used to check "timeout_ms > 0", but
		 * this is more under/overflow-resistant
		 * (people did see overflows here when system time jumps):
		 */
	} while ((unsigned)timeout_ms <= timeo);

 ret:
	close(s);
	log1("%srp reply received for this address", rv ? "no a" : "A");
	return rv;
}