Exemple #1
0
static int addgroup_wrapper(struct passwd *p, const char *group_name)
{
	char *argv[6];

	argv[0] = (char*)"addgroup";
	if (group_name) {
		/* Add user to existing group */
		argv[1] = (char*)"--";
		argv[2] = p->pw_name;
		argv[3] = (char*)group_name;
		argv[4] = NULL;
	} else {
		/* Add user to his own group with the first free gid
		 * found in passwd_study.
		 */
#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP
		/* We try to use --gid, not -g, because "standard" addgroup
		 * has no short option -g, it has only long --gid.
		 */
		argv[1] = (char*)"--gid";
#else
		/* Breaks if system in fact does NOT use busybox addgroup */
		argv[1] = (char*)"-g";
#endif
		argv[2] = utoa(p->pw_gid);
		argv[3] = (char*)"--";
		argv[4] = p->pw_name;
		argv[5] = NULL;
	}

	return spawn_and_wait(argv);
}
Exemple #2
0
/*
   This function has special algorithm.
   Don't use fork and include to main!
*/
static int xargs_exec(char **args)
{
	int status;

	status = spawn_and_wait(args);
	if (status < 0) {
		bb_perror_msg("%s", args[0]);
		return errno == ENOENT ? 127 : 126;
	}
	if (status == 255) {
		bb_error_msg("%s: exited with status 255; aborting", args[0]);
		return 124;
	}
/* Huh? I think we won't see this, ever. We don't wait with WUNTRACED!
	if (WIFSTOPPED(status)) {
		bb_error_msg("%s: stopped by signal %d",
			args[0], WSTOPSIG(status));
		return 125;
	}
*/
	if (status >= 1000) {
		bb_error_msg("%s: terminated by signal %d",
			args[0], status - 1000);
		return 125;
	}
	if (status)
		return 123;
	return 0;
}
Exemple #3
0
/**
 * Run a script.
 * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
 */
static int run(char *argv[3], const char *param, struct in_addr *ip)
{
	int status;
	char *addr = addr; /* for gcc */
	const char *fmt = "%s %s %s" + 3;

	argv[2] = (char*)param;

	VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);

	if (ip) {
		addr = inet_ntoa(*ip);
		xsetenv("ip", addr);
		fmt -= 3;
	}
	if (verbose)
		bb_info_msg(fmt, argv[2], argv[0], addr);

	status = spawn_and_wait(argv + 1);
	if (status < 0) {
		bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]);
		return -errno;
	}
	if (status != 0)
		bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff);
	return status;
}
Exemple #4
0
static int run_script(const char *action)
{
	char *env_PREVIOUS, *env_CURRENT;
	char *argv[5];
	int r;

	bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);

	argv[0] = (char*) G.script_name;
	argv[1] = (char*) G.iface;
	argv[2] = (char*) action;
	argv[3] = (char*) G.extra_arg;
	argv[4] = NULL;

	env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
	putenv(env_PREVIOUS);
	env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
	putenv(env_CURRENT);

	/* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
	r = spawn_and_wait(argv);

	unsetenv(IFPLUGD_ENV_PREVIOUS);
	unsetenv(IFPLUGD_ENV_CURRENT);
	free(env_PREVIOUS);
	free(env_CURRENT);

	bb_error_msg("exit code: %d", r & 0xff);
	return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
}
Exemple #5
0
int main(int argc, char **argv)
{
    int naptime = 0;
    assert(argc > 2);

    naptime = atoi(argv[1]);
/*     assert(naptime > 0 && naptime < 1800); */

    alarm(naptime);

    return spawn_and_wait(argc+2, argv+2);
}
void FAST_FUNC write_leases(void)
{
	int fd;
	unsigned i;
	leasetime_t curr;
	int64_t written_at;

	fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
	if (fd < 0)
		return;

	curr = written_at = time(NULL);

	written_at = SWAP_BE64(written_at);
	full_write(fd, &written_at, sizeof(written_at));

	for (i = 0; i < server_config.max_leases; i++) {
		leasetime_t tmp_time;

		if (g_leases[i].lease_nip == 0)
			continue;

		/* Screw with the time in the struct, for easier writing */
		tmp_time = g_leases[i].expires;

		g_leases[i].expires -= curr;
		if ((signed_leasetime_t) g_leases[i].expires < 0)
			g_leases[i].expires = 0;
		g_leases[i].expires = htonl(g_leases[i].expires);

		/* No error check. If the file gets truncated,
		 * we lose some leases on restart. Oh well. */
		full_write(fd, &g_leases[i], sizeof(g_leases[i]));

		/* Then restore it when done */
		g_leases[i].expires = tmp_time;
	}
	close(fd);

	if (server_config.notify_file) {
		char *argv[3];
		argv[0] = server_config.notify_file;
		argv[1] = server_config.lease_file;
		argv[2] = NULL;
		spawn_and_wait(argv);
	}
}
static void run_login_script(struct passwd *pw, char *full_tty)
{
    char *t_argv[2];

    t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
    if (t_argv[0]) {
        t_argv[1] = NULL;
        xsetenv("LOGIN_TTY", full_tty);
        xsetenv("LOGIN_USER", pw->pw_name);
        xsetenv("LOGIN_UID", utoa(pw->pw_uid));
        xsetenv("LOGIN_GID", utoa(pw->pw_gid));
        xsetenv("LOGIN_SHELL", pw->pw_shell);
        spawn_and_wait(t_argv);	/* NOMMU-friendly */
        unsetenv("LOGIN_TTY");
        unsetenv("LOGIN_USER");
        unsetenv("LOGIN_UID");
        unsetenv("LOGIN_GID");
        unsetenv("LOGIN_SHELL");
    }
}
Exemple #8
0
static int xargs_exec(void)
{
	int status;

	status = spawn_and_wait(G.args);
	if (status < 0) {
		bb_simple_perror_msg(G.args[0]);
		return errno == ENOENT ? 127 : 126;
	}
	if (status == 255) {
		bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
		return 124;
	}
	if (status >= 0x180) {
		bb_error_msg("'%s' terminated by signal %d",
			G.args[0], status - 0x180);
		return 125;
	}
	if (status)
		return 123;
	return 0;
}
Exemple #9
0
int ginit_main(int argc UNUSED_PARAM, char **argv)
{
	FILE *mntlist;
	bool ismnted_dev, ismnted_sys, ismnted_usr;
	struct mntent *mntent;

	/*
	int fd = open("/dev/console", O_RDWR);
	if (fd >= 0) {
		dup2(fd, 0);
		dup2(fd, 1);
		dup2(fd, 2);
	}
	*/

	/* If given an argv[] with an applet name, run it instead.
	 * Makes recovering simple by doing: init=/ginit bb
	 */
	if (argv[1] && argv[1][0] != '/') {
		eprintf("running user requested applet %s\n", argv[1]);
		return spawn_and_wait(argv+1);
	}

#define saw(argv...) \
	({ \
		static const char *args[] = { argv, NULL }; \
		/* These casts are fine -- see process_args for mem setup */ \
		process_args((void *)args); \
		spawn_and_wait((void *)args); \
	})

	/* First setup basic /dev */
	if (saw("mountpoint", "-q", "/dev") != 0) {
		/* Try /etc/fstab */
		if (saw("mount", "-n", "/dev"))
			/* Then devtmpfs */
			if (saw("mount", "-n", "-t", "devtmpfs", "devtmpfs", "/dev"))
				/* Finally normal tmpfs */
				saw("mount", "-n", "-t", "tmpfs", "dev", "/dev");
	} else {
		eprintf("%s appears to be mounted; skipping its setup\n", "/dev");
	}

	/* If /dev is empty (e.g. tmpfs), run mdev to seed things */
	if (access("/dev/console", F_OK) != 0) {
		if (saw("mountpoint", "-q", "/sys") != 0) {
			if (saw("mount", "-n", "/sys"))
				saw("mount", "-n", "-t", "sysfs", "sysfs", "/sys");
		} else {
			eprintf("%s appears to be mounted; skipping its setup\n", "/sys");
		}

		/* Mount /proc as mdev will fork+exec /proc/self/exe */
		if (saw("mountpoint", "-q", "/proc") != 0) {
			/* Try /etc/fstab */
			if (saw("mount", "-n", "/proc"))
				saw("mount", "-n", "-t", "proc", "proc", "/proc");
		}

		saw("mdev", "-s");
	}

	/* Then seed the stuff we care about */
	saw("mkdir", "-p", "/dev/pts", "/dev/shm");

	/* Then mount /usr */
	if (saw("mountpoint", "-q", "/usr") != 0) {
		saw("mount", "-n", "/usr", "-o", "ro");
	} else {
		eprintf("%s appears to be mounted; skipping its setup\n", "/usr");
	}

	/* Now that we're all done, exec the real init */
	if (!argv[1]) {
		argv[0] = (void *)"/sbin/init";
		argv[1] = NULL;
	} else
		++argv;
	process_args(argv);
	return execv(argv[0], argv);
}
Exemple #10
0
int inotifyd_main(int argc, char **argv)
{
	int n;
	unsigned mask;
	struct pollfd pfd;
	char **watches; // names of files being watched
	const char *args[5];

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

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

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

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

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

	// setup signals
	bb_signals(BB_FATAL_SIGS, record_signo);

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

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

	argv++;

	// Subscribe for UEVENT kernel messages
	sa.nl_family = AF_NETLINK;
	sa.nl_pad = 0;
	sa.nl_pid = getpid();
	sa.nl_groups = 1 << 0;
	fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
	xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
	close_on_exec_on(fd);

	// Without a sufficiently big RCVBUF, a ton of simultaneous events
	// can trigger ENOBUFS on read, which is unrecoverable.
	// Reproducer:
	//	uevent mdev &
	// 	find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
	//
	// SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl
	setsockopt(fd, SOL_SOCKET, SO_RCVBUF,      &RCVBUF, sizeof(RCVBUF));
	setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &RCVBUF, sizeof(RCVBUF));
	if (0) {
		int z;
		socklen_t zl = sizeof(z);
		getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl);
		bb_error_msg("SO_RCVBUF:%d", z);
	}

	for (;;) {
		char *netbuf;
		char *s, *end;
		ssize_t len;
		int idx;

		// In many cases, a system sits for *days* waiting
		// for a new uevent notification to come in.
		// We use a fresh mmap so that buffer is not allocated
		// until kernel actually starts filling it.
		netbuf = mmap(NULL, BUFFER_SIZE,
					PROT_READ | PROT_WRITE,
					MAP_PRIVATE | MAP_ANON,
					/* ignored: */ -1, 0);
		if (netbuf == MAP_FAILED)
			bb_perror_msg_and_die("mmap");

		// Here we block, possibly for a very long time
		len = safe_read(fd, netbuf, BUFFER_SIZE - 1);
		if (len < 0)
			bb_perror_msg_and_die("read");
		end = netbuf + len;
		*end = '\0';

		// Each netlink message starts with "ACTION@/path"
		// (which we currently ignore),
		// followed by environment variables.
		if (!argv[0])
			putchar('\n');
		idx = 0;
		s = netbuf;
		while (s < end) {
			if (!argv[0])
				puts(s);
			if (strchr(s, '=') && idx < MAX_ENV)
				env[idx++] = s;
			s += strlen(s) + 1;
		}
		env[idx] = NULL;

		idx = 0;
		while (env[idx])
			putenv(env[idx++]);
		if (argv[0])
			spawn_and_wait(argv);
		idx = 0;
		while (env[idx])
			bb_unsetenv(env[idx++]);
		munmap(netbuf, BUFFER_SIZE);
	}

	return 0; // not reached
}
Exemple #12
0
int install_main(int argc, char **argv)
{
	struct stat statbuf;
	mode_t mode;
	uid_t uid;
	gid_t gid;
	char *arg, *last;
	const char *gid_str;
	const char *uid_str;
	const char *mode_str;
	int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
	int opts;
	int min_args = 1;
	int ret = EXIT_SUCCESS;
	int isdir = 0;
#if ENABLE_SELINUX
	security_context_t scontext;
	bool use_default_selinux_context = 1;
#endif
	enum {
		OPT_c             = 1 << 0,
		OPT_v             = 1 << 1,
		OPT_b             = 1 << 2,
		OPT_MKDIR_LEADING = 1 << 3,
		OPT_DIRECTORY     = 1 << 4,
		OPT_PRESERVE_TIME = 1 << 5,
		OPT_STRIP         = 1 << 6,
		OPT_GROUP         = 1 << 7,
		OPT_MODE          = 1 << 8,
		OPT_OWNER         = 1 << 9,
#if ENABLE_SELINUX
		OPT_SET_SECURITY_CONTEXT = 1 << 10,
		OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11,
#endif
	};

#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
	applet_long_options = install_longopts;
#endif
	opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
	/* -c exists for backwards compatibility, it's needed */
	/* -v is ignored ("print name of each created directory") */
	/* -b is ignored ("make a backup of each existing destination file") */
	opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"),
			&gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext));
	argc -= optind;
	argv += optind;

#if ENABLE_SELINUX
	if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) {
		selinux_or_die();
		use_default_selinux_context = 0;
		if (opts & OPT_PRESERVE_SECURITY_CONTEXT) {
			copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
		}
		if (opts & OPT_SET_SECURITY_CONTEXT) {
			setfscreatecon_or_die(scontext);
			copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
		}
	}
#endif

	/* preserve access and modification time, this is GNU behaviour,
	 * BSD only preserves modification time */
	if (opts & OPT_PRESERVE_TIME) {
		copy_flags |= FILEUTILS_PRESERVE_STATUS;
	}
	mode = 0755; /* GNU coreutils 6.10 compat */
	if (opts & OPT_MODE)
		bb_parse_mode(mode_str, &mode);
	uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
	gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();

	last = argv[argc - 1];
	if (!(opts & OPT_DIRECTORY)) {
		argv[argc - 1] = NULL;
		min_args++;

		/* coreutils install resolves link in this case, don't use lstat */
		isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
	}

	if (argc < min_args)
		bb_show_usage();

	while ((arg = *argv++) != NULL) {
		char *dest = last;
		if (opts & OPT_DIRECTORY) {
			dest = arg;
			/* GNU coreutils 6.9 does not set uid:gid
			 * on intermediate created directories
			 * (only on last one) */
			if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) {
				ret = EXIT_FAILURE;
				goto next;
			}
		} else {
			if (opts & OPT_MKDIR_LEADING) {
				char *ddir = xstrdup(dest);
				bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR);
				/* errors are not checked. copy_file
				 * will fail if dir is not created. */
				free(ddir);
			}
			if (isdir)
				dest = concat_path_file(last, bb_basename(arg));
			if (copy_file(arg, dest, copy_flags) != 0) {
				/* copy is not made */
				ret = EXIT_FAILURE;
				goto next;
			}
			if (opts & OPT_STRIP) {
				char *args[4];
				args[0] = (char*)"strip";
				args[1] = (char*)"-p"; /* -p --preserve-dates */
				args[2] = dest;
				args[3] = NULL;
				if (spawn_and_wait(args)) {
					bb_perror_msg("strip");
					ret = EXIT_FAILURE;
				}
			}
		}

		/* Set the file mode (always, not only with -m).
		 * GNU coreutils 6.10 is not affected by umask. */
		if (chmod(dest, mode) == -1) {
			bb_perror_msg("can't change %s of %s", "permissions", dest);
			ret = EXIT_FAILURE;
		}
#if ENABLE_SELINUX
		if (use_default_selinux_context)
			setdefaultfilecon(dest);
#endif
		/* Set the user and group id */
		if ((opts & (OPT_OWNER|OPT_GROUP))
		 && lchown(dest, uid, gid) == -1
		) {
			bb_perror_msg("can't change %s of %s", "ownership", dest);
			ret = EXIT_FAILURE;
		}
 next:
		if (ENABLE_FEATURE_CLEAN_UP && isdir)
			free(dest);
	}

	return ret;
}
Exemple #13
0
int flock_main(int argc UNUSED_PARAM, char **argv)
{
	int mode, opt, fd;
	enum {
		OPT_s = (1 << 0),
		OPT_x = (1 << 1),
		OPT_n = (1 << 2),
		OPT_u = (1 << 3),
		OPT_c = (1 << 4),
	};

#if ENABLE_LONG_OPTS
	static const char getopt_longopts[] ALIGN1 =
		"shared\0"      No_argument       "s"
		"exclusive\0"   No_argument       "x"
		"unlock\0"      No_argument       "u"
		"nonblock\0"    No_argument       "n"
		;
	applet_long_options = getopt_longopts;
#endif
	opt_complementary = "-1";

	opt = getopt32(argv, "+sxnu");
	argv += optind;

	if (argv[1]) {
		fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666);
		if (fd < 0 && errno == EISDIR)
			fd = open(argv[0], O_RDONLY|O_NOCTTY);
		if (fd < 0)
			bb_perror_msg_and_die("can't open '%s'", argv[0]);
		//TODO? close_on_exec_on(fd);
	} else {
		fd = xatoi_positive(argv[0]);
	}
	argv++;

	/* If it is "flock FILE -c PROG", then -c isn't caught by getopt32:
	 * we use "+" in order to support "flock -opt FILE PROG -with-opts",
	 * we need to remove -c by hand.
	 */
	if (argv[0]
	 && argv[0][0] == '-'
	 && (  (argv[0][1] == 'c' && !argv[0][2])
	    || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0)
	    )
	) {
		argv++;
		if (argv[1])
			bb_error_msg_and_die("-c takes only one argument");
		opt |= OPT_c;
	}

	if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) {
		/* With suitably matched constants, mode setting is much simpler */
		mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN);
		if (!(mode & ~LOCK_NB))
			mode |= LOCK_EX;
	} else {
		if (opt & OPT_u)
			mode = LOCK_UN;
		else if (opt & OPT_s)
			mode = LOCK_SH;
		else
			mode = LOCK_EX;
		if (opt & OPT_n)
			mode |= LOCK_NB;
	}

	if (flock(fd, mode) != 0) {
		if (errno == EWOULDBLOCK)
			return EXIT_FAILURE;
		bb_perror_nomsg_and_die();
	}

	if (argv[0]) {
		int rc;
		if (opt & OPT_c) {
			/* -c 'PROG ARGS' means "run sh -c 'PROG ARGS'" */
			argv -= 2;
			argv[0] = (char*)get_shell_name();
			argv[1] = (char*)"-c";
			/* argv[2] = "PROG ARGS"; */
			/* argv[3] = NULL; */
		}
		rc = spawn_and_wait(argv);
		if (rc < 0)
			bb_simple_perror_msg(argv[0]);
		return rc;
	}

	return EXIT_SUCCESS;
}