Exemple #1
0
/* Always sets uid and gid */
int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
{
	struct passwd *pwd;
	struct group *gr;
	char *user, *group;
	unsigned n;

	user = (char*)ug;
	group = strchr(ug, ':');
	if (group) {
		int sz = (++group) - ug;
		user = alloca(sz);
		/* copies sz-1 bytes, stores terminating '\0' */
		safe_strncpy(user, ug, sz);
	}
	if (numeric_ok) {
		n = bb_strtou(user, NULL, 10);
		if (!errno) {
			u->uid = n;
			pwd = getpwuid(n);
			/* If we have e.g. "500" string without user */
			/* with uid 500 in /etc/passwd, we set gid == uid */
			u->gid = pwd ? pwd->pw_gid : n;
			goto skip;
		}
	}
	/* Either it is not numeric, or caller disallows numeric username */
	pwd = getpwnam(user);
	if (!pwd)
		return 0;
	u->uid = pwd->pw_uid;
	u->gid = pwd->pw_gid;

 skip:
	if (group) {
		if (numeric_ok) {
			n = bb_strtou(group, NULL, 10);
			if (!errno) {
				u->gid = n;
				return 1;
			}
		}
		gr = getgrnam(group);
		if (!gr)
			return 0;
		u->gid = gr->gr_gid;
	}
	return 1;
}
Exemple #2
0
/* NB: does chdir internally */
static void scan_proc_pids(void)
{
	DIR *d;
	struct dirent *de;
	pid_t pid;

	xchdir("/proc");
	d = opendir("/proc");
	if (!d)
		return;

	while ((de = readdir(d)) != NULL) {
		pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
		if (errno)
			continue;
		if (chdir(de->d_name) < 0)
			continue;
		scan_link("cwd", pid);
		scan_link("exe", pid);
		scan_link("root", pid);

		scan_dir_links("fd", pid);
		scan_dir_links("lib", pid);
		scan_dir_links("mmap", pid);

		scan_pid_maps("maps", pid);
		xchdir("/proc");
	}
	closedir(d);
}
Exemple #3
0
/* NB: does chdir internally */
static pid_list *scan_proc_pids(inode_list *ilist)
{
	DIR *d;
	struct dirent *de;
	pid_t pid;
	pid_list *plist;

	xchdir("/proc");
	d = opendir("/proc");
	if (!d)
		return NULL;

	plist = NULL;
	while ((de = readdir(d)) != NULL) {
		pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
		if (errno)
			continue;
		if (chdir(de->d_name) < 0)
			continue;
		plist = scan_link("cwd", pid, ilist, plist);
		plist = scan_link("exe", pid, ilist, plist);
		plist = scan_link("root", pid, ilist, plist);
		plist = scan_dir_links("fd", pid, ilist, plist);
		plist = scan_dir_links("lib", pid, ilist, plist);
		plist = scan_dir_links("mmap", pid, ilist, plist);
		plist = scan_pid_maps("maps", pid, ilist, plist);
		xchdir("/proc");
	}
	closedir(d);
	return plist;
}
Exemple #4
0
static void do_procinit(void)
{
	DIR *procdir;
	struct dirent *entry;
	int pid;

	if (pidfile) {
		do_pidfile();
		return;
	}

	procdir = xopendir("/proc");

	pid = 0;
	while (1) {
		errno = 0; /* clear any previous error */
		entry = readdir(procdir);
// TODO: this check is too generic, it's better
// to check for exact errno(s) which mean that we got stale entry
		if (errno) /* Stale entry, process has died after opendir */
			continue;
		if (!entry) /* EOF, no more entries */
			break;
		pid = bb_strtou(entry->d_name, NULL, 10);
		if (errno) /* NaN */
			continue;
		check(pid);
	}
	closedir(procdir);
	if (!pid)
		bb_error_msg_and_die("nothing in /proc - not mounted?");
}
Exemple #5
0
int get_signum(const char *name)
{
	int i;

	i = bb_strtou(name, NULL, 10);
	if (!errno)
		return i;
	if (strncasecmp(name, "SIG", 3) == 0)
		name += 3;
	for (i = 0; i < sizeof(signals) / sizeof(signals[0]); i++)
		if (strcasecmp(name, signals[i]) == 0)
			return i;

#if ENABLE_DESKTOP && (defined(SIGIOT) || defined(SIGIO))
	/* These are aliased to other names */
	if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') {
#ifdef SIGIO
		if (!name[2])
			return SIGIO;
#endif
#ifdef SIGIOT
		if ((name[2] | 0x20) == 't' && !name[3])
			return SIGIOT;
#endif
	}
#endif

	return -1;
}
static void get_prefix_1(inet_prefix *dst, char *arg, int family)
{
	char *slash;

	memset(dst, 0, sizeof(*dst));

	if (strcmp(arg, "default") == 0
	 || strcmp(arg, "all") == 0
	 || strcmp(arg, "any") == 0
	) {
		dst->family = family;
		/*dst->bytelen = 0; - done by memset */
		/*dst->bitlen = 0;*/
		return;
	}

	slash = strchr(arg, '/');
	if (slash)
		*slash = '\0';

	if (get_addr_1(dst, arg, family) == 0) {
		dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
		if (slash) {
			unsigned plen;
			inet_prefix netmask_pfx;

			netmask_pfx.family = AF_UNSPEC;
			plen = bb_strtou(slash + 1, NULL, 0);
			if ((errno || plen > (unsigned) dst->bitlen)
			 && get_addr_1(&netmask_pfx, slash + 1, family) != 0
			) {
				goto bad;
			}
			if (netmask_pfx.family == AF_INET) {
				/* fill in prefix length of dotted quad */
				uint32_t mask = ntohl(netmask_pfx.data[0]);
				uint32_t host = ~mask;

				/* a valid netmask must be 2^n - 1 */
				if (host & (host + 1))
					goto bad;

				for (plen = 0; mask; mask <<= 1)
					++plen;
				if (plen > (unsigned) dst->bitlen)
					goto bad;
				/* dst->flags |= PREFIXLEN_SPECIFIED; */
			}
			dst->bitlen = plen;
		}
	}

	if (slash)
		*slash = '/';
	return;
 bad:
	bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
}
static int get_prefix_1(inet_prefix *dst, char *arg, int family)
{
	int err;
	unsigned plen;
	char *slash;

	memset(dst, 0, sizeof(*dst));

	if (strcmp(arg, bb_str_default) == 0
	 || strcmp(arg, "all") == 0
	 || strcmp(arg, "any") == 0
	) {
		dst->family = family;
		/*dst->bytelen = 0; - done by memset */
		/*dst->bitlen = 0;*/
		return 0;
	}

	slash = strchr(arg, '/');
	if (slash)
		*slash = '\0';
	err = get_addr_1(dst, arg, family);
	if (err == 0) {
		dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
		if (slash) {
			inet_prefix netmask_pfx;

			netmask_pfx.family = AF_UNSPEC;
			plen = bb_strtou(slash + 1, NULL, 0);
			if ((errno || plen > dst->bitlen)
			 && (get_addr_1(&netmask_pfx, slash + 1, family)))
				err = -1;
			else if (netmask_pfx.family == AF_INET) {
				/* fill in prefix length of dotted quad */
				uint32_t mask = ntohl(netmask_pfx.data[0]);
				uint32_t host = ~mask;

				/* a valid netmask must be 2^n - 1 */
				if (!(host & (host + 1))) {
					for (plen = 0; mask; mask <<= 1)
						++plen;
					if (plen <= dst->bitlen) {
						dst->bitlen = plen;
						/* dst->flags |= PREFIXLEN_SPECIFIED; */
					} else
						err = -1;
				} else
					err = -1;
			} else {
				/* plain prefix */
				dst->bitlen = plen;
			}
		}
	}
	if (slash)
		*slash = '/';
	return err;
}
Exemple #8
0
static int dump_procs(FILE *fp, int look_for_login_process)
{
	struct dirent *entry;
	DIR *dir = opendir("/proc");
	int found_login_process = 0;

	fputs(G.jiffy_line, fp);
	while ((entry = readdir(dir)) != NULL) {
		char name[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
		int stat_fd;
		unsigned pid = bb_strtou(entry->d_name, NULL, 10);
		if (errno)
			continue;

		/* Android's version reads /proc/PID/cmdline and extracts
		 * non-truncated process name. Do we want to do that? */

		sprintf(name, "/proc/%u/stat", pid);
		stat_fd = open(name, O_RDONLY);
		if (stat_fd >= 0) {
			char *p;
			char stat_line[4*1024];
			int rd = safe_read(stat_fd, stat_line, sizeof(stat_line)-2);

			close(stat_fd);
			if (rd < 0)
				continue;
			stat_line[rd] = '\0';
			p = strchrnul(stat_line, '\n');
			*p++ = '\n';
			*p = '\0';
			fputs(stat_line, fp);
			if (!look_for_login_process)
				continue;
			p = strchr(stat_line, '(');
			if (!p)
				continue;
			p++;
			strchrnul(p, ')')[0] = '\0';
			/* Is it gdm, kdm or a getty? */
			if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x')
			     && p[1] == 'd' && p[2] == 'm' && p[3] == '\0'
			    )
			 || strstr(p, "getty")
			) {
				found_login_process = 1;
			}
		}
	}
	closedir(dir);
	fputc('\n', fp);
	return found_login_process;
}
/* String to timespec: "NNNN[.NNNNN]" -> struct timespec.
 * Can be used for other fixed-point needs.
 * Returns pointer past last converted char,
 * and returns errno similar to bb_strtoXX functions.
 */
char* FAST_FUNC bb_str_to_ts(struct timespec *ts, const char *arg)
{
	if (sizeof(ts->tv_sec) <= sizeof(int))
		ts->tv_sec = bb_strtou(arg, &arg, 10);
	else if (sizeof(ts->tv_sec) <= sizeof(long))
		ts->tv_sec = bb_strtoul(arg, &arg, 10);
	else
		ts->tv_sec = bb_strtoull(arg, &arg, 10);
	ts->tv_nsec = 0;

	if (*arg != '.')
		return arg;

	/* !EINVAL: number is not ok (alphanumeric ending, overflow etc) */
	if (errno != EINVAL)
		return arg;

	if (!*++arg) /* "NNN." */
		return arg;

	{ /* "NNN.xxx" - parse xxx */
		int ndigits;
		char *p;
		char buf[10]; /* we never use more than 9 digits */

		/* Need to make a copy to avoid false overflow */
		safe_strncpy(buf, arg, 10);
		ts->tv_nsec = bb_strtou(buf, &p, 10);
		ndigits = p - buf;
		arg += ndigits;
		/* normalize to nsec */
		while (ndigits < 9) {
			ndigits++;
			ts->tv_nsec *= 10;
		}
		while (isdigit(*arg)) /* skip possible 10th plus digits */
			arg++;
	}
	return arg;
}
Exemple #10
0
static void rtnl_tab_initialize(const char *file, const char **tab, int size)
{
	char *token[2];
	parser_t *parser = config_open2(file, fopen_for_read);
	while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
		int id = bb_strtou(token[0], NULL, 0);
		if (id < 0 || id > size) {
			bb_error_msg("database %s is corrupted at line %d",
				file, parser->lineno);
			break;
		}
		tab[id] = xstrdup(token[1]);
	}
	config_close(parser);
}
Exemple #11
0
static void
dev_get_major_minor(char *device_name, int *major, int *minor)
{
	char dev[16];
	char *dev_path;
	char *colon;
	int sz;

	dev_path = xasprintf("/sys/block/%s/dev", device_name);
	sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
	if (sz < 0)
		goto ret;
	dev[sz] = '\0';

	colon = strchr(dev, ':');
	if (!colon)
		goto ret;
	*major = bb_strtou(dev, NULL, 10);
	*minor = bb_strtou(colon + 1, NULL, 10);

 ret:
	free(dev_path);
	return;
}
Exemple #12
0
static int tftp_blksize_check(const char *blksize_str, int maxsize)
{
	/* Check if the blksize is valid:
	 * RFC2348 says between 8 and 65464,
	 * but our implementation makes it impossible
	 * to use blksizes smaller than 22 octets. */
	unsigned blksize = bb_strtou(blksize_str, NULL, 10);
	if (errno
	 || (blksize < 24) || (blksize > maxsize)
	) {
		bb_error_msg("bad blocksize '%s'", blksize_str);
		return -1;
	}
# if ENABLE_TFTP_DEBUG
	bb_error_msg("using blksize %u", blksize);
# endif
	return blksize;
}
int FAST_FUNC ll_proto_a2n(unsigned short *id, char *buf)
{
	unsigned i;
	const char *name = llproto_names;
	for (i = 0; i < ARRAY_SIZE(llproto_ids); i++) {
		if (strcasecmp(name, buf) == 0) {
			i = llproto_ids[i];
			 goto good;
		 }
		name += strlen(name) + 1;
	}
	errno = 0;
	i = bb_strtou(buf, NULL, 0);
	if (errno || i > 0xffff)
		return -1;
 good:
	*id = htons(i);
	return 0;
}
Exemple #14
0
/* Return port number for a service.
 * If "port" is a number use it as the port.
 * If "port" is a name it is looked up in /etc/services, if it isnt found return
 * default_port */
unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
{
	unsigned port_nr = default_port;
	if (port) {
		int old_errno;

		/* Since this is a lib function, we're not allowed to reset errno to 0.
		 * Doing so could break an app that is deferring checking of errno. */
		old_errno = errno;
		port_nr = bb_strtou(port, NULL, 10);
		if (errno || port_nr > 65535) {
			struct servent *tserv = getservbyname(port, protocol);
			port_nr = default_port;
			if (tserv)
				port_nr = ntohs(tserv->s_port);
		}
		errno = old_errno;
	}
	return (uint16_t)port_nr;
}
Exemple #15
0
static NOINLINE int process_timer_stats(void)
{
	char buf[128];
	char line[15 + 3 + 128];
	int n;
	FILE *fp;

	buf[0] = '\0';

	n = 0;
	fp = NULL;
	if (!G.cant_enable_timer_stats)
		fp = fopen_for_read("/proc/timer_stats");
	if (fp) {
// Example file contents:
// Timer Stats Version: v0.2
// Sample period: 1.329 s
//    76,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
//    88,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
//    24,  3787 firefox          hrtimer_start_range_ns (hrtimer_wakeup)
//   46D,  1136 kondemand/1      do_dbs_timer (delayed_work_timer_fn)
// ...
//     1,  1656 Xorg             hrtimer_start_range_ns (hrtimer_wakeup)
//     1,  2159 udisks-daemon    hrtimer_start_range_ns (hrtimer_wakeup)
// 331 total events, 249.059 events/sec
		while (fgets(buf, sizeof(buf), fp)) {
			const char *count, *process, *func;
			char *p;
			int idx;
			unsigned cnt;

			count = skip_whitespace(buf);
			p = strchr(count, ',');
			if (!p)
				continue;
			*p++ = '\0';
			cnt = bb_strtou(count, NULL, 10);
			if (strcmp(skip_non_whitespace(count), " total events") == 0) {
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
				n = cnt / G.total_cpus;
				if (n > 0 && n < G.interrupt_0) {
					sprintf(line, "    <interrupt> : %s", "extra timer interrupt");
					save_line(line, G.interrupt_0 - n);
				}
#endif
				break;
			}
			if (strchr(count, 'D'))
				continue; /* deferred */
			p = skip_whitespace(p); /* points to pid now */
			process = NULL;
 get_func_name:
			p = strchr(p, ' ');
			if (!p)
				continue;
			*p++ = '\0';
			p = skip_whitespace(p);
			if (process == NULL) {
				process = p;
				goto get_func_name;
			}
			func = p;

			//if (strcmp(process, "swapper") == 0
			// && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
			//) {
			//	process = "[kernel scheduler]";
			//	func = "Load balancing tick";
			//}

			if (strncmp(func, "tick_nohz_", 10) == 0)
				continue;
			if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
				continue;
			//if (strcmp(process, "powertop") == 0)
			//	continue;

			idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
			if (idx != -1) {
				process = idx < 2 ? "[kernel module]" : "<kernel core>";
			}

			strchrnul(p, '\n')[0] = '\0';

			// 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
			// ^          ^            ^
			// count      process      func

			//if (strchr(process, '['))
				sprintf(line, "%15.15s : %s", process, func);
			//else
			//	sprintf(line, "%s", process);
			save_line(line, cnt);
		}
		fclose(fp);
	}

	return n;
}
Exemple #16
0
static void number_process(int first_digit)
{
	unsigned i;
	int num;
	int keypress;
	char num_input[sizeof(int)*4]; /* more than enough */

	num_input[0] = first_digit;

	/* Clear the current line, print a prompt, and then print the digit */
	clear_line();
	printf(":%c", first_digit);

	/* Receive input until a letter is given */
	i = 1;
	while (i < sizeof(num_input)-1) {
		keypress = less_getch(i + 1);
		if ((unsigned)keypress > 255 || !isdigit(num_input[i]))
			break;
		num_input[i] = keypress;
		bb_putchar(keypress);
		i++;
	}

	num_input[i] = '\0';
	num = bb_strtou(num_input, NULL, 10);
	/* on format error, num == -1 */
	if (num < 1 || num > MAXLINES) {
		buffer_print();
		return;
	}

	/* We now know the number and the letter entered, so we process them */
	switch (keypress) {
	case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
		buffer_down(num);
		break;
	case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u':
		buffer_up(num);
		break;
	case 'g': case '<': case 'G': case '>':
		cur_fline = num + max_displayed_line;
		read_lines();
		buffer_line(num - 1);
		break;
	case 'p': case '%':
		num = num * (max_fline / 100); /* + max_fline / 2; */
		cur_fline = num + max_displayed_line;
		read_lines();
		buffer_line(num);
		break;
#if ENABLE_FEATURE_LESS_REGEXP
	case 'n':
		goto_match(match_pos + num);
		break;
	case '/':
		option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
		regex_process();
		break;
	case '?':
		option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
		regex_process();
		break;
#endif
	}
}
Exemple #17
0
int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opt;
	char *signame;
	char *startas;
	char *chuid;
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
//	char *retry_arg = NULL;
//	int retries = -1;
	char *opt_N;
#endif

	INIT_G();

	opt = GETOPT32(argv, "^"
		"KSbqtma:n:s:u:c:x:p:"
		IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:")
			/* -K or -S is required; they are mutually exclusive */
			/* -p is required if -m is given */
			/* -xpun (at least one) is required if -K is given */
			/* -xa (at least one) is required if -S is given */
			/* -q turns off -v */
			"\0"
			"K:S:K--S:S--K:m?p:K?xpun:S?xa"
			IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"),
		LONGOPTS
		&startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
		IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
		/* We accept and ignore -R <param> / --retry <param> */
		IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
	);

	if (opt & OPT_s) {
		signal_nr = get_signum(signame);
		if (signal_nr < 0) bb_show_usage();
	}

	if (!(opt & OPT_a))
		startas = execname;
	if (!execname) /* in case -a is given and -x is not */
		execname = startas;
	if (execname) {
		G.execname_sizeof = strlen(execname) + 1;
		G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
	}

//	IF_FEATURE_START_STOP_DAEMON_FANCY(
//		if (retry_arg)
//			retries = xatoi_positive(retry_arg);
//	)
	//argc -= optind;
	argv += optind;

	if (userspec) {
		user_id = bb_strtou(userspec, NULL, 10);
		if (errno)
			user_id = xuname2uid(userspec);
	}
	/* Both start and stop need to know current processes */
	do_procinit();

	if (opt & CTX_STOP) {
		int i = do_stop();
		return (opt & OPT_OKNODO) ? 0 : (i <= 0);
	}

	if (G.found_procs) {
		if (!QUIET)
			printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid);
		return !(opt & OPT_OKNODO);
	}

#ifdef OLDER_VERSION_OF_X
	if (execname)
		xstat(execname, &G.execstat);
#endif

	*--argv = startas;
	if (opt & OPT_BACKGROUND) {
#if BB_MMU
		bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK);
		/* DAEMON_DEVNULL_STDIO is superfluous -
		 * it's always done by bb_daemonize() */
#else
		/* Daemons usually call bb_daemonize_or_rexec(), but SSD can do
		 * without: SSD is not itself a daemon, it _execs_ a daemon.
		 * The usual NOMMU problem of "child can't run indefinitely,
		 * it must exec" does not bite us: we exec anyway.
		 */
		pid_t pid = xvfork();
		if (pid != 0) {
			/* parent */
			/* why _exit? the child may have changed the stack,
			 * so "return 0" may do bad things */
			_exit(EXIT_SUCCESS);
		}
		/* Child */
		setsid(); /* detach from controlling tty */
		/* Redirect stdio to /dev/null, close extra FDs */
		bb_daemon_helper(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS);
#endif
	}
	if (opt & OPT_MAKEPID) {
		/* User wants _us_ to make the pidfile */
		write_pidfile(pidfile);
	}
	if (opt & OPT_c) {
		struct bb_uidgid_t ugid;
		parse_chown_usergroup_or_die(&ugid, chuid);
		if (ugid.uid != (uid_t) -1L) {
			struct passwd *pw = xgetpwuid(ugid.uid);
			if (ugid.gid != (gid_t) -1L)
				pw->pw_gid = ugid.gid;
			/* initgroups, setgid, setuid: */
			change_identity(pw);
		} else if (ugid.gid != (gid_t) -1L) {
			xsetgid(ugid.gid);
			setgroups(1, &ugid.gid);
		}
	}
#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
	if (opt & OPT_NICELEVEL) {
		/* Set process priority */
		int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
		if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
			bb_perror_msg_and_die("setpriority(%d)", prio);
		}
	}
#endif
	execvp(startas, argv);
	bb_perror_msg_and_die("can't execute '%s'", startas);
}
Exemple #18
0
int kill_main(int argc, char **argv)
{
	char *arg;
	pid_t pid;
	int signo = SIGTERM, errors = 0, quiet = 0;
#if !ENABLE_KILLALL && !ENABLE_KILLALL5
#define killall 0
#define killall5 0
#else
/* How to determine who we are? find 3rd char from the end:
 * kill, killall, killall5
 *  ^i       ^a        ^l  - it's unique
 * (checking from the start is complicated by /bin/kill... case) */
	const char char3 = argv[0][strlen(argv[0]) - 3];
#define killall (ENABLE_KILLALL && char3 == 'a')
#define killall5 (ENABLE_KILLALL5 && char3 == 'l')
#endif

	/* Parse any options */
	argc--;
	arg = *++argv;

	if (argc < 1 || arg[0] != '-') {
		goto do_it_now;
	}

	/* The -l option, which prints out signal names.
	 * Intended usage in shell:
	 * echo "Died of SIG`kill -l $?`"
	 * We try to mimic what kill from coreutils-6.8 does */
	if (arg[1] == 'l' && arg[2] == '\0') {
		if (argc == 1) {
			/* Print the whole signal list */
			print_signames();
			return 0;
		}
		/* -l <sig list> */
		while ((arg = *++argv)) {
			if (isdigit(arg[0])) {
				signo = bb_strtou(arg, NULL, 10);
				if (errno) {
					bb_error_msg("unknown signal '%s'", arg);
					return EXIT_FAILURE;
				}
				/* Exitcodes >= 0x80 are to be treated
				 * as "killed by signal (exitcode & 0x7f)" */
				puts(get_signame(signo & 0x7f));
				/* TODO: 'bad' signal# - coreutils says:
				 * kill: 127: invalid signal
				 * we just print "127" instead */
			} else {
				signo = get_signum(arg);
				if (signo < 0) {
					bb_error_msg("unknown signal '%s'", arg);
					return EXIT_FAILURE;
				}
				printf("%d\n", signo);
			}
		}
		/* If they specified -l, we are all done */
		return EXIT_SUCCESS;
	}

	/* The -q quiet option */
	if (killall && arg[1] == 'q' && arg[2] == '\0') {
		quiet = 1;
		arg = *++argv;
		argc--;
		if (argc < 1) bb_show_usage();
		if (arg[0] != '-') goto do_it_now;
	}

	/* -SIG */
	signo = get_signum(&arg[1]);
	if (signo < 0) { /* || signo > MAX_SIGNUM ? */
		bb_error_msg("bad signal name '%s'", &arg[1]);
		return EXIT_FAILURE;
	}
	arg = *++argv;
	argc--;

do_it_now:
	pid = getpid();

	if (killall5) {
		pid_t sid;
		procps_status_t* p = NULL;

		/* Find out our own session id */
		sid = getsid(pid);
		/* Now stop all processes */
		kill(-1, SIGSTOP);
		/* Now kill all processes except our session */
		while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
			if (p->sid != (unsigned)sid && p->pid != (unsigned)pid && p->pid != 1)
				kill(p->pid, signo);
		}
		/* And let them continue */
		kill(-1, SIGCONT);
		return 0;
	}

	/* Pid or name is required for kill/killall */
	if (argc < 1) {
		bb_error_msg("you need to specify whom to kill");
		return EXIT_FAILURE;
	}

	if (killall) {
		/* Looks like they want to do a killall.  Do that */
		while (arg) {
			pid_t* pidList;

			pidList = find_pid_by_name(arg);
			if (*pidList == 0) {
				errors++;
				if (!quiet)
					bb_error_msg("%s: no process killed", arg);
			} else {
				pid_t *pl;

				for (pl = pidList; *pl; pl++) {
					if (*pl == pid)
						continue;
					if (kill(*pl, signo) == 0)
						continue;
					errors++;
					if (!quiet)
						bb_perror_msg("cannot kill pid %u", (unsigned)*pl);
				}
			}
			free(pidList);
			arg = *++argv;
		}
		return errors;
	}

	/* Looks like they want to do a kill. Do that */
	while (arg) {
		/* Support shell 'space' trick */
		if (arg[0] == ' ')
			arg++;
		pid = bb_strtoi(arg, NULL, 10);
		if (errno) {
			bb_error_msg("bad pid '%s'", arg);
			errors++;
		} else if (kill(pid, signo) != 0) {
			bb_perror_msg("cannot kill pid %d", (int)pid);
			errors++;
		}
		arg = *++argv;
	}
	return errors;
}
Exemple #19
0
static smallint scan_recursive(const char *path)
{
	DIR *d;
	struct dirent *d_ent;
	smallint stop_scan;
	smallint retval;

	d = opendir(path);
	if (d == NULL)
		return 0;

	G.recursion_depth++;
	retval = 0;
	stop_scan = 0;
	while (!stop_scan && (d_ent = readdir(d)) != NULL) {
		struct stat statbuf;
		pid_t pid;
		char *subpath;

		subpath = concat_subpath_file(path, d_ent->d_name);
		if (subpath == NULL)
			continue; /* . or .. */

		switch (G.recursion_depth) {
		case PROC_DIR:
			pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
			if (errno != 0
			 || pid == G.mypid
			/* "this PID doesn't use specified FILEs or PORT/PROTO": */
			 || scan_recursive(subpath) == 0
			) {
				break;
			}
			if (option_mask32 & OPT_KILL) {
				if (kill(pid, G.killsig) != 0) {
					bb_perror_msg("kill pid %s", d_ent->d_name);
					G.kill_failed = 1;
				}
			}
			if (!(option_mask32 & OPT_SILENT))
				printf("%s ", d_ent->d_name);
			retval = 1;
			break;

		case PROC_DIR_LINKS:
			switch (
				index_in_substrings(
					"cwd"  "\0" "exe"  "\0"
					"root" "\0" "fd"   "\0"
					"lib"  "\0" "mmap" "\0"
					"maps" "\0",
					d_ent->d_name
				)
			) {
			enum {
				CWD_LINK,
				EXE_LINK,
				ROOT_LINK,
				FD_DIR_LINKS,
				LIB_DIR_LINKS,
				MMAP_DIR_LINKS,
				MAPS,
			};
			case CWD_LINK:
			case EXE_LINK:
			case ROOT_LINK:
				goto scan_link;
			case FD_DIR_LINKS:
			case LIB_DIR_LINKS:
			case MMAP_DIR_LINKS:
				// change for ofgwrite
				retval = scan_recursive(subpath);
				break;
			case MAPS:
				// change for ofgwrite
				retval = scan_proc_net_or_maps(subpath, 0);
			default:
				break;
			}
			break;
		case PROC_SUBDIR_LINKS:
  scan_link:
			if (stat(subpath, &statbuf) < 0)
				break;
			// change for ofgwrite
			retval = search_dev_inode(&statbuf);
			if (retval)
			{
				if (strcmp(d_ent->d_name, "exe") == 0)
				{
					char* ln = xmalloc_readlink(subpath);
					if (ln != NULL)
					{
						// change for ofgwrite: Don't kill VU+ and GB specific processes
						if (strcmp(ln, "/oldroot/usr/bin/dvb_server") == 0
							|| strcmp(ln, "/oldroot/usr/bin/init_client") == 0
							|| strcmp(ln, "/oldroot/usr/bin/ntfs-3g") == 0
							|| strcmp(ln, "/oldroot/usr/share/platform/dvb_init") == 0
							|| strcmp(ln, "/oldroot/usr/bin/nxserver") == 0
							|| strcmp(ln, "/oldroot/usr/bin/init_driver") == 0
							|| strcmp(ln, "/oldroot/usr/share/platform/dvb_init.bin") == 0
							|| strcmp(ln, "/oldroot/usr/share/platform/nxserver") == 0
							|| strcmp(ln, "/oldroot/usr/bin/showiframe") == 0
							|| strcmp(ln, "/oldroot/usr/bin/libreader") == 0
							|| ( strncmp(ln, "/oldroot/lib/modules/", 21) == 0
								&& strstr(ln, "/extra/hi_play.ko") != NULL )
						   )
						{
							my_printf("found vu or gb or octagon or ntfs process %s -> don't kill\n", ln);
							retval = 0;
							stop_scan=1;
						}
						free(ln);
					}
				}
			}
		default:
			break;
		}
		free(subpath);
	}
	closedir(d);
	G.recursion_depth--;
	return retval;
}
procps_status_t* procps_scan(procps_status_t* sp, int flags)
{
	struct dirent *entry;
	char buf[PROCPS_BUFSIZE];
	char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
	char *filename_tail;
	long tasknice;
	unsigned pid;
	int n;
	struct stat sb;

	if (!sp)
		sp = alloc_procps_scan(flags);

	for (;;) {
		entry = readdir(sp->dir);
		if (entry == NULL) {
			free_procps_scan(sp);
			return NULL;
		}
		pid = bb_strtou(entry->d_name, NULL, 10);
		if (errno)
			continue;

		/* After this point we have to break, not continue
		 * ("continue" would mean that current /proc/NNN
		 * is not a valid process info) */

		memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));

		sp->pid = pid;
		if (!(flags & ~PSSCAN_PID)) break;

		filename_tail = filename + sprintf(filename, "/proc/%d", pid);

		if (flags & PSSCAN_UIDGID) {
			if (stat(filename, &sb))
				break;
			/* Need comment - is this effective or real UID/GID? */
			sp->uid = sb.st_uid;
			sp->gid = sb.st_gid;
		}

		if (flags & PSSCAN_STAT) {
			char *cp;
			/* see proc(5) for some details on this */
			strcpy(filename_tail, "/stat");
			n = read_to_buf(filename, buf);
			if (n < 0)
				break;
			cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
			if (!cp || cp[1] != ' ')
				break;
			cp[0] = '\0';
			if (sizeof(sp->comm) < 16)
				BUG_comm_size();
			sscanf(buf, "%*s (%15c", sp->comm);
			n = sscanf(cp+2,
				"%c %u "               /* state, ppid */
				"%u %u %*s %*s "       /* pgid, sid, tty, tpgid */
				"%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
				"%lu %lu "             /* utime, stime */
				"%*s %*s %*s "         /* cutime, cstime, priority */
				"%ld "                 /* nice */
				"%*s %*s %*s "         /* timeout, it_real_value, start_time */
				"%lu ",                /* vsize */
				sp->state, &sp->ppid,
				&sp->pgid, &sp->sid,
				&sp->utime, &sp->stime,
				&tasknice,
				&sp->vsz);
			if (n != 8)
				break;

			if (sp->vsz == 0 && sp->state[0] != 'Z')
				sp->state[1] = 'W';
			else
				sp->state[1] = ' ';
			if (tasknice < 0)
				sp->state[2] = '<';
			else if (tasknice > 0)
				sp->state[2] = 'N';
			else
				sp->state[2] = ' ';

			sp->vsz >>= 10; /* vsize is in bytes and we want kb */
		}

		if (flags & PSSCAN_CMD) {
			free(sp->cmd);
			sp->cmd = NULL;
			strcpy(filename_tail, "/cmdline");
			n = read_to_buf(filename, buf);
			if (n <= 0)
				break;
			if (buf[n-1] == '\n') {
				if (!--n)
					break;
				buf[n] = '\0';
			}
			do {
				n--;
				if ((unsigned char)(buf[n]) < ' ')
					buf[n] = ' ';
			} while (n);
			sp->cmd = xstrdup(buf);
		}
		break;
	}
Exemple #21
0
int renice_main(int argc, char **argv)
{
	static const char Xetpriority_msg[] ALIGN1 = "%cetpriority";

	int retval = EXIT_SUCCESS;
	int which = PRIO_PROCESS;	/* Default 'which' value. */
	int use_relative = 0;
	int adjustment, new_priority;
	unsigned who;
	char *arg;

	/* Yes, they are not #defines in glibc 2.4! #if won't work */
	if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX)
		BUG_bad_PRIO_PROCESS();
	if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX)
		BUG_bad_PRIO_PGRP();
	if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX)
		BUG_bad_PRIO_USER();

	arg = *++argv;

	/* Check if we are using a relative adjustment. */
	if (arg && arg[0] == '-' && arg[1] == 'n') {
		use_relative = 1;
		if (!arg[2])
			arg = *++argv;
		else
			arg += 2;
	}

	if (!arg) {				/* No args?  Then show usage. */
		bb_show_usage();
	}

	/* Get the priority adjustment (absolute or relative). */
	adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2);

	while ((arg = *++argv) != NULL) {
		/* Check for a mode switch. */
		if (arg[0] == '-' && arg[1]) {
			static const char opts[] ALIGN1 = {
				'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER
			};
			const char *p = strchr(opts, arg[1]);
			if (p) {
				which = p[4];
				if (!arg[2])
					continue;
				arg += 2;
			}
		}

		/* Process an ID arg. */
		if (which == PRIO_USER) {
			struct passwd *p;
			p = getpwnam(arg);
			if (!p) {
				bb_error_msg("unknown user: %s", arg);
				goto HAD_ERROR;
			}
			who = p->pw_uid;
		} else {
			who = bb_strtou(arg, NULL, 10);
			if (errno) {
				bb_error_msg("bad value: %s", arg);
				goto HAD_ERROR;
			}
		}

		/* Get priority to use, and set it. */
		if (use_relative) {
			int old_priority;

			errno = 0;	 /* Needed for getpriority error detection. */
			old_priority = getpriority(which, who);
			if (errno) {
				bb_perror_msg(Xetpriority_msg, 'g');
				goto HAD_ERROR;
			}

			new_priority = old_priority + adjustment;
		} else {
			new_priority = adjustment;
		}

		if (setpriority(which, who, new_priority) == 0) {
			continue;
		}

		bb_perror_msg(Xetpriority_msg, 's');
 HAD_ERROR:
		retval = EXIT_FAILURE;
	}

	/* No need to check for errors outputing to stderr since, if it
	 * was used, the HAD_ERROR label was reached and retval was set. */

	return retval;
}
//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 #23
0
/* Run uuidcache_check_device() for every device mentioned
 * in /proc/partitions */
static void
uuidcache_init_partitions(void)
{
	char line[100];
	int ma, mi;
	unsigned long long sz;
	FILE *procpt;
	int firstPass;
	int handleOnFirst;
	char *chptr;

	procpt = xfopen("/proc/partitions", "r");
/*
# cat /proc/partitions
major minor  #blocks  name

   8     0  293036184 sda
   8     1    6835626 sda1
   8     2          1 sda2
   8     5     979933 sda5
   8     6   15623181 sda6
   8     7   97659103 sda7
   8     8  171935631 sda8
*/
	for (firstPass = 1; firstPass >= 0; firstPass--) {
		fseek(procpt, 0, SEEK_SET);

		while (fgets(line, sizeof(line), procpt)) {
			/* The original version of this code used sscanf, but
			   diet's sscanf is quite limited */
			chptr = line;
			if (*chptr != ' ') continue;
			chptr = skip_whitespace(chptr);

			ma = bb_strtou(chptr, &chptr, 0);
			if (ma < 0) continue;
			chptr = skip_whitespace(chptr);

			mi = bb_strtou(chptr, &chptr, 0);
			if (mi < 0) continue;
			chptr = skip_whitespace(chptr);

			sz = bb_strtoull(chptr, &chptr, 0);
			if ((long long)sz == -1LL) continue;
			chptr = skip_whitespace(chptr);

			/* skip extended partitions (heuristic: size 1) */
			if (sz == 1)
				continue;

			*strchrnul(chptr, '\n') = '\0';
			/* now chptr => device name */
			dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
						ma, mi, sz, chptr);
			if (!chptr[0])
				continue;

			/* look only at md devices on first pass */
			handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
			if (firstPass != handleOnFirst)
				continue;

			/* heuristic: partition name ends in a digit */
			if (isdigit(chptr[strlen(chptr) - 1])) {
				uuidcache_check_device(chptr, ma, mi, 0);
			}
		}
	}

	fclose(procpt);
}
Exemple #24
0
int FAST_FUNC
shell_builtin_ulimit(char **argv)
{
	unsigned opts;
	unsigned argc;

	/* We can't use getopt32: need to handle commands like
	 * ulimit 123 -c2 -l 456
	 */

	/* In case getopt was already called:
	 * reset the libc getopt() function, which keeps internal state.
	 */
#ifdef __GLIBC__
	optind = 0;
#else /* BSD style */
	optind = 1;
	/* optreset = 1; */
#endif
	/* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */

        argc = 1;
        while (argv[argc])
                argc++;

	opts = 0;
	while (1) {
		struct rlimit limit;
		const struct limits *l;
		int opt_char = getopt(argc, argv, ulimit_opt_string);

		if (opt_char == -1)
			break;
		if (opt_char == 'H') {
			opts |= OPT_hard;
			continue;
		}
		if (opt_char == 'S') {
			opts |= OPT_soft;
			continue;
		}

		if (opt_char == 'a') {
			for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
				getrlimit(l->cmd, &limit);
				printf("-%c: %-30s ", l->option, l->name);
				printlim(opts, &limit, l);
			}
			continue;
		}

		if (opt_char == 1)
			opt_char = 'f';
		for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
			if (opt_char == l->option) {
				char *val_str;

				getrlimit(l->cmd, &limit);

				val_str = optarg;
				if (!val_str && argv[optind] && argv[optind][0] != '-')
					val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
				if (val_str) {
					rlim_t val;

					if (strcmp(val_str, "unlimited") == 0)
						val = RLIM_INFINITY;
					else {
						if (sizeof(val) == sizeof(int))
							val = bb_strtou(val_str, NULL, 10);
						else if (sizeof(val) == sizeof(long))
							val = bb_strtoul(val_str, NULL, 10);
						else
							val = bb_strtoull(val_str, NULL, 10);
						if (errno) {
							bb_error_msg("invalid number '%s'", val_str);
							return EXIT_FAILURE;
						}
						val <<= l->factor_shift;
					}
//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
					/* from man bash: "If neither -H nor -S
					 * is specified, both the soft and hard
					 * limits are set. */
					if (!opts)
						opts = OPT_hard + OPT_soft;
					if (opts & OPT_hard)
						limit.rlim_max = val;
					if (opts & OPT_soft)
						limit.rlim_cur = val;
//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
					if (setrlimit(l->cmd, &limit) < 0) {
						bb_perror_msg("error setting limit");
						return EXIT_FAILURE;
					}
				} else {
					printlim(opts, &limit, l);
				}
				break;
			}
		} /* for (every possible opt) */

		if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
			/* bad option. getopt already complained. */
			break;
		}

	} /* while (there are options) */

	return 0;
}
Exemple #25
0
int lpd_main(int argc UNUSED_PARAM, char *argv[])
{
	int spooling = spooling; // for compiler
	char *s, *queue;
	char *filenames[2];

	// goto spool directory
	if (*++argv)
		xchdir(*argv++);

	// error messages of xfuncs will be sent over network
	xdup2(STDOUT_FILENO, STDERR_FILENO);

	// nullify ctrl/data filenames
	memset(filenames, 0, sizeof(filenames));

	// read command
	s = queue = xmalloc_read_stdin();
	// we understand only "receive job" command
	if (2 != *queue) {
 unsupported_cmd:
		printf("Command %02x %s\n",
			(unsigned char)s[0], "is not supported");
		goto err_exit;
	}

	// parse command: "2 | QUEUE_NAME | '\n'"
	queue++;
	// protect against "/../" attacks
	// *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do
	if (!*sane(queue))
		return EXIT_FAILURE;

	// queue is a directory -> chdir to it and enter spooling mode
	spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done
	// we don't free(s), we might need "queue" var later

	while (1) {
		char *fname;
		int fd;
		// int is easier than ssize_t: can use xatoi_u,
		// and can correctly display error returns (-1)
		int expected_len, real_len;

		// signal OK
		safe_write(STDOUT_FILENO, "", 1);

		// get subcommand
		// valid s must be of form: "SUBCMD | LEN | space | FNAME"
		// N.B. we bail out on any error
		s = xmalloc_read_stdin();
		if (!s) { // (probably) EOF
			char *p, *q, var[2];

			// non-spooling mode or no spool helper specified
			if (!spooling || !*argv)
				return EXIT_SUCCESS; // the only non-error exit
			// spooling mode but we didn't see both ctrlfile & datafile
			if (spooling != 7)
				goto err_exit; // reject job

			// spooling mode and spool helper specified -> exec spool helper
			// (we exit 127 if helper cannot be executed)
			var[1] = '\0';
			// read and delete ctrlfile
			q = xmalloc_xopen_read_close(filenames[0], NULL);
			unlink(filenames[0]);
			// provide datafile name
			// we can use leaky setenv since we are about to exec or exit
			xsetenv("DATAFILE", filenames[1]);
			// parse control file by "\n"
			while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) {
				*p++ = '\0';
				// q is a line of <SYM><VALUE>,
				// we are setting environment string <SYM>=<VALUE>.
				// Ignoring "l<datafile>", exporting others:
				if (*q != 'l') {
					var[0] = *q++;
					xsetenv(var, q);
				}
				q = p; // next line
			}
			// helper should not talk over network.
			// this call reopens stdio fds to "/dev/null"
			// (no daemonization is done)
			bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
			BB_EXECVP(*argv, argv);
			exit(127);
		}

		// validate input.
		// we understand only "control file" or "data file" cmds
		if (2 != s[0] && 3 != s[0])
			goto unsupported_cmd;
		if (spooling & (1 << (s[0]-1))) {
			printf("Duplicated subcommand\n");
			goto err_exit;
		}
		// get filename
		*strchrnul(s, '\n') = '\0';
		fname = strchr(s, ' ');
		if (!fname) {
// bad_fname:
			printf("No or bad filename\n");
			goto err_exit;
		}
		*fname++ = '\0';
//		// s[0]==2: ctrlfile, must start with 'c'
//		// s[0]==3: datafile, must start with 'd'
//		if (fname[0] != s[0] + ('c'-2))
//			goto bad_fname;
		// get length
		expected_len = bb_strtou(s + 1, NULL, 10);
		if (errno || expected_len < 0) {
			printf("Bad length\n");
			goto err_exit;
		}
		if (2 == s[0] && expected_len > 16 * 1024) {
			// SECURITY:
			// ctrlfile can't be big (we want to read it back later!)
			printf("File is too big\n");
			goto err_exit;
		}

		// open the file
		if (spooling) {
			// spooling mode: dump both files
			// job in flight has mode 0200 "only writable"
			sane(fname);
			fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
			if (fd < 0)
				goto err_exit;
			filenames[s[0] - 2] = xstrdup(fname);
		} else {
			// non-spooling mode:
			// 2: control file (ignoring), 3: data file
			fd = -1;
			if (3 == s[0])
				fd = xopen(queue, O_RDWR | O_APPEND);
		}

		// signal OK
		safe_write(STDOUT_FILENO, "", 1);

		// copy the file
		real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len);
		if (real_len != expected_len) {
			printf("Expected %d but got %d bytes\n",
				expected_len, real_len);
			goto err_exit;
		}
		// get EOF indicator, see whether it is NUL (ok)
		// (and don't trash s[0]!)
		if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) {
			// don't send error msg to peer - it obviously
			// doesn't follow the protocol, so probably
			// it can't understand us either
			goto err_exit;
		}

		if (spooling) {
			// chmod completely downloaded file as "readable+writable"
			fchmod(fd, 0600);
			// accumulate dump state
			// N.B. after all files are dumped spooling should be 1+2+4==7
			spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile
		}

		free(s);
		close(fd); // NB: can do close(-1). Who cares?

		// NB: don't do "signal OK" write here, it will be done
		// at the top of the loop
	} // while (1)

 err_exit:
	// don't keep corrupted files
	if (spooling) {
#define i spooling
		for (i = 2; --i >= 0; )
			if (filenames[i])
				unlink(filenames[i]);
	}
	return EXIT_FAILURE;
}
Exemple #26
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]);
}
NOINLINE
sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
{
	void (*sha_begin)(void *ctx) FAST_FUNC;
	void (*sha_hash)(void *ctx, const void *buffer, size_t len) FAST_FUNC;
	void (*sha_end)(void *ctx, void *resbuf) FAST_FUNC;
	int _32or64;

	char *result, *resptr;

	/* btw, sha256 needs [32] and uint32_t only */
	struct {
		unsigned char alt_result[64];
		unsigned char temp_result[64];
		union {
			sha256_ctx_t x;
			sha512_ctx_t y;
		} ctx;
		union {
			sha256_ctx_t x;
			sha512_ctx_t y;
		} alt_ctx;
	} L __attribute__((__aligned__(__alignof__(uint64_t))));
#define alt_result  (L.alt_result )
#define temp_result (L.temp_result)
#define ctx         (L.ctx        )
#define alt_ctx     (L.alt_ctx    )
	unsigned salt_len;
	unsigned key_len;
	unsigned cnt;
	unsigned rounds;
	char *cp;
	char is_sha512;

	/* Analyze salt, construct already known part of result */
	cnt = strlen(salt_data) + 1 + 43 + 1;
	is_sha512 = salt_data[1];
	if (is_sha512 == '6')
		cnt += 43;
	result = resptr = xzalloc(cnt); /* will provide NUL terminator */
	*resptr++ = '$';
	*resptr++ = is_sha512;
	*resptr++ = '$';
	rounds = ROUNDS_DEFAULT;
	salt_data += 3;
	if (strncmp(salt_data, str_rounds, 7) == 0) {
		/* 7 == strlen("rounds=") */
		char *endp;
		cnt = bb_strtou(salt_data + 7, &endp, 10);
		if (*endp == '$') {
			salt_data = endp + 1;
			rounds = cnt;
			if (rounds < ROUNDS_MIN)
				rounds = ROUNDS_MIN;
			if (rounds > ROUNDS_MAX)
				rounds = ROUNDS_MAX;
			/* add "rounds=NNNNN$" to result */
			resptr += sprintf(resptr, str_rounds, rounds);
		}
	}
	salt_len = strchrnul(salt_data, '$') - salt_data;
	if (salt_len > SALT_LEN_MAX)
		salt_len = SALT_LEN_MAX;
	/* xstrdup assures suitable alignment; also we will use it
	   as a scratch space later. */
	salt_data = xstrndup(salt_data, salt_len);
	/* add "salt$" to result */
	strcpy(resptr, salt_data);
	resptr += salt_len;
	*resptr++ = '$';
	/* key data doesn't need much processing */
	key_len = strlen(key_data);
	key_data = xstrdup(key_data);

	/* Which flavor of SHAnnn ops to use? */
	sha_begin = (void*)sha256_begin;
	sha_hash = (void*)sha256_hash;
	sha_end = (void*)sha256_end;
	_32or64 = 32;
	if (is_sha512 == '6') {
		sha_begin = (void*)sha512_begin;
		sha_hash = (void*)sha512_hash;
		sha_end = (void*)sha512_end;
		_32or64 = 64;
	}

	/* Add KEY, SALT.  */
	sha_begin(&ctx);
	sha_hash(&ctx, key_data, key_len);
	sha_hash(&ctx, salt_data, salt_len);

	/* Compute alternate SHA sum with input KEY, SALT, and KEY.
	   The final result will be added to the first context.  */
	sha_begin(&alt_ctx);
	sha_hash(&alt_ctx, key_data, key_len);
	sha_hash(&alt_ctx, salt_data, salt_len);
	sha_hash(&alt_ctx, key_data, key_len);
	sha_end(&alt_ctx, alt_result);

	/* Add result of this to the other context.  */
	/* Add for any character in the key one byte of the alternate sum.  */
	for (cnt = key_len; cnt > _32or64; cnt -= _32or64)
		sha_hash(&ctx, alt_result, _32or64);
	sha_hash(&ctx, alt_result, cnt);

	/* Take the binary representation of the length of the key and for every
	   1 add the alternate sum, for every 0 the key.  */
	for (cnt = key_len; cnt != 0; cnt >>= 1)
		if ((cnt & 1) != 0)
			sha_hash(&ctx, alt_result, _32or64);
		else
			sha_hash(&ctx, key_data, key_len);

	/* Create intermediate result.  */
	sha_end(&ctx, alt_result);

	/* Start computation of P byte sequence.  */
	/* For every character in the password add the entire password.  */
	sha_begin(&alt_ctx);
	for (cnt = 0; cnt < key_len; ++cnt)
		sha_hash(&alt_ctx, key_data, key_len);
	sha_end(&alt_ctx, temp_result);

	/* NB: past this point, raw key_data is not used anymore */

	/* Create byte sequence P.  */
#define p_bytes key_data /* reuse the buffer as it is of the key_len size */
	cp = p_bytes; /* was: ... = alloca(key_len); */
	for (cnt = key_len; cnt >= _32or64; cnt -= _32or64) {
		cp = memcpy(cp, temp_result, _32or64);
		cp += _32or64;
	}
	memcpy(cp, temp_result, cnt);

	/* Start computation of S byte sequence.  */
	/* For every character in the password add the entire password.  */
	sha_begin(&alt_ctx);
	for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt)
		sha_hash(&alt_ctx, salt_data, salt_len);
	sha_end(&alt_ctx, temp_result);

	/* NB: past this point, raw salt_data is not used anymore */

	/* Create byte sequence S.  */
#define s_bytes salt_data /* reuse the buffer as it is of the salt_len size */
	cp = s_bytes; /* was: ... = alloca(salt_len); */
	for (cnt = salt_len; cnt >= _32or64; cnt -= _32or64) {
		cp = memcpy(cp, temp_result, _32or64);
		cp += _32or64;
	}
	memcpy(cp, temp_result, cnt);

	/* Repeatedly run the collected hash value through SHA to burn
	   CPU cycles.  */
	for (cnt = 0; cnt < rounds; ++cnt) {
		sha_begin(&ctx);

		/* Add key or last result.  */
		if ((cnt & 1) != 0)
			sha_hash(&ctx, p_bytes, key_len);
		else
			sha_hash(&ctx, alt_result, _32or64);
		/* Add salt for numbers not divisible by 3.  */
		if (cnt % 3 != 0)
			sha_hash(&ctx, s_bytes, salt_len);
		/* Add key for numbers not divisible by 7.  */
		if (cnt % 7 != 0)
			sha_hash(&ctx, p_bytes, key_len);
		/* Add key or last result.  */
		if ((cnt & 1) != 0)
			sha_hash(&ctx, alt_result, _32or64);
		else
			sha_hash(&ctx, p_bytes, key_len);

		sha_end(&ctx, alt_result);
	}

	/* Append encrypted password to result buffer */
//TODO: replace with something like
//	bb_uuencode(cp, src, length, bb_uuenc_tbl_XXXbase64);
#define b64_from_24bit(B2, B1, B0, N) \
do { \
	unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \
	resptr = to64(resptr, w, N); \
} while (0)
	if (is_sha512 == '5') {
		unsigned i = 0;
		while (1) {
			unsigned j = i + 10;
			unsigned k = i + 20;
			if (j >= 30) j -= 30;
			if (k >= 30) k -= 30;
			b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4);
			if (k == 29)
				break;
			i = k + 1;
		}
		b64_from_24bit(0, alt_result[31], alt_result[30], 3);
		/* was:
		b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4);
		b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4);
		b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4);
		b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4);
		b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4);
		b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4);
		b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4);
		b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4);
		b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4);
		b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4);
		b64_from_24bit(0, alt_result[31], alt_result[30], 3);
		*/
	} else {
		unsigned i = 0;
		while (1) {
			unsigned j = i + 21;
			unsigned k = i + 42;
			if (j >= 63) j -= 63;
			if (k >= 63) k -= 63;
			b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4);
			if (j == 20)
				break;
			i = j + 1;
		}
		b64_from_24bit(0, 0, alt_result[63], 2);
		/* was:
		b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4);
		b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4);
		b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4);
		b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4);
		b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4);
		b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4);
		b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4);
		b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4);
		b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4);
		b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4);
		b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4);
		b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4);
		b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4);
		b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4);
		b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4);
		b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4);
		b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4);
		b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4);
		b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4);
		b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4);
		b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4);
		b64_from_24bit(0, 0, alt_result[63], 2);
		*/
	}
	/* *resptr = '\0'; - xzalloc did it */
#undef b64_from_24bit

	/* Clear the buffer for the intermediate result so that people
	   attaching to processes or reading core dumps cannot get any
	   information.  */
	memset(&L, 0, sizeof(L)); /* [alt]_ctx and XXX_result buffers */
	memset(key_data, 0, key_len); /* also p_bytes */
	memset(salt_data, 0, salt_len); /* also s_bytes */
	free(key_data);
	free(salt_data);
#undef p_bytes
#undef s_bytes

	return result;
#undef alt_result
#undef temp_result
#undef ctx
#undef alt_ctx
}
Exemple #28
0
int kill_main(int argc, char **argv)
{
	char *arg;
	pid_t pid;
	int signo = SIGTERM, errors = 0, quiet = 0;
#if !ENABLE_KILLALL && !ENABLE_KILLALL5
#define killall 0
#define killall5 0
#else
/* How to determine who we are? find 3rd char from the end:
 * kill, killall, killall5
 *  ^i       ^a        ^l  - it's unique
 * (checking from the start is complicated by /bin/kill... case) */
	const char char3 = argv[0][strlen(argv[0]) - 3];
#define killall (ENABLE_KILLALL && char3 == 'a')
#define killall5 (ENABLE_KILLALL5 && char3 == 'l')
#endif

	/* Parse any options */
	argc--;
	arg = *++argv;

	if (argc < 1 || arg[0] != '-') {
		goto do_it_now;
	}

	/* The -l option, which prints out signal names.
	 * Intended usage in shell:
	 * echo "Died of SIG`kill -l $?`"
	 * We try to mimic what kill from coreutils-6.8 does */
	if (arg[1] == 'l' && arg[2] == '\0') {
		if (argc == 1) {
			/* Print the whole signal list */
			print_signames();
			return 0;
		}
		/* -l <sig list> */
		while ((arg = *++argv)) {
			if (isdigit(arg[0])) {
				signo = bb_strtou(arg, NULL, 10);
				if (errno) {
					bb_error_msg("unknown signal '%s'", arg);
					return EXIT_FAILURE;
				}
				/* Exitcodes >= 0x80 are to be treated
				 * as "killed by signal (exitcode & 0x7f)" */
				puts(get_signame(signo & 0x7f));
				/* TODO: 'bad' signal# - coreutils says:
				 * kill: 127: invalid signal
				 * we just print "127" instead */
			} else {
				signo = get_signum(arg);
				if (signo < 0) {
					bb_error_msg("unknown signal '%s'", arg);
					return EXIT_FAILURE;
				}
				printf("%d\n", signo);
			}
		}
		/* If they specified -l, we are all done */
		return EXIT_SUCCESS;
	}

	/* The -q quiet option */
	if (killall && arg[1] == 'q' && arg[2] == '\0') {
		quiet = 1;
		arg = *++argv;
		argc--;
		if (argc < 1)
			bb_show_usage();
		if (arg[0] != '-')
			goto do_it_now;
	}

	arg++; /* skip '-' */

	/* -o PID? (if present, it always is at the end of command line) */
	if (killall5 && arg[0] == 'o')
		goto do_it_now;

	if (argc > 1 && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
		argc--;
		arg = *++argv;
	} /* else it must be -SIG */
	signo = get_signum(arg);
	if (signo < 0) { /* || signo > MAX_SIGNUM ? */
		bb_error_msg("bad signal name '%s'", arg);
		return EXIT_FAILURE;
	}
	arg = *++argv;
	argc--;

 do_it_now:
	pid = getpid();

	if (killall5) {
		pid_t sid;
		procps_status_t* p = NULL;
		int ret = 0;

		/* Find out our session id */
		sid = getsid(pid);
		/* Stop all processes */
		if (signo != SIGSTOP && signo != SIGCONT)
			kill(-1, SIGSTOP);
		/* Signal all processes except those in our session */
		while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) {
			int i;

			if (p->sid == (unsigned)sid
			 || p->pid == (unsigned)pid
			 || p->pid == 1
			) {
				continue;
			}

			/* All remaining args must be -o PID options.
			 * Check p->pid against them. */
			for (i = 0; i < argc; i++) {
				pid_t omit;

				arg = argv[i];
				if (arg[0] != '-' || arg[1] != 'o') {
					bb_error_msg("bad option '%s'", arg);
					ret = 1;
					goto resume;
				}
				arg += 2;
				if (!arg[0] && argv[++i])
					arg = argv[i];
				omit = bb_strtoi(arg, NULL, 10);
				if (errno) {
					bb_error_msg("invalid number '%s'", arg);
					ret = 1;
					goto resume;
				}
				if (p->pid == omit)
					goto dont_kill;
			}
			kill(p->pid, signo);
 dont_kill: ;
		}
 resume:
		/* And let them continue */
		if (signo != SIGSTOP && signo != SIGCONT)
			kill(-1, SIGCONT);
		return ret;
	}

	/* Pid or name is required for kill/killall */
	if (argc < 1) {
		bb_error_msg("you need to specify whom to kill");
		return EXIT_FAILURE;
	}

	if (killall) {
		/* Looks like they want to do a killall.  Do that */
		while (arg) {
			pid_t* pidList;

			pidList = find_pid_by_name(arg);
			if (*pidList == 0) {
				errors++;
				if (!quiet)
					bb_error_msg("%s: no process killed", arg);
			} else {
				pid_t *pl;

				for (pl = pidList; *pl; pl++) {
					if (*pl == pid)
						continue;
					if (kill(*pl, signo) == 0)
						continue;
					errors++;
					if (!quiet)
						bb_perror_msg("can't kill pid %d", (int)*pl);
				}
			}
			free(pidList);
			arg = *++argv;
		}
		return errors;
	}

	/* Looks like they want to do a kill. Do that */
	while (arg) {
#if ENABLE_ASH || ENABLE_HUSH
		/*
		 * We need to support shell's "hack formats" of
		 * " -PRGP_ID" (yes, with a leading space)
		 * and " PID1 PID2 PID3" (with degenerate case "")
		 */
		while (*arg != '\0') {
			char *end;
			if (*arg == ' ')
				arg++;
			pid = bb_strtoi(arg, &end, 10);
			if (errno && (errno != EINVAL || *end != ' ')) {
				bb_error_msg("invalid number '%s'", arg);
				errors++;
				break;
			}
			if (kill(pid, signo) != 0) {
				bb_perror_msg("can't kill pid %d", (int)pid);
				errors++;
			}
			arg = end; /* can only point to ' ' or '\0' now */
		}
#else
		pid = bb_strtoi(arg, NULL, 10);
		if (errno) {
			bb_error_msg("invalid number '%s'", arg);
			errors++;
		} else if (kill(pid, signo) != 0) {
			bb_perror_msg("can't kill pid %d", (int)pid);
			errors++;
		}
#endif
		arg = *++argv;
	}
	return errors;
}