static void connection_status(void)
{
	/* "only 1 client max" desn't need this */
	if (cmax > 1)
		bb_error_msg("status %u/%u", cnum, cmax);
}
Beispiel #2
0
/*
 * Read commands until we are told to stop.
 */
static void doCommands(void)
{
	const char *cp;
	char *endbuf, *newname, buf[USERSIZE];
	int len, num1, num2, have1, have2;

	while (TRUE) {
		printf(": ");
		fflush(stdout);

		if (fgets(buf, sizeof(buf), stdin) == NULL)
			return;

		len = strlen(buf);

		if (len == 0)
			return;

		endbuf = &buf[len - 1];

		if (*endbuf != '\n') {
			bb_error_msg("command line too long");

			do {
				len = fgetc(stdin);
			} while ((len != EOF) && (len != '\n'));

			continue;
		}

		while ((endbuf > buf) && isblank(endbuf[-1]))
			endbuf--;

		*endbuf = '\0';

		cp = buf;

		while (isblank(*cp))
			cp++;

		have1 = FALSE;
		have2 = FALSE;

		if ((curNum == 0) && (lastNum > 0)) {
			curNum = 1;
			curLine = lines.next;
		}

		if (!getNum(&cp, &have1, &num1))
			continue;

		while (isblank(*cp))
			cp++;

		if (*cp == ',') {
			cp++;

			if (!getNum(&cp, &have2, &num2))
				continue;

			if (!have1)
				num1 = 1;

			if (!have2)
				num2 = lastNum;

			have1 = TRUE;
			have2 = TRUE;
		}

		if (!have1)
			num1 = curNum;

		if (!have2)
			num2 = num1;

		switch (*cp++) {
			case 'a':
				addLines(num1 + 1);
				break;

			case 'c':
				deleteLines(num1, num2);
				addLines(num1);
				break;

			case 'd':
				deleteLines(num1, num2);
				break;

			case 'f':
				if (*cp && !isblank(*cp)) {
					bb_error_msg("bad file command");
					break;
				}

				while (isblank(*cp))
					cp++;

				if (*cp == '\0') {
					if (fileName)
						printf("\"%s\"\n", fileName);
					else
						printf("No file name\n");
					break;
				}

				newname = strdup(cp);

				if (newname == NULL) {
					bb_error_msg("no memory for file name");
					break;
				}

				if (fileName)
					free(fileName);

				fileName = newname;
				break;

			case 'i':
				addLines(num1);
				break;

			case 'k':
				while (isblank(*cp))
					cp++;

				if ((*cp < 'a') || (*cp > 'a') || cp[1]) {
					bb_error_msg("bad mark name");
					break;
				}

				marks[*cp - 'a'] = num2;
				break;

			case 'l':
				printLines(num1, num2, TRUE);
				break;

			case 'p':
				printLines(num1, num2, FALSE);
				break;

			case 'q':
				while (isblank(*cp))
					cp++;

				if (have1 || *cp) {
					bb_error_msg("bad quit command");
					break;
				}

				if (!dirty)
					return;

				printf("Really quit? ");
				fflush(stdout);

				buf[0] = '\0';
				fgets(buf, sizeof(buf), stdin);
				cp = buf;

				while (isblank(*cp))
					cp++;

				if ((*cp == 'y') || (*cp == 'Y'))
					return;

				break;

			case 'r':
				if (*cp && !isblank(*cp)) {
					bb_error_msg("bad read command");
					break;
				}

				while (isblank(*cp))
					cp++;

				if (*cp == '\0') {
					bb_error_msg("no file name");
					break;
				}

				if (!have1)
					num1 = lastNum;

				if (readLines(cp, num1 + 1))
					break;

				if (fileName == NULL)
					fileName = strdup(cp);

				break;

			case 's':
				subCommand(cp, num1, num2);
				break;

			case 'w':
				if (*cp && !isblank(*cp)) {
					bb_error_msg("bad write command");
					break;
				}

				while (isblank(*cp))
					cp++;

				if (!have1) {
					num1 = 1;
					num2 = lastNum;
				}

				if (*cp == '\0')
					cp = fileName;

				if (cp == NULL) {
					bb_error_msg("no file name specified");
					break;
				}

				writeLines(cp, num1, num2);
				break;

			case 'z':
				switch (*cp) {
				case '-':
					printLines(curNum-21, curNum, FALSE);
					break;
				case '.':
					printLines(curNum-11, curNum+10, FALSE);
					break;
				default:
					printLines(curNum, curNum+21, FALSE);
					break;
				}
				break;

			case '.':
				if (have1) {
					bb_error_msg("no arguments allowed");
					break;
				}

				printLines(curNum, curNum, FALSE);
				break;

			case '-':
				if (setCurNum(curNum - 1))
					printLines(curNum, curNum, FALSE);

				break;

			case '=':
				printf("%d\n", num1);
				break;

			case '\0':
				if (have1) {
					printLines(num2, num2, FALSE);
					break;
				}

				if (setCurNum(curNum + 1))
					printLines(curNum, curNum, FALSE);

				break;

			default:
				bb_error_msg("unimplemented command");
				break;
		}
	}
}
//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 err;
	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;

	errno = err = 0;

	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;
			// Setting it to more than 1 breaks poll():
			// it blocks even if there's data. !??
			//tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
			/* reads would block only if < 1 char is available */
			tty.c_cc[VMIN] = 1;
			/* no timeout (reads block forever) */
			tty.c_cc[VTIME] = 0;
		}
		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;
		struct pollfd pfd[1];
		int timeout;

		if ((bufpos & 0xff) == 0)
			buffer = xrealloc(buffer, bufpos + 0x101);

		timeout = -1;
		if (end_ms) {
			timeout = end_ms - (unsigned)monotonic_ms();
			if (timeout <= 0) { /* already late? */
				retval = (const char *)(uintptr_t)1;
				goto ret;
			}
		}

		/* We must poll even if timeout is -1:
		 * we want to be interrupted if signal arrives,
		 * regardless of SA_RESTART-ness of that signal!
		 */
		errno = 0;
		pfd[0].fd = fd;
		pfd[0].events = POLLIN;
		if (poll(pfd, 1, timeout) != 1) {
			/* timed out, or EINTR */
			err = errno;
			retval = (const char *)(uintptr_t)1;
			goto ret;
		}
		if (read(fd, &buffer[bufpos], 1) != 1) {
			err = errno;
			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);

	errno = err;
	return retval;
}
static int prtdepend(const char *base_dir, const char *module_dir, int nflag)
{
	FILE *dep = stdout;
	FILE *generic_string = stdout;
	MODULE **tbdep;
	MODULE *ptmod;
	int i;
	int ret = 0;
	int skipchars;	/* For depmod -a in image of a tree */
	char *tmdn;

	tmdn = concat_path_file(module_dir, "modules.dep");

	if (!nflag) {
		dep = fopen(tmdn, "w");
	}

	skipchars = strlen(base_dir);
	tbdep = (MODULE **)alloca(sizeof(MODULE) * n_modules);

	ptmod = modules;
	for (i = 0; i < n_modules; i++, ptmod++) {
		SYMBOL **undefs = ptmod->undefs.symtab;
		int n_undefs = ptmod->undefs.n_syms;
		int nbdepmod = 0;
		int nberr = 0;
		int e;
		int m;

		/* Don't print a dependency list for the _kernel_ ... */
		if (strcmp(ptmod->name, "-") == 0)
			continue;

		/* Look for unresolved symbols in this module */
		for (e = 0; e < n_undefs; e++, undefs++) {
			MODULE *mod = (*undefs)->module;

			if ((*undefs)->status == SYM_RESOLVED) {
				/* Resolved by self or exported kernel symbol */
				if (strcmp(mod->name, ptmod->name) == 0 ||
				    strcmp(mod->name, "-") == 0)
					continue;

				/* No duplicate dependencies, please */
				for (m = 0; m < nbdepmod; m++) {
					if (tbdep[m] == mod)
						break;
				}
				if (m == nbdepmod)
					tbdep[nbdepmod++] = mod;
			} else {
				/* Kludge to preserve old depmod behaviour without -u.
				 * 2.4.13 change to keep going had the unwanted side
				 * effect of always treating unresolved symbols as an
				 * error.  Use the error() routine but do not count
				 * any errors.  Remove in 2.5.
				 */
				if (!flag_quiet && nberr == 0)
					bb_error_msg("*** Unresolved symbols in %s",
					      ptmod->name);
				if (!flag_quiet && flag_showerror)
						bb_error_msg("\t%s", (*undefs)->name);
				nberr++;
				if (flag_unresolved_error)
					ret = 1;
			}
		}

		fprintf(dep, "%s:", ptmod->name + skipchars);
		for (m = 0; m < nbdepmod; m++) {
			if (m != 0 /*&& (m & 3) == 0*/)
				fprintf(dep, " \\\n");
			fprintf(dep, "\t%s", tbdep[m]->name + skipchars);
		}
		fprintf(dep, "\n\n");
	}

	if (generic_string != stdout)
		fclose(generic_string);
	/* Close depfile last, it's timestamp is critical */
	if (dep != stdout)
		fclose(dep);
	return ret;
}
Beispiel #5
0
/*
 * Parse a line number argument if it is present.  This is a sum
 * or difference of numbers, '.', '$', 'x, or a search string.
 * Returns TRUE if successful (whether or not there was a number).
 * Returns FALSE if there was a parsing error, with a message output.
 * Whether there was a number is returned indirectly, as is the number.
 * The character pointer which stopped the scan is also returned.
 */
static int getNum(const char **retcp, int *retHaveNum, int *retNum)
{
	const char *cp;
	char *endStr, str[USERSIZE];
	int haveNum, value, num, sign;

	cp = *retcp;
	haveNum = FALSE;
	value = 0;
	sign = 1;

	while (TRUE) {
		while (isblank(*cp))
			cp++;

		switch (*cp) {
			case '.':
				haveNum = TRUE;
				num = curNum;
				cp++;
				break;

			case '$':
				haveNum = TRUE;
				num = lastNum;
				cp++;
				break;

			case '\'':
				cp++;

				if ((*cp < 'a') || (*cp > 'z')) {
					bb_error_msg("bad mark name");
					return FALSE;
				}

				haveNum = TRUE;
				num = marks[*cp++ - 'a'];
				break;

			case '/':
				strcpy(str, ++cp);
				endStr = strchr(str, '/');

				if (endStr) {
					*endStr++ = '\0';
					cp += (endStr - str);
				}
				else
					cp = "";

				num = searchLines(str, curNum, lastNum);

				if (num == 0)
					return FALSE;

				haveNum = TRUE;
				break;

			default:
				if (!isdigit(*cp)) {
					*retcp = cp;
					*retHaveNum = haveNum;
					*retNum = value;
					return TRUE;
				}

				num = 0;

				while (isdigit(*cp))
					num = num * 10 + *cp++ - '0';

				haveNum = TRUE;
				break;
		}

		value += num * sign;

		while (isblank(*cp))
			cp++;

		switch (*cp) {
			case '-':
				sign = -1;
				cp++;
				break;

			case '+':
				sign = 1;
				cp++;
				break;

			default:
				*retcp = cp;
				*retHaveNum = haveNum;
				*retNum = value;
				return TRUE;
		}
	}
}
Beispiel #6
0
int i2cget_main(int argc UNUSED_PARAM, char **argv)
{
	const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
	const char *const optstr = "fy";

	int bus_num, bus_addr, data_addr = -1, status;
	int mode = I2C_SMBUS_BYTE, pec = 0, fd;
	unsigned opts;

        opt_complementary = "-2:?4"; /* from 2 to 4 args */
	opts = getopt32(argv, optstr);
	argv += optind;

	bus_num = i2c_bus_lookup(argv[0]);
	bus_addr = i2c_parse_bus_addr(argv[1]);

	if (argv[2]) {
		data_addr = i2c_parse_data_addr(argv[2]);
		mode = I2C_SMBUS_BYTE_DATA;
		if (argv[3]) {
			switch (argv[3][0]) {
			case 'b':	/* Already set */		break;
			case 'w':	mode = I2C_SMBUS_WORD_DATA;	break;
			case 'c':	mode = I2C_SMBUS_BYTE;		break;
			default:
				bb_error_msg("invalid mode");
				bb_show_usage();
			}
			pec = argv[3][1] == 'p';
		}
	}

	fd = i2c_dev_open(bus_num);
	check_read_funcs(fd, mode, data_addr, pec);
	i2c_set_slave_addr(fd, bus_addr, opts & opt_f);

	if (!(opts & opt_y))
		confirm_action(bus_addr, mode, data_addr, pec);

	if (pec)
		i2c_set_pec(fd, 1);

	switch (mode) {
	case I2C_SMBUS_BYTE:
		if (data_addr >= 0) {
			status = i2c_smbus_write_byte(fd, data_addr);
			if (status < 0)
				bb_error_msg("warning - write failed");
		}
		status = i2c_smbus_read_byte(fd);
		break;
	case I2C_SMBUS_WORD_DATA:
		status = i2c_smbus_read_word_data(fd, data_addr);
		break;
	default: /* I2C_SMBUS_BYTE_DATA */
		status = i2c_smbus_read_byte_data(fd, data_addr);
	}
	close(fd);

	if (status < 0)
		bb_perror_msg_and_die("read failed");

	printf("0x%0*x\n", mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);

	return 0;
}
/*
 *	Read the symbols in an object and register them in the symbol table.
 *	Return -1 if there is an error.
 */
static int loadobj(const char *objname, int ignore_suffix)
{
	static char *currdir;
	static int currdir_len;
	int fp;
	MODULE *mod;
	SYMBOL *undefs[5000];
	SYMBOL *defsym[5000];
	struct obj_file *f;
	struct obj_section *sect;
	struct obj_symbol *objsym;
	int i;
	int is_2_2;
	int ksymtab;
	int len;
	int n_undefs = 0;
	int n_defsym = 0;
	char *p;
	void *image;
	unsigned long m_size;

	p = strrchr(objname, '/');
	len = 1 + (int)(p - objname);

	if ((fp = open(objname, O_RDONLY)) < 0)
		return 1;

	if (!(f = obj_load(fp, ET_REL, objname))) {
		close(fp);
		return -1;
	}
	close(fp);

	/*
	 * Allow arch code to define _GLOBAL_OFFSET_TABLE_
	 */
	arch_create_got(f);

	/*
	 * If we have changed base directory
	 * then use the defined symbols from modules
	 * in the _same_ directory to resolve whatever
	 * undefined symbols there are.
	 *
	 * This strategy ensures that we will have
	 * as correct dependencies as possible,
	 * even if the same symbol is defined by
	 * other modules in other directories.
	 */
	if (currdir_len != len || currdir == NULL ||
	    strncmp(currdir, objname, len) != 0) {
		if (currdir)
			resolve();
		currdir = bb_xstrdup(objname);
		currdir_len = len;
	}

	mod = modules + n_modules++;
	mod->name = bb_xstrdup(objname);

	if ((sect = obj_find_section(f, "__ksymtab")) != NULL)
		ksymtab = sect->idx; /* Only in 2.2 (or at least not 2.0) */
	else
		ksymtab = -1;

	if (sect ||
	    obj_find_section(f, ".modinfo") ||
	    obj_find_symbol(f, "__this_module"))
		is_2_2 = 1;
	else
		is_2_2 = 0;

	for (i = 0; i < HASH_BUCKETS; ++i) {
		for (objsym = f->symtab[i]; objsym; objsym = objsym->next) {
			if (objsym->secidx == SHN_UNDEF) {
				if (ELFW(ST_BIND)(objsym->info) != STB_WEAK &&
				    objsym->r_type /* assumes that R_arch_NONE is always 0 on all arch */) {
					undefs[n_undefs++] = addsym(objsym->name,
								    mod,
								    SYM_UNDEF,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2 && ksymtab != -1) {
				/* A 2.2 module using EXPORT_SYMBOL */
				if (objsym->secidx == ksymtab &&
				    ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					/* Ignore leading "__ksymtab_" */
					defsym[n_defsym++] = addsym(objsym->name + 10,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2) {
				/*
				 * A 2.2 module _not_ using EXPORT_SYMBOL
				 * It might still want to export symbols,
				 * although strictly speaking it shouldn't...
				 * (Seen in pcmcia)
				 */
				if (ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					defsym[n_defsym++] = addsym(objsym->name,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			/* Not undefined or 2.2 ksymtab entry */
			if (objsym->secidx < SHN_LORESERVE
			/*
			 * The test below is removed for 2.0 compatibility
			 * since some 2.0-modules (correctly) hide the
			 * symbols it exports via register_symtab()
			 */
			/* && ELFW(ST_BIND)(objsym->info) == STB_GLOBAL */
			     ) {
				defsym[n_defsym++] = addsym(objsym->name,
							    mod,
							    SYM_DEFINED,
							    ignore_suffix);
			}
		}
	}

	/*
	 *	Finalize the information about a module.
	 */
	mod->defsym.n_syms = n_defsym;
	if (n_defsym > 0) {
		int size = n_defsym * sizeof(SYMBOL *);

		mod->defsym.symtab = (SYMBOL **)xmalloc(size);
		memcpy(mod->defsym.symtab, defsym, size);
	} else
		mod->defsym.symtab = NULL;

	mod->undefs.n_syms = n_undefs;
	if (n_undefs > 0) {
		int size = n_undefs * sizeof(SYMBOL *);

		mod->undefs.symtab = (SYMBOL **) xmalloc(size);
		memcpy(mod->undefs.symtab, undefs, size);
		mod->resolved = 0;
	} else {
		mod->undefs.symtab = NULL;
		mod->resolved = 1;
	}

	/* Do a pseudo relocation to base address 0x1000 (arbitrary).
	 * All undefined symbols are treated as absolute 0.  This builds
	 * enough of a module to allow extraction of internal data such
	 * as device tables.
	 */
	obj_clear_undefineds(f);
	obj_allocate_commons(f);
	m_size = obj_load_size(f);
	if (!obj_relocate(f, 0x1000)) {
		bb_error_msg("depmod obj_relocate failed\n");
		return(-1);
	}
	extract_generic_string(f, mod);
	image = xmalloc(m_size);
	obj_create_image(f, image);
	free(image);

	obj_free(f);
	return 0;
}
Beispiel #8
0
int su_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned flags;
	char *opt_shell = NULL;
	char *opt_command = NULL;
	const char *opt_username = "******";
	struct passwd *pw;
	uid_t cur_uid = getuid();
	const char *tty;
#if ENABLE_FEATURE_UTMP
	char user_buf[64];
#endif
	const char *old_user;

	flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
	//argc -= optind;
	argv += optind;

	if (argv[0] && LONE_DASH(argv[0])) {
		flags |= SU_OPT_l;
		argv++;
	}

	/* get user if specified */
	if (argv[0]) {
		opt_username = argv[0];
		argv++;
	}

	if (ENABLE_FEATURE_SU_SYSLOG) {
		/* The utmp entry (via getlogin) is probably the best way to
		 * identify the user, especially if someone su's from a su-shell.
		 * But getlogin can fail -- usually due to lack of utmp entry.
		 * in this case resort to getpwuid.  */
#if ENABLE_FEATURE_UTMP
		old_user = user_buf;
		if (getlogin_r(user_buf, sizeof(user_buf)) != 0)
#endif
		{
			pw = getpwuid(cur_uid);
			old_user = pw ? xstrdup(pw->pw_name) : "";
		}
		tty = xmalloc_ttyname(2);
		if (!tty) {
			tty = "none";
		}
		openlog(applet_name, 0, LOG_AUTH);
	}

	pw = xgetpwnam(opt_username);

	if (cur_uid == 0 || ask_and_check_password(pw) > 0) {
		if (ENABLE_FEATURE_SU_SYSLOG)
			syslog(LOG_NOTICE, "%c %s %s:%s",
				'+', tty, old_user, opt_username);
	} else {
		if (ENABLE_FEATURE_SU_SYSLOG)
			syslog(LOG_NOTICE, "%c %s %s:%s",
				'-', tty, old_user, opt_username);
		bb_do_delay(LOGIN_FAIL_DELAY);
		bb_error_msg_and_die("incorrect password");
	}

	if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
		closelog();
	}

	if (!opt_shell && (flags & SU_OPT_mp)) {
		/* -s SHELL is not given, but "preserve env" opt is */
		opt_shell = getenv("SHELL");
	}

#if ENABLE_FEATURE_SU_CHECKS_SHELLS
	if (opt_shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) {
		/* The user being su'd to has a nonstandard shell, and so is
		 * probably a uucp account or has restricted access.  Don't
		 * compromise the account by allowing access with a standard
		 * shell.  */
		bb_error_msg("using restricted shell");
		opt_shell = NULL; /* ignore -s PROG */
	}
	/* else: user can run whatever he wants via "su -s PROG USER".
	 * This is safe since PROG is run under user's uid/gid. */
#endif
	if (!opt_shell)
		opt_shell = pw->pw_shell;

	change_identity(pw);
	setup_environment(opt_shell,
			((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV)
			+ (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV)
			+ (!(flags & SU_OPT_l) * SETUP_ENV_NO_CHDIR),
			pw);
	IF_SELINUX(set_current_security_context(NULL);)
Beispiel #9
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:

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

		/* Now stop all processes */
		kill(-1, SIGSTOP);
		/* Find out our own session id */
		pid = getpid();
		sid = getsid(pid);
		/* Now kill all processes except our session */
		while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
			if (p->sid != sid && p->pid != 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 */
		pid = getpid();
		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;
}
Beispiel #10
0
int sulogin_main(int argc UNUSED_PARAM, char **argv)
{
	int timeout = 0;
	struct passwd *pwd;
	const char *shell;

	/* Note: sulogin is not a suid app. It is meant to be run by init
	 * for single user / emergency mode. init starts it as root.
	 * Normal users (potentially malisious ones) can only run it under
	 * their UID, therefore no paranoia here is warranted:
	 * $LD_LIBRARY_PATH in env, TTY = /dev/sda
	 * are no more dangerous here than in e.g. cp applet.
	 */

	logmode = LOGMODE_BOTH;
	openlog(applet_name, 0, LOG_AUTH);

	getopt32(argv, "t:+", &timeout);
	argv += optind;

	if (argv[0]) {
		close(0);
		close(1);
		dup(xopen(argv[0], O_RDWR));
		close(2);
		dup(0);
	}

	pwd = getpwuid(0);
	if (!pwd) {
		bb_error_msg_and_die("no password entry for root");
	}

	while (1) {
		int r;

		r = ask_and_check_password_extended(pwd, timeout,
			"Give root password for system maintenance\n"
			"(or type Control-D for normal startup):"
		);
		if (r < 0) {
			/* ^D, ^C, timeout, or read error */
			bb_error_msg("normal startup");
			return 0;
		}
		if (r > 0) {
			break;
		}
		bb_do_delay(LOGIN_FAIL_DELAY);
		bb_error_msg("Login incorrect");
	}

	bb_error_msg("starting shell for system maintenance");

	IF_SELINUX(renew_current_security_context());

	shell = getenv("SUSHELL");
	if (!shell)
		shell = getenv("sushell");
	if (!shell)
		shell = pwd->pw_shell;

	/* Exec login shell with no additional parameters. Never returns. */
	run_shell(shell, 1, NULL);
}
Beispiel #11
0
/*
 * Builds the dependency list (aka stack) of a module.
 * head: the highest module in the stack (last to insmod, first to rmmod)
 * tail: the lowest module in the stack (first to insmod, last to rmmod)
 */
static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
{
	struct mod_list_t *find;
	struct dep_t *dt;
	struct mod_opt_t *opt = 0;
	char *path = 0;

	/* Search for the given module name amongst all dependency rules.
	 * The module name in a dependency rule can be a shell pattern,
	 * so try to match the given module name against such a pattern.
	 * Of course if the name in the dependency rule is a plain string,
	 * then we consider it a pattern, and matching will still work. */
	for ( dt = depend; dt; dt = dt-> m_next ) {
		if ( fnmatch ( dt-> m_name, mod, 0 ) == 0) {
			break;
		}
	}

	if( !dt ) {
		bb_error_msg ("module %s not found.", mod);
		return;
	}

	// resolve alias names
	while ( dt-> m_isalias ) {
		if ( dt-> m_depcnt == 1 ) {
			struct dep_t *adt;

			for ( adt = depend; adt; adt = adt-> m_next ) {
				if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 )
					break;
			}
			if ( adt ) {
				/* This is the module we are aliased to */
				struct mod_opt_t *opts = dt-> m_options;
				/* Option of the alias are appended to the options of the module */
				while( opts ) {
					adt-> m_options = append_option( adt-> m_options, opts-> m_opt_val );
					opts = opts-> m_next;
				}
				dt = adt;
			}
			else {
				bb_error_msg ("module %s not found.", mod);
				return;
			}
		}
		else {
			bb_error_msg ("Bad alias %s", dt-> m_name);
			return;
		}
	}

	mod = dt-> m_name;
	path = dt-> m_path;
	opt = dt-> m_options;

	// search for duplicates
	for ( find = *head; find; find = find-> m_next ) {
		if ( !strcmp ( mod, find-> m_name )) {
			// found -> dequeue it

			if ( find-> m_prev )
				find-> m_prev-> m_next = find-> m_next;
			else
				*head = find-> m_next;

			if ( find-> m_next )
				find-> m_next-> m_prev = find-> m_prev;
			else
				*tail = find-> m_prev;

			break; // there can be only one duplicate
		}
	}

	if ( !find ) { // did not find a duplicate
		find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
		find-> m_name = mod;
		find-> m_path = path;
		find-> m_options = opt;
	}

	// enqueue at tail
	if ( *tail )
		(*tail)-> m_next = find;
	find-> m_prev   = *tail;
	find-> m_next   = 0;

	if ( !*head )
		*head = find;
	*tail = find;

	if ( dt ) {
		int i;

		/* Add all dependable module for that new module */
		for ( i = 0; i < dt-> m_depcnt; i++ )
			check_dep ( dt-> m_deparr [i], head, tail );
	}
}
Beispiel #12
0
static void warn3x(const char *m1, const char *m2, const char *m3)
{
	bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
}
Beispiel #13
0
static void sig_term_handler(int sig)
{
	if (verbose)
		bb_error_msg("got signal %u, exit", sig);
	kill_myself_with_sig(sig);
}
Beispiel #14
0
int tcpudpsvd_main(int argc UNUSED_PARAM, 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;
	unsigned opts;

	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
	opts = 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 */
	opts = 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 (opts & 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 (opts & OPT_u) {
		xget_uidgid(&ugid, user);
	}
#ifdef SSLSVD
	if (opts & OPT_U) ssluser = optarg;
	if (opts & OPT_slash) root = optarg;
	if (opts & OPT_Z) cert = optarg;
	if (opts & 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) && !(opts & OPT_u)) {
		xfunc_exitcode = 100;
		bb_error_msg_and_die(bb_msg_you_must_be_root);
	}
	if (opts & OPT_u)
		if (!uidgid_get(&sslugid, ssluser, 1)) {
			if (errno) {
				bb_perror_msg_and_die("can't 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("can't initialize ssl");
	if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
		if (client)
			fatal("can't read cert, key, or ca file");
		fatal("can't read cert or key file");
	}
	if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
		fatal("can't 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);
		close_on_exec_on(sock);
	} 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 (opts & OPT_u) {
		/* drop permissions */
		xsetgid(ugid.gid);
		xsetuid(ugid.uid);
	}
#endif

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

	/* 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 */

	{ /* 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 || !(opts & OPT_E)) {
			if (!max_per_host) /* remote_addr is not yet known */
				free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
			if (opts & OPT_h) {
				free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
				if (!remote_hostname) {
					bb_error_msg("can't 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 (opts & 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("can't 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((opts & OPT_h)
				? "start %u %s-%s (%s-%s)"
				: "start %u %s-%s",
				pid,
				local_addr, remote_addr,
				local_hostname, remote_hostname);
		}

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

#ifdef SO_ORIGINAL_DST
			/* 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);
			}
#endif
			xsetenv_plain("PROTO", proto);
			xsetenv_proto(proto, "LOCALADDR", local_addr);
			xsetenv_proto(proto, "REMOTEADDR", remote_addr);
			if (opts & 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(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */
	/* Non-ignored signals revert to SIG_DFL on exec anyway */
	/*signal(SIGCHLD, SIG_DFL);*/
	sig_unblock(SIGCHLD);

#ifdef SSLSVD
	strcpy(id, utoa(pid));
	ssl_io(0, argv);
	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
#else
	BB_EXECVP_or_die(argv);
#endif
}
Beispiel #15
0
static void send_r_n(const char *s)
{
	if (verbose)
		bb_error_msg("send:'%s'", s);
	printf("%s\r\n", s);
}
Beispiel #16
0
static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
{
	struct icmp6_hdr *icmppkt;
	struct timeval tv, *tp;
	int dupflag;
	unsigned long triptime;
	char buf[INET6_ADDRSTRLEN];

	gettimeofday(&tv, NULL);

	/* discard if too short */
	if (sz < (datalen + sizeof(struct icmp6_hdr)))
		return;

	icmppkt = (struct icmp6_hdr *) packet;
	if (icmppkt->icmp6_id != myid)
		return;				/* not our ping */

	if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
		++nreceived;
		tp = (struct timeval *) &icmppkt->icmp6_data8[4];

		if ((tv.tv_usec -= tp->tv_usec) < 0) {
			--tv.tv_sec;
			tv.tv_usec += 1000000;
		}
		tv.tv_sec -= tp->tv_sec;

		triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
		tsum += triptime;
		if (triptime < tmin)
			tmin = triptime;
		if (triptime > tmax)
			tmax = triptime;

		if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) {
			++nrepeats;
			--nreceived;
			dupflag = 1;
		} else {
			SET(icmppkt->icmp6_seq % MAX_DUP_CHK);
			dupflag = 0;
		}

		if (option_mask32 & OPT_QUIET)
			return;

		printf("%d bytes from %s: icmp6_seq=%u", sz,
			   inet_ntop(AF_INET6, &pingaddr.sin6_addr,
						 buf, sizeof(buf)),
			   ntohs(icmppkt->icmp6_seq));
		printf(" ttl=%d time=%lu.%lu ms", hoplimit,
			   triptime / 10, triptime % 10);
		if (dupflag)
			printf(" (DUP!)");
		puts("");
	} else
		if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
			bb_error_msg("warning: got ICMP %d (%s)",
					icmppkt->icmp6_type, icmp6_type_name(icmppkt->icmp6_type));
}
Beispiel #17
0
/*
 *     Write a single sysctl setting
 */
int sysctl_write_setting(const char *setting, int output)
{
	int retval = 0;
	const char *name = setting;
	const char *value;
	const char *equals;
	char *tmpname, *outname, *cptr;
	int fd = -1;

	if (!name)			/* probably dont' want to display this  err */
		return 0;

	if (!(equals = strchr(setting, '='))) {
		bb_error_msg(ERR_NO_EQUALS, setting);
		return -1;
	}

	value = equals + sizeof(char);	/* point to the value in name=value */

	if (!*name || !*value || name == equals) {
		bb_error_msg(ERR_MALFORMED_SETTING, setting);
		return -2;
	}

	tmpname = bb_xasprintf("%s%.*s", PROC_PATH, (equals - name), name);
	outname = bb_xstrdup(tmpname + strlen(PROC_PATH));

	while ((cptr = strchr(tmpname, '.')) != NULL)
		*cptr = '/';

	while ((cptr = strchr(outname, '/')) != NULL)
		*cptr = '.';

	if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
		switch (errno) {
		case ENOENT:
			bb_error_msg(ERR_INVALID_KEY, outname);
			break;
		case EACCES:
			bb_perror_msg(ERR_PERMISSION_DENIED, outname);
			break;
		default:
			bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
			break;
		}
		retval = -1;
	} else {
		dwrite_str(fd, value);
		close(fd);
		if (output) {
			dwrite_str(STDOUT_FILENO, outname);
			dwrite_str(STDOUT_FILENO, " = ");
		}
		dwrite_str(STDOUT_FILENO, value);
		dwrite_str(STDOUT_FILENO, "\n");
	}

	/* cleanup */
	free(tmpname);
	free(outname);
	return retval;
}						/* end sysctl_write_setting() */
Beispiel #18
0
static int writeTarHeader(struct TarBallInfo *tbInfo,
		const char *header_name, const char *fileName, struct stat *statbuf)
{
	struct TarHeader header;

	if (sizeof(header) != 512)
		BUG_tar_header_size();

	memset(&header, 0, sizeof(struct TarHeader));

	strncpy(header.name, header_name, sizeof(header.name));

	/* POSIX says to mask mode with 07777. */
	PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
	PUT_OCTAL(header.uid, statbuf->st_uid);
	PUT_OCTAL(header.gid, statbuf->st_gid);
	memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
	PUT_OCTAL(header.mtime, statbuf->st_mtime);

	/* Enter the user and group names */
	safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
	safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));

	if (tbInfo->hlInfo) {
		/* This is a hard link */
		header.typeflag = LNKTYPE;
		strncpy(header.linkname, tbInfo->hlInfo->name,
				sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
		/* Write out long linkname if needed */
		if (header.linkname[sizeof(header.linkname)-1])
			writeLongname(tbInfo->tarFd, GNULONGLINK,
					tbInfo->hlInfo->name, 0);
#endif
	} else if (S_ISLNK(statbuf->st_mode)) {
		char *lpath = xmalloc_readlink_or_warn(fileName);
		if (!lpath)
			return FALSE;
		header.typeflag = SYMTYPE;
		strncpy(header.linkname, lpath, sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
		/* Write out long linkname if needed */
		if (header.linkname[sizeof(header.linkname)-1])
			writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
#else
		/* If it is larger than 100 bytes, bail out */
		if (header.linkname[sizeof(header.linkname)-1]) {
			free(lpath);
			bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
			return FALSE;
		}
#endif
		free(lpath);
	} else if (S_ISDIR(statbuf->st_mode)) {
		header.typeflag = DIRTYPE;
		/* Append '/' only if there is a space for it */
		if (!header.name[sizeof(header.name)-1])
			header.name[strlen(header.name)] = '/';
	} else if (S_ISCHR(statbuf->st_mode)) {
		header.typeflag = CHRTYPE;
		PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
		PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
	} else if (S_ISBLK(statbuf->st_mode)) {
		header.typeflag = BLKTYPE;
		PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
		PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
	} else if (S_ISFIFO(statbuf->st_mode)) {
		header.typeflag = FIFOTYPE;
	} else if (S_ISREG(statbuf->st_mode)) {
		if (sizeof(statbuf->st_size) > 4
		 && statbuf->st_size > (off_t)0777777777777LL
		) {
			bb_error_msg_and_die("cannot store file '%s' "
				"of size %"OFF_FMT"d, aborting",
				fileName, statbuf->st_size);
		}
		header.typeflag = REGTYPE;
		PUT_OCTAL(header.size, statbuf->st_size);
	} else {
		bb_error_msg("%s: unknown file type", fileName);
		return FALSE;
	}

#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
	/* Write out long name if needed */
	/* (we, like GNU tar, output long linkname *before* long name) */
	if (header.name[sizeof(header.name)-1])
		writeLongname(tbInfo->tarFd, GNULONGNAME,
				header_name, S_ISDIR(statbuf->st_mode));
#endif

	/* Now write the header out to disk */
	chksum_and_xwrite(tbInfo->tarFd, &header);

	/* Now do the verbose thing (or not) */
	if (tbInfo->verboseFlag) {
		FILE *vbFd = stdout;

		if (tbInfo->tarFd == STDOUT_FILENO)	/* If the archive goes to stdout, verbose to stderr */
			vbFd = stderr;
		/* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
		/* We don't have such excesses here: for us "v" == "vv" */
		/* '/' is probably a GNUism */
		fprintf(vbFd, "%s%s\n", header_name,
				S_ISDIR(statbuf->st_mode) ? "/" : "");
	}

	return TRUE;
}
Beispiel #19
0
int i2cset_main(int argc, char **argv)
{
	const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
			      opt_m = (1 << 2), opt_r = (1 << 3);
	const char *const optstr = "fym:r";

	int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
	int val, blen = 0, mask = 0, fd, status;
	unsigned char block[I2C_SMBUS_BLOCK_MAX];
	char *opt_m_arg = NULL;
	unsigned opts;

        opt_complementary = "-3"; /* from 3 to ? args */
	opts = getopt32(argv, optstr, &opt_m_arg);
	argv += optind;
	argc -= optind;

	bus_num = i2c_bus_lookup(argv[0]);
	bus_addr = i2c_parse_bus_addr(argv[1]);
	data_addr = i2c_parse_data_addr(argv[2]);

	if (argv[3]) {
		if (!argv[4] && argv[3][0] != 'c') {
			mode = I2C_SMBUS_BYTE_DATA; /* Implicit b */
		} else {
			switch (argv[argc-1][0]) {
			case 'c': /* Already set */			break;
			case 'b': mode = I2C_SMBUS_BYTE_DATA;		break;
			case 'w': mode = I2C_SMBUS_WORD_DATA;		break;
			case 's': mode = I2C_SMBUS_BLOCK_DATA;		break;
			case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA;	break;
			default:
				bb_error_msg("invalid mode");
				bb_show_usage();
			}

			pec = argv[argc-1][1] == 'p';
			if (mode == I2C_SMBUS_BLOCK_DATA ||
					mode == I2C_SMBUS_I2C_BLOCK_DATA) {
				if (pec && mode == I2C_SMBUS_I2C_BLOCK_DATA)
					bb_error_msg_and_die(
						"PEC not supported for I2C "
						"block writes");
				if (opts & opt_m)
					bb_error_msg_and_die(
						"mask not supported for block "
						"writes");
			}
		}
	}

	/* Prepare the value(s) to be written according to current mode. */
	switch (mode) {
	case I2C_SMBUS_BYTE_DATA:
		val = xstrtou_range(argv[3], 0, 0, 0xff);
		break;
	case I2C_SMBUS_WORD_DATA:
		val = xstrtou_range(argv[3], 0, 0, 0xffff);
		break;
	case I2C_SMBUS_BLOCK_DATA:
	case I2C_SMBUS_I2C_BLOCK_DATA:
		for (blen = 3; blen < (argc - 1); blen++)
			block[blen] = xstrtou_range(argv[blen], 0, 0, 0xff);
		val = -1;
		break;
	default:
		val = -1;
		break;
	}

	if (opts & opt_m) {
		mask = xstrtou_range(opt_m_arg, 0, 0,
				(mode == I2C_SMBUS_BYTE ||
				 mode == I2C_SMBUS_BYTE_DATA) ? 0xff : 0xffff);
	}

	fd = i2c_dev_open(bus_num);
	check_write_funcs(fd, mode, pec);
	i2c_set_slave_addr(fd, bus_addr, opts & opt_f);

	if (!(opts & opt_y))
		confirm_action(bus_addr, mode, data_addr, pec);

	/*
	 * If we're using mask - read the current value here and adjust the
	 * value to be written.
	 */
	if (opts & opt_m) {
		int tmpval;

		switch (mode) {
		case I2C_SMBUS_BYTE:
			tmpval = i2c_smbus_read_byte(fd);
			break;
		case I2C_SMBUS_WORD_DATA:
			tmpval = i2c_smbus_read_word_data(fd, data_addr);
			break;
		default:
			tmpval = i2c_smbus_read_byte_data(fd, data_addr);
		}

		if (tmpval < 0)
			bb_perror_msg_and_die("can't read old value");

		val = (val & mask) | (tmpval & ~mask);

		if (!(opts & opt_y)) {
			bb_error_msg("old value 0x%0*x, write mask "
				"0x%0*x, will write 0x%0*x to register "
				"0x%02x",
				mode == I2C_SMBUS_WORD_DATA ? 4 : 2, tmpval,
				mode == I2C_SMBUS_WORD_DATA ? 4 : 2, mask,
				mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
				data_addr);
			confirm_or_abort();
		}
	}

	if (pec)
		i2c_set_pec(fd, 1);

	switch (mode) {
	case I2C_SMBUS_BYTE:
		status = i2c_smbus_write_byte(fd, data_addr);
		break;
	case I2C_SMBUS_WORD_DATA:
		status = i2c_smbus_write_word_data(fd, data_addr, val);
		break;
	case I2C_SMBUS_BLOCK_DATA:
		status = i2c_smbus_write_block_data(fd, data_addr,
						    blen, block);
		break;
	case I2C_SMBUS_I2C_BLOCK_DATA:
		status = i2c_smbus_write_i2c_block_data(fd, data_addr,
							blen, block);
		break;
	default: /* I2C_SMBUS_BYTE_DATA */
		status = i2c_smbus_write_byte_data(fd, data_addr, val);
		break;
	}
	if (status < 0)
		bb_perror_msg_and_die("write failed");

	if (pec)
		i2c_set_pec(fd, 0); /* Clear PEC. */

	/* No readback required - we're done. */
	if (!(opts & opt_r))
		return 0;

	switch (mode) {
	case I2C_SMBUS_BYTE:
		status = i2c_smbus_read_byte(fd);
		val = data_addr;
		break;
	case I2C_SMBUS_WORD_DATA:
		status = i2c_smbus_read_word_data(fd, data_addr);
		break;
	default: /* I2C_SMBUS_BYTE_DATA */
		status = i2c_smbus_read_byte_data(fd, data_addr);
	}

	if (status < 0) {
		puts("Warning - readback failed");
	} else
	if (status != val) {
		printf("Warning - data mismatch - wrote "
		       "0x%0*x, read back 0x%0*x\n",
		       mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
		       mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
	} else {
		printf("Value 0x%0*x written, readback matched\n",
		       mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val);
	}

	return 0;
}
Beispiel #20
0
static int writeFileToTarball(const char *fileName, struct stat *statbuf,
			void *userData, int depth ATTRIBUTE_UNUSED)
{
	struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
	const char *header_name;
	int inputFileFd = -1;

	/*
	 * Check to see if we are dealing with a hard link.
	 * If so -
	 * Treat the first occurance of a given dev/inode as a file while
	 * treating any additional occurances as hard links.  This is done
	 * by adding the file information to the HardLinkInfo linked list.
	 */
	tbInfo->hlInfo = NULL;
	if (statbuf->st_nlink > 1) {
		tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
		if (tbInfo->hlInfo == NULL)
			addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
	}

	/* It is against the rules to archive a socket */
	if (S_ISSOCK(statbuf->st_mode)) {
		bb_error_msg("%s: socket ignored", fileName);
		return TRUE;
	}

	/* It is a bad idea to store the archive we are in the process of creating,
	 * so check the device and inode to be sure that this particular file isn't
	 * the new tarball */
	if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
		tbInfo->statBuf.st_ino == statbuf->st_ino) {
		bb_error_msg("%s: file is the archive; skipping", fileName);
		return TRUE;
	}

	header_name = fileName;
	while (header_name[0] == '/') {
		static int alreadyWarned = FALSE;

		if (alreadyWarned == FALSE) {
			bb_error_msg("removing leading '/' from member names");
			alreadyWarned = TRUE;
		}
		header_name++;
	}

#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
	if (strlen(fileName) >= NAME_SIZE) {
		bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
		return TRUE;
	}
#endif

	if (header_name[0] == '\0')
		return TRUE;

	if (exclude_file(tbInfo->excludeList, header_name))
		return SKIP;

	/* Is this a regular file? */
	if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
		/* open the file we want to archive, and make sure all is well */
		inputFileFd = open(fileName, O_RDONLY);
		if (inputFileFd < 0) {
			bb_perror_msg("%s: cannot open", fileName);
			return FALSE;
		}
	}

	/* Add an entry to the tarball */
	if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
		return FALSE;
	}

	/* If it was a regular file, write out the body */
	if (inputFileFd >= 0) {
		size_t readSize;
		/* Wwrite the file to the archive. */
		/* We record size into header first, */
		/* and then write out file. If file shrinks in between, */
		/* tar will be corrupted. So we don't allow for that. */
		/* NB: GNU tar 1.16 warns and pads with zeroes */
		/* or even seeks back and updates header */
		bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
		////off_t readSize;
		////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
		////if (readSize != statbuf->st_size && readSize >= 0) {
		////	bb_error_msg_and_die("short read from %s, aborting", fileName);
		////}

		/* Check that file did not grow in between? */
		/* if (safe_read(inputFileFd, 1) == 1) warn but continue? */

		close(inputFileFd);

		/* Pad the file up to the tar block size */
		/* (a few tricks here in the name of code size) */
		readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
		memset(bb_common_bufsiz1, 0, readSize);
		xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
	}

	return TRUE;
}
/*
 * Add the symbol defined in the kernel, simulating a pseudo module.
 * Create a dummy module entry in the list of modules.
 * This module is used to handle the exported kernel symbols.
 *
 * Load from the currently running kernel
 * OR
 * from a file containing kernel symbols from a different kernel.
 * The file can be either a "System.map" file or a copy of "/proc/ksyms".
 *
 * Return -1 if any error.
 */
static int addksyms(const char *file_syms)
{
	FILE *fp;
	MODULE *mod = modules + n_modules++;
	SYMBOL *symtab[MAX_MAP_SYM];
	struct module_symbol *ksym;
	unsigned int so_far = 0;
	int is_mapfile = 0;
	int n_syms = 0;
	int size;
	char line[PATH_MAX];
	char *p;

	mod->name = "-";
	mod->defsym.n_syms = 0;
	mod->defsym.symtab = NULL;
	mod->undefs.n_syms = 0;
	mod->undefs.symtab = NULL;

	/* Fake the _mod_use_count_ symbol provided by insmod */
	/* Dummy */
	symtab[n_syms++] = addsym("mod_use_count_", mod, SYM_DEFINED, 0);
	symtab[n_syms++] = addsym("__this_module", mod, SYM_DEFINED, 0);

	/*
	 * Specification: depmod / kernel syms only
	 * When initialising its symbol table from the kernel
	 * depmod silently discards all symbol from loaded modules.
	 *
	 * This means that depmod may be used at any time to compute
	 * the dependancy table, even if there are modules already
	 * loaded.
	 *
	 * depmod use the kernel system call to obtain the
	 * symbol table, not /proc/ksyms.
	 */

	if (file_syms) {
		if ((fp = fopen(file_syms, "r")) == NULL) {
			bb_error_msg("Can't read %s", file_syms);
			return -1;
		}
		if (!fgets(line, sizeof(line), fp)) {
			fclose(fp);
			bb_error_msg("premature EOF on %s", file_syms);
			return -1;
		}

		p = strtok(line, " \t\n");
		if (!p) {
			fclose(fp);
			bb_error_msg("Illegal format of %s", file_syms);
			return -1;
		}
		if (!isspace(*line))	/* Adressless symbol? */
			p = strtok(NULL, " \t\n");
		/* The second word is either the symbol name or a type */
		if (p && p[0] && !p[1]) { /* System.map */
			is_mapfile = 1;
			p = strtok(NULL, " \t\n");
		} else { /* /proc/ksyms copy */
			if (p && strtok(NULL, " \t\n"))
				p = NULL;
		}

		if (p && strncmp(p, "GPLONLY_", 8) == 0)
			p += 8;
		if (p)
			symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0);

		while (fgets(line, sizeof(line), fp)) {
			if (!is_mapfile && strchr(line, '['))
				continue;
			p = strtok(line, " \t\n"); /* Skip first word */
			if (!isspace(*line))	/* Adressless symbol? */
				p = strtok(NULL, " \t\n");
			if (is_mapfile) {
				if (!p || !p[0] || p[1])
					continue;
				p = strtok(NULL, " \t\n");
				/* Sparc has symbols like '.div' that need to be
				 * exported.  They strip the '.' and prefix the
				 * symbol with '__sparc_dot_'.  There is no need
				 * for 'sparc' in the name, other systems with
				 * the same problem should use just '__dot_'.
				 * Support both prefixes, ready for other
				 * systems.
				 * */
				if (!strncmp(p, "__kstrtab___dot_", 15)) {
					p += 15;
					*p = '.';
				}
				else if (!strncmp(p, "__kstrtab___sparc_dot_", 21)) {
					p += 21;
					*p = '.';
				}
				else if (!strncmp(p, "__kstrtab_", 10))
					p += 10;
				else if (!strncmp(p, "__export_priv_", 14)) {
					/* Sparc has some weird exported symbols marked
					 * __export_priv_ instead of the normal __kstrtab_.
					 * Replace the 'v' with '_' and point at the start of
					 * the '__' before the name.  I see no good reason
					 * to use __export_priv_, but for compatibility with
					 * old sparc kernels, it is supported.  Try to remove
					 * __export_priv_ from sparc and remove this code
					 * four releases after the last kernel that uses
					 * __export_priv_.
					 */
					p += 12;
					*p = '_';
				} else
					continue;
			}
			if (strncmp(p, "GPLONLY_", 8) == 0)
				p += 8;
			assert(n_syms < MAX_MAP_SYM);
			symtab[n_syms++] = addsym(p, mod, SYM_DEFINED, 0);
		}
		fclose(fp);
	} else { /* Use the exported symbols from currently running kernel */
		if (!new_get_kernel_info(K_SYMBOLS))
			return -1;

		for (ksym = ksyms; so_far < nksyms; ++so_far, ksym++) {
			if (strncmp((char *)ksym->name, "GPLONLY_", 8) == 0)
				ksym->name = ((char *)ksym->name) + 8;
			assert(n_syms < MAX_MAP_SYM);
			symtab[n_syms++] = addsym((char *)ksym->name, mod, SYM_DEFINED, 0);
		}
	}

	size = n_syms * sizeof(SYMBOL *);
	mod->defsym.symtab = (SYMBOL **)xmalloc(size);
	mod->defsym.n_syms = n_syms;
	memcpy(mod->defsym.symtab, symtab, size);

	return 0;
}
Beispiel #22
0
static int writeTarFile(const int tar_fd, const int verboseFlag,
	const unsigned long dereferenceFlag, const llist_t *include,
	const llist_t *exclude, const int gzip)
{
	pid_t gzipPid = 0;
	int errorFlag = FALSE;
	struct TarBallInfo tbInfo;

	tbInfo.hlInfoHead = NULL;

	fchmod(tar_fd, 0644);
	tbInfo.tarFd = tar_fd;
	tbInfo.verboseFlag = verboseFlag;

	/* Store the stat info for the tarball's file, so
	 * can avoid including the tarball into itself....  */
	if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
		bb_perror_msg_and_die("cannot stat tar file");

	if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
		int gzipDataPipe[2] = { -1, -1 };
		int gzipStatusPipe[2] = { -1, -1 };
		volatile int vfork_exec_errno = 0;
		const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";

		if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
			bb_perror_msg_and_die("pipe");

		signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */

#if defined(__GNUC__) && __GNUC__
		/* Avoid vfork clobbering */
		(void) &include;
		(void) &errorFlag;
		(void) &zip_exec;
#endif

		gzipPid = vfork();

		if (gzipPid == 0) {
			dup2(gzipDataPipe[0], 0);
			close(gzipDataPipe[1]);

			dup2(tbInfo.tarFd, 1);

			close(gzipStatusPipe[0]);
			fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC);	/* close on exec shows success */

			BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
			vfork_exec_errno = errno;

			close(gzipStatusPipe[1]);
			exit(-1);
		} else if (gzipPid > 0) {
			close(gzipDataPipe[0]);
			close(gzipStatusPipe[1]);

			while (1) {
				char buf;

				int n = full_read(gzipStatusPipe[0], &buf, 1);

				if (n == 0 && vfork_exec_errno != 0) {
					errno = vfork_exec_errno;
					bb_perror_msg_and_die("cannot exec %s", zip_exec);
				} else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
					continue;	/* try it again */
				break;
			}
			close(gzipStatusPipe[0]);

			tbInfo.tarFd = gzipDataPipe[1];
		} else bb_perror_msg_and_die("vfork gzip");
	}

	tbInfo.excludeList = exclude;

	/* Read the directory/files and iterate over them one at a time */
	while (include) {
		if (!recursive_action(include->data, TRUE, dereferenceFlag,
				FALSE, writeFileToTarball, writeFileToTarball, &tbInfo, 0))
		{
			errorFlag = TRUE;
		}
		include = include->link;
	}
	/* Write two empty blocks to the end of the archive */
	memset(bb_common_bufsiz1, 0, 2*TAR_BLOCK_SIZE);
	xwrite(tbInfo.tarFd, bb_common_bufsiz1, 2*TAR_BLOCK_SIZE);

	/* To be pedantically correct, we would check if the tarball
	 * is smaller than 20 tar blocks, and pad it if it was smaller,
	 * but that isn't necessary for GNU tar interoperability, and
	 * so is considered a waste of space */

	/* Close so the child process (if any) will exit */
	close(tbInfo.tarFd);

	/* Hang up the tools, close up shop, head home */
	if (ENABLE_FEATURE_CLEAN_UP)
		freeHardLinkInfo(&tbInfo.hlInfoHead);

	if (errorFlag)
		bb_error_msg("error exit delayed from previous errors");

	if (gzipPid) {
		int status;
		if (waitpid(gzipPid, &status, 0) == -1)
			bb_perror_msg("waitpid");
		else if (!WIFEXITED(status) || WEXITSTATUS(status))
			/* gzip was killed or has exited with nonzero! */
			errorFlag = TRUE;
	}
	return errorFlag;
}
Beispiel #23
0
/*
 * Do the substitute command.
 * The current line is set to the last substitution done.
 */
static void subCommand(const char * cmd, int num1, int num2)
{
	char *cp, *oldStr, *newStr, buf[USERSIZE];
	int delim, oldLen, newLen, deltaLen, offset;
	LINE *lp, *nlp;
	int globalFlag, printFlag, didSub, needPrint;

	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
		bb_error_msg("bad line range for substitute");
		return;
	}

	globalFlag = FALSE;
	printFlag = FALSE;
	didSub = FALSE;
	needPrint = FALSE;

	/*
	 * Copy the command so we can modify it.
	 */
	strcpy(buf, cmd);
	cp = buf;

	if (isblank(*cp) || (*cp == '\0')) {
		bb_error_msg("bad delimiter for substitute");
		return;
	}

	delim = *cp++;
	oldStr = cp;

	cp = strchr(cp, delim);

	if (cp == NULL) {
		bb_error_msg("missing 2nd delimiter for substitute");
		return;
	}

	*cp++ = '\0';

	newStr = cp;
	cp = strchr(cp, delim);

	if (cp)
		*cp++ = '\0';
	else
		cp = (char*)"";

	while (*cp) switch (*cp++) {
		case 'g':
			globalFlag = TRUE;
			break;

		case 'p':
			printFlag = TRUE;
			break;

		default:
			bb_error_msg("unknown option for substitute");
			return;
	}

	if (*oldStr == '\0') {
		if (searchString[0] == '\0') {
			bb_error_msg("no previous search string");
			return;
		}

		oldStr = searchString;
	}

	if (oldStr != searchString)
		strcpy(searchString, oldStr);

	lp = findLine(num1);

	if (lp == NULL)
		return;

	oldLen = strlen(oldStr);
	newLen = strlen(newStr);
	deltaLen = newLen - oldLen;
	offset = 0;
	nlp = NULL;

	while (num1 <= num2) {
		offset = findString(lp, oldStr, oldLen, offset);

		if (offset < 0) {
			if (needPrint) {
				printLines(num1, num1, FALSE);
				needPrint = FALSE;
			}

			offset = 0;
			lp = lp->next;
			num1++;

			continue;
		}

		needPrint = printFlag;
		didSub = TRUE;
		dirty = TRUE;

		/*
		 * If the replacement string is the same size or shorter
		 * than the old string, then the substitution is easy.
		 */
		if (deltaLen <= 0) {
			memcpy(&lp->data[offset], newStr, newLen);

			if (deltaLen) {
				memcpy(&lp->data[offset + newLen],
					&lp->data[offset + oldLen],
					lp->len - offset - oldLen);

				lp->len += deltaLen;
			}

			offset += newLen;

			if (globalFlag)
				continue;

			if (needPrint) {
				printLines(num1, num1, FALSE);
				needPrint = FALSE;
			}

			lp = lp->next;
			num1++;

			continue;
		}

		/*
		 * The new string is larger, so allocate a new line
		 * structure and use that.  Link it in in place of
		 * the old line structure.
		 */
		nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);

		if (nlp == NULL) {
			bb_error_msg("cannot get memory for line");
			return;
		}

		nlp->len = lp->len + deltaLen;

		memcpy(nlp->data, lp->data, offset);

		memcpy(&nlp->data[offset], newStr, newLen);

		memcpy(&nlp->data[offset + newLen],
			&lp->data[offset + oldLen],
			lp->len - offset - oldLen);

		nlp->next = lp->next;
		nlp->prev = lp->prev;
		nlp->prev->next = nlp;
		nlp->next->prev = nlp;

		if (curLine == lp)
			curLine = nlp;

		free(lp);
		lp = nlp;

		offset += newLen;

		if (globalFlag)
			continue;

		if (needPrint) {
			printLines(num1, num1, FALSE);
			needPrint = FALSE;
		}

		lp = lp->next;
		num1++;
	}

	if (!didSub)
		bb_error_msg("no substitutions found for \"%s\"", oldStr);
}
Beispiel #24
0
// file libbb/warn_ignoring_args.c line 12
static void bb_warn_ignoring_args(char *arg)
{
  if(!(arg == ((char *)NULL)))
    bb_error_msg("ignoring all arguments");

}
Beispiel #25
0
/*
 * Read lines from a file at the specified line number.
 * Returns TRUE if the file was successfully read.
 */
static int readLines(const char * file, int num)
{
	int fd, cc;
	int len, lineCount, charCount;
	char *cp;

	if ((num < 1) || (num > lastNum + 1)) {
		bb_error_msg("bad line for read");
		return FALSE;
	}

	fd = open(file, 0);

	if (fd < 0) {
		perror(file);
		return FALSE;
	}

	bufPtr = bufBase;
	bufUsed = 0;
	lineCount = 0;
	charCount = 0;
	cc = 0;

	printf("\"%s\", ", file);
	fflush(stdout);

	do {
		cp = memchr(bufPtr, '\n', bufUsed);

		if (cp) {
			len = (cp - bufPtr) + 1;

			if (!insertLine(num, bufPtr, len)) {
				close(fd);
				return FALSE;
			}

			bufPtr += len;
			bufUsed -= len;
			charCount += len;
			lineCount++;
			num++;

			continue;
		}

		if (bufPtr != bufBase) {
			memcpy(bufBase, bufPtr, bufUsed);
			bufPtr = bufBase + bufUsed;
		}

		if (bufUsed >= bufSize) {
			len = (bufSize * 3) / 2;
			cp = realloc(bufBase, len);

			if (cp == NULL) {
				bb_error_msg("no memory for buffer");
				close(fd);
				return FALSE;
			}

			bufBase = cp;
			bufPtr = bufBase + bufUsed;
			bufSize = len;
		}

		cc = read(fd, bufPtr, bufSize - bufUsed);
		bufUsed += cc;
		bufPtr = bufBase;

	} while (cc > 0);

	if (cc < 0) {
		perror(file);
		close(fd);
		return FALSE;
	}

	if (bufUsed) {
		if (!insertLine(num, bufPtr, bufUsed)) {
			close(fd);
			return -1;
		}

		lineCount++;
		charCount += bufUsed;
	}

	close(fd);

	printf("%d lines%s, %d chars\n", lineCount,
		(bufUsed ? " (incomplete)" : ""), charCount);

	return TRUE;
}
Beispiel #26
0
extern int run_parts(char **args, const unsigned char test_mode)
{
	struct dirent **namelist = 0;
	struct stat st;
	char *filename;
	char *arg0 = args[0];
	int entries;
	int i;
	int exitstatus = 0;

#if __GNUC__
	/* Avoid longjmp clobbering */
	(void) &i;
	(void) &exitstatus;
#endif
	/* scandir() isn't POSIX, but it makes things easy. */
	entries = scandir(arg0, &namelist, valid_name, alphasort);

	if (entries == -1) {
		if (test_mode & 2) {
			return(2);
		}
		bb_perror_msg_and_die("failed to open directory %s", arg0);
	}

	for (i = 0; i < entries; i++) {

		filename = concat_path_file(arg0, namelist[i]->d_name);

		if (stat(filename, &st) < 0) {
			bb_perror_msg_and_die("failed to stat component %s", filename);
		}
		if (S_ISREG(st.st_mode) && !access(filename, X_OK)) {
			if (test_mode & 1) {
				puts(filename);
			} else {
				/* exec_errno is common vfork variable */
				volatile int exec_errno = 0;
				int result;
				int pid;

				if ((pid = vfork()) < 0) {
					bb_perror_msg_and_die("failed to fork");
				} else if (!pid) {
					args[0] = filename;
					execv(filename, args);
					exec_errno = errno;
					_exit(1);
				}

				waitpid(pid, &result, 0);
				if(exec_errno) {
					errno = exec_errno;
					bb_perror_msg_and_die("failed to exec %s", filename);
				}
				if (WIFEXITED(result) && WEXITSTATUS(result)) {
					bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result));
					exitstatus = 1;
				} else if (WIFSIGNALED(result)) {
					bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result));
					exitstatus = 1;
				}
			}
		} 
		else if (!S_ISDIR(st.st_mode)) {
			bb_error_msg("component %s is not an executable plain file", filename);
			exitstatus = 1;
		}

		free(namelist[i]);
		free(filename);
	}
	free(namelist);
	
	return(exitstatus);
}
Beispiel #27
0
/*
 * Print lines in a specified range.
 * The last line printed becomes the current line.
 * If expandFlag is TRUE, then the line is printed specially to
 * show magic characters.
 */
static int printLines(int num1, int num2, int expandFlag)
{
	const LINE *lp;
	const char *cp;
	int ch, count;

	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
		bb_error_msg("bad line range for print");
		return FALSE;
	}

	lp = findLine(num1);

	if (lp == NULL)
		return FALSE;

	while (num1 <= num2) {
		if (!expandFlag) {
			write(1, lp->data, lp->len);
			setCurNum(num1++);
			lp = lp->next;

			continue;
		}

		/*
		 * Show control characters and characters with the
		 * high bit set specially.
		 */
		cp = lp->data;
		count = lp->len;

		if ((count > 0) && (cp[count - 1] == '\n'))
			count--;

		while (count-- > 0) {
			ch = *cp++;

			if (ch & 0x80) {
				fputs("M-", stdout);
				ch &= 0x7f;
			}

			if (ch < ' ') {
				fputc('^', stdout);
				ch += '@';
			}

			if (ch == 0x7f) {
				fputc('^', stdout);
				ch = '?';
			}

			fputc(ch, stdout);
		}

		fputs("$\n", stdout);

		setCurNum(num1++);
		lp = lp->next;
	}

	return TRUE;
}
Beispiel #28
0
static void rcptto(const char *s)
{
	// N.B. we don't die if recipient is rejected, for the other recipients may be accepted
	if (250 != smtp_checkp("RCPT TO:<%s>", s, -1))
		bb_error_msg("Bad recipient: <%s>", s);
}
Beispiel #29
0
void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
{
	file_header_t *file_header = archive_handle->file_header;
	int dst_fd;
	int res;

#if ENABLE_FEATURE_TAR_SELINUX
	char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
	if (!sctx)
		sctx = archive_handle->tar__sctx[PAX_GLOBAL];
	if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
		setfscreatecon(sctx);
		free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
		archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
	}
#endif

	if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
		char *slash = strrchr(file_header->name, '/');
		if (slash) {
			*slash = '\0';
			bb_make_directory(file_header->name, -1, FILEUTILS_RECUR);
			*slash = '/';
		}
	}

	if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
		/* Remove the entry if it exists */
		if (!S_ISDIR(file_header->mode)) {
			/* Is it hardlink?
			 * We encode hard links as regular files of size 0 with a symlink */
			if (S_ISREG(file_header->mode)
			 && file_header->link_target
			 && file_header->size == 0
			) {
				/* Ugly special case:
				 * tar cf t.tar hardlink1 hardlink2 hardlink1
				 * results in this tarball structure:
				 * hardlink1
				 * hardlink2 -> hardlink1
				 * hardlink1 -> hardlink1 <== !!!
				 */
				if (strcmp(file_header->link_target, file_header->name) == 0)
					goto ret;
			}
			/* Proceed with deleting */
			if (_unlink(file_header->name) == -1
			 && errno != ENOENT
			) {
				bb_perror_msg_and_die("can't remove old file %s",
						file_header->name);
			}
		}
	}
	else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
		/* Remove the existing entry if its older than the extracted entry */
		struct stat existing_sb;
		if (lstat(file_header->name, &existing_sb) == -1) {
			if (errno != ENOENT) {
				bb_perror_msg_and_die("can't stat old file");
			}
		}
		else if (existing_sb.st_mtime >= file_header->mtime) {
			if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
			 && !S_ISDIR(file_header->mode)
			) {
				bb_error_msg("%s not created: newer or "
					"same age file exists", file_header->name);
			}
			data_skip(archive_handle);
			goto ret;
		}
		else if ((_unlink(file_header->name) == -1) && (errno != EISDIR)) {
			bb_perror_msg_and_die("can't remove old file %s",
					file_header->name);
		}
	}

	/* Handle hard links separately
	 * We encode hard links as regular files of size 0 with a symlink */
	if (S_ISREG(file_header->mode)
	 && file_header->link_target
	 && file_header->size == 0
	) {
		/* hard link */
		res = link(file_header->link_target, file_header->name);
		if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
			bb_perror_msg("can't create %slink "
					"from %s to %s", "hard",
					file_header->name,
					file_header->link_target);
		}
		/* Hardlinks have no separate mode/ownership, skip chown/chmod */
		goto ret;
	}

	/* Create the filesystem entry */
	switch (file_header->mode & S_IFMT) {
	case S_IFREG: {
		/* Regular file */
		char *dst_name;
		int flags = O_WRONLY | O_CREAT | O_EXCL;
		if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
			flags = O_WRONLY | O_CREAT | O_TRUNC;
		dst_name = file_header->name;
#ifdef ARCHIVE_REPLACE_VIA_RENAME
		if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
			/* rpm-style temp file name */
			dst_name = xasprintf("%s;%x", dst_name, (int)getpid());
#endif
		if (_sopen_s(&dst_fd, dst_name, flags, _SH_DENYNO, file_header->mode) != 0) {
			bb_perror_msg_and_die("can't open file %s", dst_name);
		}
		bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
		_close(dst_fd);
#ifdef ARCHIVE_REPLACE_VIA_RENAME
		if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
			xrename(dst_name, file_header->name);
			free(dst_name);
		}
#endif
		break;
	}
	case S_IFDIR:
		res = mkdir(file_header->name, file_header->mode);
		if ((res == -1)
		 && (errno != EISDIR) /* btw, Linux doesn't return this */
		 && (errno != EEXIST)
		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
		) {
			bb_perror_msg("can't make dir %s", file_header->name);
		}
		break;
	case S_IFLNK:
		/* Symlink */
//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
		res = symlink(file_header->link_target, file_header->name);
		if ((res == -1)
		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
		) {
			bb_perror_msg("can't create %slink "
				"from %s to %s", "sym",
				file_header->name,
				file_header->link_target);
		}
		break;
	case S_IFSOCK:
	case S_IFBLK:
	case S_IFCHR:
	case S_IFIFO:
		res = mknod(file_header->name, file_header->mode, file_header->device);
		if ((res == -1)
		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
		) {
			bb_perror_msg("can't create node %s", file_header->name);
		}
		break;
	default:
		bb_error_msg_and_die("unrecognized file type");
	}

	if (!S_ISLNK(file_header->mode)) {
		if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
			uid_t uid = file_header->uid;
			gid_t gid = file_header->gid;
#if ENABLE_FEATURE_TAR_UNAME_GNAME
			if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
				if (file_header->tar__uname) {
//TODO: cache last name/id pair?
					struct passwd *pwd = getpwnam(file_header->tar__uname);
					if (pwd) uid = pwd->pw_uid;
				}
				if (file_header->tar__gname) {
					struct group *grp = getgrnam(file_header->tar__gname);
					if (grp) gid = grp->gr_gid;
				}
			}
#endif
			/* GNU tar 1.15.1 uses chown, not lchown */
			chown(file_header->name, uid, gid);
		}
		/* uclibc has no lchmod, glibc is even stranger -
		 * it has lchmod which seems to do nothing!
		 * so we use chmod... */
		if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
			(void)_chmod(file_header->name, file_header->mode);
		}
		if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
			struct timeval t[2];

			t[1].tv_sec = t[0].tv_sec = (long)file_header->mtime;
			t[1].tv_usec = t[0].tv_usec = 0;
			utimes(file_header->name, t);
		}
	}

 ret: ;
#if ENABLE_FEATURE_TAR_SELINUX
	if (sctx) {
		/* reset the context after creating an entry */
		setfscreatecon(NULL);
	}
#endif
}
Beispiel #30
0
int man_main(int argc UNUSED_PARAM, char **argv)
{
	parser_t *parser;
	const char *pager;
	char **man_path_list;
	char *sec_list;
	char *cur_path, *cur_sect;
	int count_mp, cur_mp;
	int opt, not_found;
	char *token[2];

	opt_complementary = "-1"; /* at least one argument */
	opt = getopt32(argv, "+aw");
	argv += optind;

	sec_list = xstrdup("1:2:3:4:5:6:7:8:9");
	/* Last valid man_path_list[] is [0x10] */
	man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
	count_mp = 0;
	man_path_list[0] = xstrdup(getenv("MANPATH"));
	if (man_path_list[0])
		count_mp++;
	pager = getenv("MANPAGER");
	if (!pager) {
		pager = getenv("PAGER");
		if (!pager)
			pager = "more";
	}

	/* Parse man.conf */
	parser = config_open("/etc/man.conf");
	while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
		if (!token[1])
			continue;
		if (strcmp("MANPATH", token[0]) == 0) {
			man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
			man_path_list[count_mp] = xstrdup(token[1]);
			count_mp++;
			/* man_path_list is NULL terminated */
			/*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
		}
		if (strcmp("MANSECT", token[0]) == 0) {
			free(sec_list);
			sec_list = xstrdup(token[1]);
		}
	}
	config_close(parser);

	not_found = 0;
	do { /* for each argv[] */
		int found = 0;
		cur_mp = 0;

		if (strchr(*argv, '/')) {
			found = show_manpage(pager, *argv, /*man:*/ 1, 0);
			goto check_found;
		}
		while ((cur_path = man_path_list[cur_mp++]) != NULL) {
			/* for each MANPATH */
			do { /* for each MANPATH item */
				char *next_path = strchrnul(cur_path, ':');
				int path_len = next_path - cur_path;
				cur_sect = sec_list;
				do { /* for each section */
					char *next_sect = strchrnul(cur_sect, ':');
					int sect_len = next_sect - cur_sect;
					char *man_filename;
					int cat0man1 = 0;

					/* Search for cat, then man page */
					while (cat0man1 < 2) {
						int found_here;
						man_filename = xasprintf("%.*s/%s%.*s/%s.%.*s" Z_SUFFIX,
								path_len, cur_path,
								"cat\0man" + (cat0man1 * 4),
								sect_len, cur_sect,
								*argv,
								sect_len, cur_sect);
						found_here = show_manpage(pager, man_filename, cat0man1, 0);
						found |= found_here;
						cat0man1 += found_here + 1;
						free(man_filename);
					}

					if (found && !(opt & OPT_a))
						goto next_arg;
					cur_sect = next_sect;
					while (*cur_sect == ':')
						cur_sect++;
				} while (*cur_sect);
				cur_path = next_path;
				while (*cur_path == ':')
					cur_path++;
			} while (*cur_path);
		}
 check_found:
		if (!found) {
			bb_error_msg("no manual entry for '%s'", *argv);
			not_found = 1;
		}
 next_arg:
		argv++;
	} while (*argv);

	return not_found;
}