Esempio n. 1
0
string
xgetcwd (void)
{
    /* If the system provides getcwd, use it.  If not, use getwd if
       available.  But provide a way not to use getcwd: on some systems
       getcwd forks, which is expensive and may in fact be impossible for
       large programs like tex.  If your system needs this define and it
       is not detected by configure, let me know.
                                       -- Olaf Weber <[email protected] */
#if defined (HAVE_GETCWD) && !defined (GETCWD_FORKS)
    char path[PATH_MAX + 1];
#if defined(WIN32)
    string pp;
#endif

    if (getcwd (path, PATH_MAX + 1) == NULL) {
        FATAL_PERROR ("getcwd");
    }

#if defined(WIN32)
    for (pp = path; *pp; pp++) {
        if (*pp == '\\')
            *pp = '/';
#if defined (KPSE_COMPAT_API)
        else if (IS_KANJI(pp))
            pp++;
#endif
    }
#endif

    return xstrdup (path);
#elif defined (HAVE_GETWD)
    char path[PATH_MAX + 1];

    if (getwd (path) == NULL) {
        FATAL_PERROR ("getwd");
    }

    return xstrdup (path);
#else /* (not HAVE_GETCWD || GETCWD_FORKS) && not HAVE_GETWD */
    struct stat root_stat, cwd_stat;
    string cwd_path = (string)xmalloc(2); /* In case we assign "/" below.  */

    *cwd_path = 0;

    /* Find the inodes of the root and current directories.  */
    root_stat = xstat("/");
    cwd_stat  = xstat(".");

    /* Go up the directory hierarchy until we get to root, prepending each
       directory we pass through to `cwd_path'.  */
    while (!SAME_FILE_P(root_stat, cwd_stat)) {
        struct dirent *e;
        DIR *parent_dir;
        boolean found = false;

        xchdir("..");
        parent_dir = xopendir(".");

        /* Look through the parent directory for the entry with the same
           inode, so we can get its name.  */
        while ((e = readdir (parent_dir)) != NULL && !found) {
            struct stat test_stat;
            test_stat = xlstat(e->d_name);

            if (SAME_FILE_P(test_stat, cwd_stat)) {
                /* We've found it.  Prepend the pathname.  */
                string temp = cwd_path;
                cwd_path = concat3("/", e->d_name, cwd_path);
                free(temp);

                /* Set up to test the next parent.  */
                cwd_stat = xstat(".");

                /* Stop reading this directory.  */
                found = true;
            }
        }
        if (!found)
            LIB_FATAL2("No inode %d/device %d in parent directory",
                   cwd_stat.st_ino, cwd_stat.st_dev);

        xclosedir(parent_dir);
    }

    /* If the current directory is the root, cwd_path will be the empty
       string, and we will have not gone through the loop.  */
    if (*cwd_path == 0)
        strcpy(cwd_path, "/");
    else
        /* Go back to where we were.  */
        xchdir(cwd_path);

#ifdef DOSISH
    /* Prepend the drive letter to CWD_PATH, since this technique
       never tells us what the drive is.

       Note that on MS-DOS/MS-Windows, the branch that works around
       missing `getwd' will probably only work for DJGPP (which does
       have `getwd'), because only DJGPP reports meaningful
       st_ino numbers.  But someday, somebody might need this...  */
    {
        char drive[3];
        string temp = cwd_path;

        /* Make the drive letter lower-case, unless it is beyond Z: (yes,
           there ARE such drives, in case of Novell Netware on MS-DOS).  */
        drive[0] = root_stat.st_dev + (root_stat.st_dev < 26 ? 'a' : 'A');
        drive[1] = ':';
        drive[2] = '\0';

        cwd_path = concat(drive, cwd_path);
        free(temp);
    }
#endif

    return cwd_path;
#endif /* (not HAVE_GETCWD || GETCWD_FORKS) && not HAVE_GETWD */
}
Esempio n. 2
0
int tar_main(int argc UNUSED_PARAM, char **argv)
{
	char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar;
	archive_handle_t *tar_handle;
	char *base_dir = NULL;
	const char *tar_filename = "-";
	unsigned opt;
	int verboseFlag = 0;
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
	llist_t *excludes = NULL;
#endif

	/* Initialise default values */
	tar_handle = init_handle();
	tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS
	                     | ARCHIVE_PRESERVE_DATE
	                     | ARCHIVE_EXTRACT_UNCONDITIONAL;

	/* Apparently only root's tar preserves perms (see bug 3844) */
	if (getuid() != 0)
		tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM;

	/* Prepend '-' to the first argument if required */
	opt_complementary = "--:" // first arg is options
		"tt:vv:" // count -t,-v
		"?:" // bail out with usage instead of error return
		"X::T::" // cumulative lists
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
		"\xff::" // cumulative lists for --exclude
#endif
		IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
		IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
		IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
	applet_long_options = tar_longopts;
#endif
	opt = getopt32(argv,
		"txC:f:Opvk"
		IF_FEATURE_TAR_CREATE(   "ch"  )
		IF_FEATURE_SEAMLESS_BZ2( "j"   )
		IF_FEATURE_SEAMLESS_LZMA("a"   )
		IF_FEATURE_TAR_FROM(     "T:X:")
		IF_FEATURE_SEAMLESS_GZ(  "z"   )
		IF_FEATURE_SEAMLESS_Z(   "Z"   )
		, &base_dir // -C dir
		, &tar_filename // -f filename
		IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
		IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
		, &excludes // --exclude
#endif
		, &verboseFlag // combined count for -t and -v
		, &verboseFlag // combined count for -t and -v
		);
	argv += optind;

	if (verboseFlag) tar_handle->action_header = header_verbose_list;
	if (verboseFlag == 1) tar_handle->action_header = header_list;

	if (opt & OPT_EXTRACT)
		tar_handle->action_data = data_extract_all;

	if (opt & OPT_2STDOUT)
		tar_handle->action_data = data_extract_to_stdout;

	if (opt & OPT_KEEP_OLD)
		tar_handle->ah_flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;

	if (opt & OPT_NOPRESERVE_OWN)
		tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_OWN;

	if (opt & OPT_NOPRESERVE_PERM)
		tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM;

	if (opt & OPT_NUMERIC_OWNER)
		tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER;

	if (opt & OPT_GZIP)
		get_header_ptr = get_header_tar_gz;

	if (opt & OPT_BZIP2)
		get_header_ptr = get_header_tar_bz2;

	if (opt & OPT_LZMA)
		get_header_ptr = get_header_tar_lzma;

	if (opt & OPT_COMPRESS)
		get_header_ptr = get_header_tar_Z;

#if ENABLE_FEATURE_TAR_FROM
	tar_handle->reject = append_file_list_to_list(tar_handle->reject);
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
	/* Append excludes to reject */
	while (excludes) {
		llist_t *next = excludes->link;
		excludes->link = tar_handle->reject;
		tar_handle->reject = excludes;
		excludes = next;
	}
#endif
	tar_handle->accept = append_file_list_to_list(tar_handle->accept);
#endif

	/* Setup an array of filenames to work with */
	/* TODO: This is the same as in ar, separate function ? */
	while (*argv) {
		/* kill trailing '/' unless the string is just "/" */
		char *cp = last_char_is(*argv, '/');
		if (cp > *argv)
			*cp = '\0';
		llist_add_to_end(&tar_handle->accept, *argv);
		argv++;
	}

	if (tar_handle->accept || tar_handle->reject)
		tar_handle->filter = filter_accept_reject_list;

	/* Open the tar file */
	{
		FILE *tar_stream;
		int flags;

		if (opt & OPT_CREATE) {
			/* Make sure there is at least one file to tar up.  */
			if (tar_handle->accept == NULL)
				bb_error_msg_and_die("empty archive");

			tar_stream = stdout;
			/* Mimicking GNU tar 1.15.1: */
			flags = O_WRONLY | O_CREAT | O_TRUNC;
		} else {
			tar_stream = stdin;
			flags = O_RDONLY;
		}

		if (LONE_DASH(tar_filename)) {
			tar_handle->src_fd = fileno(tar_stream);
			tar_handle->seek = seek_by_read;
		} else {
			if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) {
				get_header_ptr = get_header_tar;
				tar_handle->src_fd = open_zipped(tar_filename);
				if (tar_handle->src_fd < 0)
					bb_perror_msg_and_die("can't open '%s'", tar_filename);
			} else {
				tar_handle->src_fd = xopen(tar_filename, flags);
			}
		}
	}

	if (base_dir)
		xchdir(base_dir);

#ifdef CHECK_FOR_CHILD_EXITCODE
	/* We need to know whether child (gzip/bzip/etc) exits abnormally */
	signal(SIGCHLD, handle_SIGCHLD);
#endif

	/* create an archive */
	if (opt & OPT_CREATE) {
#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
		int zipMode = 0;
		if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP))
			zipMode = 1;
		if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2))
			zipMode = 2;
#endif
		/* NB: writeTarFile() closes tar_handle->src_fd */
		return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
				tar_handle->accept,
				tar_handle->reject, zipMode);
	}

	while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
		continue;

	/* Check that every file that should have been extracted was */
	while (tar_handle->accept) {
		if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
		 && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
		) {
			bb_error_msg_and_die("%s: not found in archive",
				tar_handle->accept->data);
		}
		tar_handle->accept = tar_handle->accept->link;
	}
	if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
		close(tar_handle->src_fd);

	return EXIT_SUCCESS;
}
Esempio n. 3
0
/* put a new crontab into CRONDIR
 */
int
newcrontab(char *file, char *name)
{
    static crontab tab = { 0 };
    int tempfile = 0;
    register c;
    FILE *in, *out;
    struct utimbuf touch;
    int save_euid = geteuid();

    umask(077);

    if ( strcmp(file, "-") == 0 ) {
	long size = 0;
	if ( (in = tmpfile()) == 0 ) {
	    error("can't create tempfile: %s", strerror(errno));
	    return 0;
	}
	while ( ((c = getchar()) != EOF) && !ferror(in) ) {
	    ++size;
	    fputc(c, in);
	}
	if (ferror(in) || (fflush(in) == EOF) || fseek(in, 0L, SEEK_SET) == -1 ) {
	    fclose(in);
	    error("can't store crontab in tempfile: %s", strerror(errno));
	    return 0;
	}
	if (size == 0) {
	    fatal("no input; use -r to delete a crontab");
	}
	tempfile = 1;
    }
    else {
	seteuid(getuid());
	in = fopen(file, "r");
	seteuid(save_euid);
	if ( in == 0 ) {
	    error("can't open %s: %s", file, strerror(errno));
	    return 0;
	}
    }

    zerocrontab(&tab);
    if ( !readcrontab(&tab, in) ) {
	fclose(in);
	return 0;
    }
    if ( tempfile && (tab.nrl == 0) && (tab.nre == 0) ) {
	error("no jobs or environment variables defined; use -r to delete a crontab");
	return 0;
    }
    if ( fseek(in, 0L, SEEK_SET) == -1 ) {
	fclose(in);
	error("file error: %s", strerror(errno));
	return 0;
    }

    superpowers();
    xchdir(CRONDIR);

    if ( out = fopen(name, "w") ) {
	while ( ((c = fgetc(in)) != EOF) && (fputc(c,out) != EOF) )
	    ;
	if ( ferror(out) || (fclose(out) == EOF) ) {
	    unlink(name);
	    fatal("can't write to crontab: %s", strerror(errno));
	}
	fclose(in);
	time(&touch.modtime);
	time(&touch.actime);
	utime(".", &touch);
	exit(0);
    }
    fatal("can't write to crontab: %s", strerror(errno));
}
Esempio n. 4
0
int rtcwake_main(int argc UNUSED_PARAM, char **argv)
{
	time_t rtc_time;

	unsigned opt;
	const char *rtcname = NULL;
	const char *suspend;
	const char *opt_seconds;
	const char *opt_time;

	time_t sys_time;
	time_t alarm_time = 0;
	unsigned seconds = 0;
	int utc = -1;
	int fd;

#if ENABLE_LONG_OPTS
	static const char rtcwake_longopts[] ALIGN1 =
		"auto\0"    No_argument "a"
		"local\0"   No_argument "l"
		"utc\0"     No_argument "u"
		"device\0"  Required_argument "d"
		"mode\0"    Required_argument "m"
		"seconds\0" Required_argument "s"
		"time\0"    Required_argument "t"
		;
	applet_long_options = rtcwake_longopts;
#endif
	opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time);

	/* this is the default
	if (opt & RTCWAKE_OPT_AUTO)
		utc = -1;
	*/
	if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
		utc = opt & RTCWAKE_OPT_UTC;
	if (!(opt & RTCWAKE_OPT_SUSPEND_MODE))
		suspend = DEFAULT_MODE;
	if (opt & RTCWAKE_OPT_SECONDS)
		/* alarm time, seconds-to-sleep (relative) */
		seconds = xatoi(opt_seconds);
	if (opt & RTCWAKE_OPT_TIME)
		/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
		alarm_time = xatol(opt_time);

	if (!alarm_time && !seconds)
		bb_error_msg_and_die("must provide wake time");

	if (utc == -1)
		utc = rtc_adjtime_is_utc();

	/* the rtcname is relative to /dev */
	xchdir("/dev");

	/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
	fd = rtc_xopen(&rtcname, O_RDONLY);

	if (strcmp(suspend, "on") && !may_wakeup(rtcname))
		bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);

	/* relative or absolute alarm time, normalized to time_t */
	sys_time = time(NULL);
	{
		struct tm tm_time;
		rtc_read_tm(&tm_time, fd);
		rtc_time = rtc_tm2time(&tm_time, utc);
	}


	if (alarm_time) {
		if (alarm_time < sys_time)
			bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
		alarm_time += sys_time - rtc_time;
	} else
		alarm_time = rtc_time + seconds + 1;
	setup_alarm(fd, &alarm_time, rtc_time);

	sync();
	printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
	fflush_all();
	usleep(10 * 1000);

	if (strcmp(suspend, "on"))
		xopen_xwrite_close(SYS_POWER_PATH, suspend);
	else {
		/* "fake" suspend ... we'll do the delay ourselves */
		unsigned long data;

		do {
			ssize_t ret = safe_read(fd, &data, sizeof(data));
			if (ret < 0) {
				bb_perror_msg("rtc read");
				break;
			}
		} while (!(data & RTC_AF));
	}

	xioctl(fd, RTC_AIE_OFF, 0);

	if (ENABLE_FEATURE_CLEAN_UP)
		close(fd);

	return EXIT_SUCCESS;
}
Esempio n. 5
0
int crontab_main(int argc UNUSED_PARAM, char **argv)
{
	const struct passwd *pas;
	const char *crontab_dir = CRONTABS;
	char *tmp_fname;
	char *new_fname;
	char *user_name;  /* -u USER */
	int fd;
	int src_fd;
	int opt_ler;

	/* file [opts]     Replace crontab from file
	 * - [opts]        Replace crontab from stdin
	 * -u user         User
	 * -c dir          Crontab directory
	 * -l              List crontab for user
	 * -e              Edit crontab for user
	 * -r              Delete crontab for user
	 * bbox also supports -d == -r, but most other crontab
	 * implementations do not. Deprecated.
	 */
	enum {
		OPT_u = (1 << 0),
		OPT_c = (1 << 1),
		OPT_l = (1 << 2),
		OPT_e = (1 << 3),
		OPT_r = (1 << 4),
		OPT_ler = OPT_l + OPT_e + OPT_r,
	};

	opt_complementary = "?1:dr"; /* max one argument; -d implies -r */
	opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir);
	argv += optind;

	if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
		/* run by non-root? */
		if (opt_ler & (OPT_u|OPT_c))
			bb_error_msg_and_die("only root can use -c or -u");
	}

	if (opt_ler & OPT_u) {
		pas = getpwnam(user_name);
		if (!pas)
			bb_error_msg_and_die("user %s is not known", user_name);
	} else {
/* XXX: xgetpwuid */
		uid_t my_uid = getuid();
		pas = getpwuid(my_uid);
		if (!pas)
			bb_perror_msg_and_die("unknown uid %d", (int)my_uid);
	}

#define user_name DONT_USE_ME_BEYOND_THIS_POINT

	/* From now on, keep only -l, -e, -r bits */
	opt_ler &= OPT_ler;
	if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
		bb_show_usage();

	/* Read replacement file under user's UID/GID/group vector */
	src_fd = STDIN_FILENO;
	if (!opt_ler) { /* Replace? */
		if (!argv[0])
			bb_show_usage();
		if (NOT_LONE_DASH(argv[0])) {
			src_fd = open_as_user(pas, argv[0]);
			if (src_fd < 0)
				bb_error_msg_and_die("user %s cannot read %s",
						pas->pw_name, argv[0]);
		}
	}

	/* cd to our crontab directory */
	xchdir(crontab_dir);

	tmp_fname = NULL;

	/* Handle requested operation */
	switch (opt_ler) {

	default: /* case OPT_r: Delete */
		unlink(pas->pw_name);
		break;

	case OPT_l: /* List */
		{
			char *args[2] = { pas->pw_name, NULL };
			return bb_cat(args);
			/* list exits,
			 * the rest go play with cron update file */
		}

	case OPT_e: /* Edit */
		tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid());
		/* No O_EXCL: we don't want to be stuck if earlier crontabs
		 * were killed, leaving stale temp file behind */
		src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
		fchown(src_fd, pas->pw_uid, pas->pw_gid);
		fd = open(pas->pw_name, O_RDONLY);
		if (fd >= 0) {
			bb_copyfd_eof(fd, src_fd);
			close(fd);
			xlseek(src_fd, 0, SEEK_SET);
		}
		close_on_exec_on(src_fd); /* don't want editor to see this fd */
		edit_file(pas, tmp_fname);
		/* fall through */

	case 0: /* Replace (no -l, -e, or -r were given) */
		new_fname = xasprintf("%s.new", pas->pw_name);
		fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
		if (fd >= 0) {
			bb_copyfd_eof(src_fd, fd);
			close(fd);
			xrename(new_fname, pas->pw_name);
		} else {
			bb_error_msg("cannot create %s/%s",
					crontab_dir, new_fname);
		}
		if (tmp_fname)
			unlink(tmp_fname);
		/*free(tmp_fname);*/
		/*free(new_fname);*/

	} /* switch */

	/* Bump notification file.  Handle window where crond picks file up
	 * before we can write our entry out.
	 */
	while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
		struct stat st;

		fdprintf(fd, "%s\n", pas->pw_name);
		if (fstat(fd, &st) != 0 || st.st_nlink != 0) {
			/*close(fd);*/
			break;
		}
		/* st.st_nlink == 0:
		 * file was deleted, maybe crond missed our notification */
		close(fd);
		/* loop */
	}
	if (fd < 0) {
		bb_error_msg("cannot append to %s/%s",
				crontab_dir, CRONUPDATE);
	}
	return 0;
}
Esempio n. 6
0
int lpd_main(int argc UNUSED_PARAM, char *argv[])
{
	int spooling = spooling; // for compiler
	char *s, *queue;
	char *filenames[2];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 err_exit:
	// don't keep corrupted files
	if (spooling) {
#define i spooling
		for (i = 2; --i >= 0; )
			if (filenames[i])
				unlink(filenames[i]);
	}
	return EXIT_FAILURE;
}
Esempio n. 7
0
int rtcwake_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opt;
	const char *rtcname = NULL;
	const char *suspend = "standby";
	const char *opt_seconds;
	const char *opt_time;

	time_t rtc_time;
	time_t sys_time;
	time_t alarm_time = alarm_time;
	unsigned seconds = seconds; /* for compiler */
	int utc = -1;
	int fd;

#if ENABLE_LONG_OPTS
	static const char rtcwake_longopts[] ALIGN1 =
		"auto\0"    No_argument "a"
		"local\0"   No_argument "l"
		"utc\0"     No_argument "u"
		"device\0"  Required_argument "d"
		"mode\0"    Required_argument "m"
		"seconds\0" Required_argument "s"
		"time\0"    Required_argument "t"
		;
#endif
	opt = getopt32long(argv,
			/* Must have -s or -t, exclusive */
			"^alud:m:s:t:" "\0" "s:t:s--t:t--s", rtcwake_longopts,
			&rtcname, &suspend, &opt_seconds, &opt_time);

	/* this is the default
	if (opt & RTCWAKE_OPT_AUTO)
		utc = -1;
	*/
	if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
		utc = opt & RTCWAKE_OPT_UTC;
	if (opt & RTCWAKE_OPT_SECONDS) {
		/* alarm time, seconds-to-sleep (relative) */
		seconds = xatou(opt_seconds);
	} else {
		/* RTCWAKE_OPT_TIME */
		/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
		if (sizeof(alarm_time) <= sizeof(long))
			alarm_time = xatol(opt_time);
		else
			alarm_time = xatoll(opt_time);
	}

	if (utc == -1)
		utc = rtc_adjtime_is_utc();

	/* the rtcname is relative to /dev */
	xchdir("/dev");

	/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
	fd = rtc_xopen(&rtcname, O_RDONLY);

	if (strcmp(suspend, "on") != 0)
		if (!may_wakeup(rtcname))
			bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);

	/* relative or absolute alarm time, normalized to time_t */
	sys_time = time(NULL);
	{
		struct tm tm_time;
		rtc_read_tm(&tm_time, fd);
		rtc_time = rtc_tm2time(&tm_time, utc);
	}

	if (opt & RTCWAKE_OPT_TIME) {
		/* Correct for RTC<->system clock difference */
		alarm_time += rtc_time - sys_time;
		if (alarm_time < rtc_time)
			/*
			 * Compat message text.
			 * I'd say "RTC time is already ahead of ..." instead.
			 */
			bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
	} else
		alarm_time = rtc_time + seconds + 1;

	setup_alarm(fd, &alarm_time, rtc_time);
	sync();
#if 0 /*debug*/
	printf("sys_time: %s", ctime(&sys_time));
	printf("rtc_time: %s", ctime(&rtc_time));
#endif
	printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
	fflush_all();
	usleep(10 * 1000);

	if (strcmp(suspend, "on") != 0)
		xopen_xwrite_close(SYS_POWER_PATH, suspend);
	else {
		/* "fake" suspend ... we'll do the delay ourselves */
		unsigned long data;

		do {
			ssize_t ret = safe_read(fd, &data, sizeof(data));
			if (ret < 0) {
				bb_perror_msg("rtc read");
				break;
			}
		} while (!(data & RTC_AF));
	}

	xioctl(fd, RTC_AIE_OFF, 0);

	if (ENABLE_FEATURE_CLEAN_UP)
		close(fd);

	return EXIT_SUCCESS;
}
Esempio n. 8
0
void FAST_FUNC xchroot(const char *path)
{
	if (chroot(path))
		bb_perror_msg_and_die("can't change root directory to '%s'", path);
	xchdir("/");
}
Esempio n. 9
0
int dpkg_deb_main(int argc, char **argv)
{
	archive_handle_t *ar_archive;
	archive_handle_t *tar_archive;
	llist_t *control_tar_llist = NULL;
	unsigned opt;
	const char *extract_dir;
	int need_args;

	/* Setup the tar archive handle */
	tar_archive = init_handle();

	/* Setup an ar archive handle that refers to the gzip sub archive */
	ar_archive = init_handle();
	ar_archive->dpkg__sub_archive = tar_archive;
	ar_archive->filter = filter_accept_list_reassign;

	llist_add_to(&ar_archive->accept, (char*)"data.tar");
	llist_add_to(&control_tar_llist, (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
	llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
	llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
	llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2");
	llist_add_to(&control_tar_llist, (char*)"control.tar.bz2");
#endif
#if ENABLE_FEATURE_SEAMLESS_LZ
	llist_add_to(&ar_archive->accept, (char*)"data.tar.lz");
	llist_add_to(&control_tar_llist, (char*)"control.tar.lz");
#endif
#if ENABLE_FEATURE_SEAMLESS_LZMA
	llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
	llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
	llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
	llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
#endif

	opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
	opt = getopt32(argv, "cefXx");
	argv += optind;
	argc -= optind;

	if (opt & DPKG_DEB_OPT_CONTENTS) {
		tar_archive->action_header = header_verbose_list;
	}
	extract_dir = NULL;
	need_args = 1;
	if (opt & DPKG_DEB_OPT_CONTROL) {
		ar_archive->accept = control_tar_llist;
		tar_archive->action_data = data_extract_all;
		if (1 == argc) {
			extract_dir = "./DEBIAN";
		} else {
			need_args++;
		}
	}
	if (opt & DPKG_DEB_OPT_FIELD) {
		/* Print the entire control file
		 * it should accept a second argument which specifies a
		 * specific field to print */
		ar_archive->accept = control_tar_llist;
		llist_add_to(&(tar_archive->accept), (char*)"./control");
		tar_archive->filter = filter_accept_list;
		tar_archive->action_data = data_extract_to_stdout;
	}
	if (opt & DPKG_DEB_OPT_EXTRACT) {
		tar_archive->action_header = header_list;
	}
	if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
		tar_archive->action_data = data_extract_all;
		need_args = 2;
	}

	if (need_args != argc) {
		bb_show_usage();
	}

	tar_archive->src_fd = ar_archive->src_fd = xopen(argv[0], O_RDONLY);

	/* Work out where to extract the files */
	/* 2nd argument is a dir name */
	if (argv[1]) {
		extract_dir = argv[1];
	}
	if (extract_dir) {
		mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
		xchdir(extract_dir);
	}

	/* Do it */
	unpack_ar_archive(ar_archive);

	/* Cleanup */
	if (ENABLE_FEATURE_CLEAN_UP)
		close(ar_archive->src_fd);

	return EXIT_SUCCESS;
}
Esempio n. 10
0
void xchroot(char *path)
{
  if (chroot(path)) error_exit("chroot '%s'", path);
  xchdir("/");
}
Esempio n. 11
0
static void edir(const char *directory_name)
{
	int wdir;
	DIR *dir;
	struct dirent *d;
	int fd;

	wdir = xopen(".", O_RDONLY | O_NDELAY);
	xchdir(directory_name);
	dir = opendir(".");
	if (!dir)
		bb_perror_msg_and_die("opendir %s", directory_name);
	for (;;) {
		errno = 0;
		d = readdir(dir);
		if (!d) {
			if (errno)
				bb_perror_msg_and_die("readdir %s",
						directory_name);
			break;
		}
		if (d->d_name[0] == '.') continue;
		fd = open(d->d_name, O_RDONLY | O_NDELAY);
		if (fd < 0) {
			if ((errno == EISDIR) && env_dir) {
				if (OPT_verbose)
					bb_perror_msg("warning: %s/%s is a directory",
						directory_name,	d->d_name);
				continue;
			} else
				bb_perror_msg_and_die("open %s/%s",
						directory_name, d->d_name);
		}
		if (fd >= 0) {
			char buf[256];
			char *tail;
			int size;

			size = safe_read(fd, buf, sizeof(buf)-1);
			if (size < 0)
				bb_perror_msg_and_die("read %s/%s",
						directory_name, d->d_name);
			if (size == 0) {
				unsetenv(d->d_name);
				continue;
			}
			buf[size] = '\n';
			tail = memchr(buf, '\n', sizeof(buf));
			/* skip trailing whitespace */;
			while (1) {
				if (tail[0]==' ') tail[0] = '\0';
				if (tail[0]=='\t') tail[0] = '\0';
				if (tail[0]=='\n') tail[0] = '\0';
				if (tail == buf) break;
				tail--;
			}
			xsetenv(d->d_name, buf);
		}
	}
	closedir(dir);
	if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
	close(wdir);
}
Esempio n. 12
0
/* Returns pid */
pid_t fork_execv_on_steroids(int flags,
		char **argv,
		int *pipefds,
		char **env_vec,
		const char *dir,
		uid_t uid)
{
	pid_t child;
	/* Reminder: [0] is read end, [1] is write end */
	int pipe_to_child[2];
	int pipe_fm_child[2];

	/* Sanitize flags */
	if (!pipefds)
		flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT);

	if (flags & EXECFLG_INPUT)
		xpipe(pipe_to_child);
	if (flags & EXECFLG_OUTPUT)
		xpipe(pipe_fm_child);

	/* Prepare it before fork, to avoid thread-unsafe malloc there */
	char *prog_as_string = NULL;
	prog_as_string = concat_str_vector(argv);
	gid_t gid;
	if (flags & EXECFLG_SETGUID) {
		struct passwd* pw = getpwuid(uid);
		gid = pw ? pw->pw_gid : uid;
	}

	fflush(NULL);
	child = fork();
	if (child == -1) {
		perror_msg_and_die("fork");
	}
	if (child == 0) {
		/* Child */

		if (dir)
			xchdir(dir);

		if (flags & EXECFLG_SETGUID) {
			setgroups(1, &gid);
			xsetregid(gid, gid);
			xsetreuid(uid, uid);
		}

		if (env_vec) {
			/* Note: we use the glibc extension that putenv("var")
			 * *unsets* $var if "var" string has no '=' */
			while (*env_vec)
				putenv(*env_vec++);
		}

		/* Play with stdio descriptors */
		if (flags & EXECFLG_INPUT) {
			/* NB: close must be first, because
			 * pipe_to_child[1] may be equal to STDIN_FILENO
			 */
			close(pipe_to_child[1]);
			xmove_fd(pipe_to_child[0], STDIN_FILENO);
		} else if (flags & EXECFLG_INPUT_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
		}
		if (flags & EXECFLG_OUTPUT) {
			close(pipe_fm_child[0]);
			xmove_fd(pipe_fm_child[1], STDOUT_FILENO);
		} else if (flags & EXECFLG_OUTPUT_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO);
		}

		/* This should be done BEFORE stderr redirect */
		log_notice("Executing: %s", prog_as_string);

		if (flags & EXECFLG_ERR2OUT) {
			/* Want parent to see errors in the same stream */
			xdup2(STDOUT_FILENO, STDERR_FILENO);
		} else if (flags & EXECFLG_ERR_NUL) {
			xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO);
		}

		if (flags & EXECFLG_SETSID)
			setsid();
		if (flags & EXECFLG_SETPGID)
			setpgid(0, 0);

		execvp(argv[0], argv);
		if (!(flags & EXECFLG_QUIET))
			perror_msg("Can't execute '%s'", argv[0]);
		_exit(127); /* shell uses this exit code in this case */
	}

	/* Parent */
	free(prog_as_string);

	if (flags & EXECFLG_INPUT) {
		close(pipe_to_child[0]);
		pipefds[1] = pipe_to_child[1];
	}
	if (flags & EXECFLG_OUTPUT) {
		close(pipe_fm_child[1]);
		pipefds[0] = pipe_fm_child[0];
	}

	return child;
}
Esempio n. 13
0
int crontab_main(int ac, char **av)
{
	enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
	const struct passwd *pas;
	const char *repFile = NULL;
	int repFd = 0;
	int i;
	char caller[256];           /* user that ran program */
	char buf[1024];
	int UserId;

	UserId = getuid();
	pas = getpwuid(UserId);
	if (pas == NULL)
		bb_perror_msg_and_die("getpwuid");

	safe_strncpy(caller, pas->pw_name, sizeof(caller));

	i = 1;
	if (ac > 1) {
		if (LONE_DASH(av[1])) {
			option = REPLACE;
			++i;
		} else if (av[1][0] != '-') {
			option = REPLACE;
			++i;
			repFile = av[1];
		}
	}

	for (; i < ac; ++i) {
		char *ptr = av[i];

		if (*ptr != '-')
			break;
		ptr += 2;

		switch (ptr[-1]) {
		case 'l':
			if (ptr[-1] == 'l')
				option = LIST;
			/* fall through */
		case 'e':
			if (ptr[-1] == 'e')
				option = EDIT;
			/* fall through */
		case 'd':
			if (ptr[-1] == 'd')
				option = DELETE;
			/* fall through */
		case 'u':
			if (i + 1 < ac && av[i+1][0] != '-') {
				++i;
				if (getuid() == geteuid()) {
					pas = getpwnam(av[i]);
					if (pas) {
						UserId = pas->pw_uid;
					} else {
						bb_error_msg_and_die("user %s unknown", av[i]);
					}
				} else {
					bb_error_msg_and_die("only the superuser may specify a user");
				}
			}
			break;
		case 'c':
			if (getuid() == geteuid()) {
				CDir = (*ptr) ? ptr : av[++i];
			} else {
				bb_error_msg_and_die("-c option: superuser only");
			}
			break;
		default:
			i = ac;
			break;
		}
	}
	if (i != ac || option == NONE)
		bb_show_usage();

	/*
	 * Get password entry
	 */

	pas = getpwuid(UserId);
	if (pas == NULL)
		bb_perror_msg_and_die("getpwuid");

	/*
	 * If there is a replacement file, obtain a secure descriptor to it.
	 */

	if (repFile) {
		repFd = GetReplaceStream(caller, repFile);
		if (repFd < 0)
			bb_error_msg_and_die("cannot read replacement file");
	}

	/*
	 * Change directory to our crontab directory
	 */

	xchdir(CDir);

	/*
	 * Handle options as appropriate
	 */

	switch (option) {
	case LIST:
		{
			FILE *fi;

			fi = fopen(pas->pw_name, "r");
			if (fi) {
				while (fgets(buf, sizeof(buf), fi) != NULL)
					fputs(buf, stdout);
				fclose(fi);
			} else {
				bb_error_msg("no crontab for %s", pas->pw_name);
			}
		}
		break;
	case EDIT:
		{
/* FIXME: messy code here! we have file copying helpers for this! */
			FILE *fi;
			int fd;
			int n;
			char tmp[128];

			snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
			fd = xopen3(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600);
/* race, use fchown */
			chown(tmp, getuid(), getgid());
			fi = fopen(pas->pw_name, "r");
			if (fi) {
				while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
					full_write(fd, buf, n);
			}
			EditFile(caller, tmp);
			remove(tmp);
			lseek(fd, 0L, SEEK_SET);
			repFd = fd;
		}
		option = REPLACE;
		/* fall through */
	case REPLACE:
		{
/* same here */
			char path[1024];
			int fd;
			int n;

			snprintf(path, sizeof(path), "%s.new", pas->pw_name);
			fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600);
			if (fd >= 0) {
				while ((n = read(repFd, buf, sizeof(buf))) > 0) {
					full_write(fd, buf, n);
				}
				close(fd);
				rename(path, pas->pw_name);
			} else {
				bb_error_msg("cannot create %s/%s", CDir, path);
			}
			close(repFd);
		}
		break;
	case DELETE:
		remove(pas->pw_name);
		break;
	case NONE:
	default:
		break;
	}

	/*
	 *  Bump notification file.  Handle window where crond picks file up
	 *  before we can write our entry out.
	 */

	if (option == REPLACE || option == DELETE) {
		FILE *fo;
		struct stat st;

		while ((fo = fopen(CRONUPDATE, "a"))) {
			fprintf(fo, "%s\n", pas->pw_name);
			fflush(fo);
			if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
				fclose(fo);
				break;
			}
			fclose(fo);
			/* loop */
		}
		if (fo == NULL) {
			bb_error_msg("cannot append to %s/%s", CDir, CRONUPDATE);
		}
	}
	return 0;
}
Esempio n. 14
0
/* TODO: use recursive_action? */
static NOINLINE void edir(const char *directory_name)
{
	int wdir;
	DIR *dir;
	struct dirent *d;
	int fd;

	wdir = xopen(".", O_RDONLY | O_NDELAY);
	xchdir(directory_name);
	dir = xopendir(".");
	for (;;) {
		char buf[256];
		char *tail;
		int size;

		errno = 0;
		d = readdir(dir);
		if (!d) {
			if (errno)
				bb_perror_msg_and_die("readdir %s",
						directory_name);
			break;
		}
		if (d->d_name[0] == '.')
			continue;
		fd = open(d->d_name, O_RDONLY | O_NDELAY);
		if (fd < 0) {
			if ((errno == EISDIR) && directory_name) {
				if (option_mask32 & OPT_v)
					bb_perror_msg("warning: %s/%s is a directory",
						directory_name, d->d_name);
				continue;
			}
			bb_perror_msg_and_die("open %s/%s",
						directory_name, d->d_name);
		}
		size = full_read(fd, buf, sizeof(buf)-1);
		close(fd);
		if (size < 0)
			bb_perror_msg_and_die("read %s/%s",
					directory_name, d->d_name);
		if (size == 0) {
			unsetenv(d->d_name);
			continue;
		}
		buf[size] = '\n';
		tail = strchr(buf, '\n');
		/* skip trailing whitespace */
		while (1) {
			*tail = '\0';
			tail--;
			if (tail < buf || !isspace(*tail))
				break;
		}
		xsetenv(d->d_name, buf);
	}
	closedir(dir);
	if (fchdir(wdir) == -1)
		bb_perror_msg_and_die("fchdir");
	close(wdir);
}
Esempio n. 15
0
int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
{
	archive_handle_t *ar_archive;
	archive_handle_t *tar_archive;
	llist_t *control_tar_llist = NULL;
	unsigned opt;
	const char *extract_dir;

	/* Setup the tar archive handle */
	tar_archive = init_handle();

	/* Setup an ar archive handle that refers to the gzip sub archive */
	ar_archive = init_handle();
	ar_archive->dpkg__sub_archive = tar_archive;
	ar_archive->filter = filter_accept_list_reassign;

	llist_add_to(&ar_archive->accept, (char*)"data.tar");
	llist_add_to(&control_tar_llist, (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
	llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
	llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
	llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2");
	llist_add_to(&control_tar_llist, (char*)"control.tar.bz2");
#endif
#if ENABLE_FEATURE_SEAMLESS_LZMA
	llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
	llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
	llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
	llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
#endif

	/* Must have 1 or 2 args */
	opt_complementary = "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
	opt = getopt32(argv, "cefXx");
	argv += optind;
	//argc -= optind;

	extract_dir = argv[1];
	if (opt & DPKG_DEB_OPT_CONTENTS) { // -c
		tar_archive->action_header = header_verbose_list;
		if (extract_dir)
			bb_show_usage();
	}
	if (opt & DPKG_DEB_OPT_FIELD) { // -f
		/* Print the entire control file */
//TODO: standard tool accepts an optional list of fields to print
		ar_archive->accept = control_tar_llist;
		llist_add_to(&(tar_archive->accept), (char*)"./control");
		tar_archive->filter = filter_accept_list;
		tar_archive->action_data = data_extract_to_stdout;
		if (extract_dir)
			bb_show_usage();
	}
	if (opt & DPKG_DEB_OPT_CONTROL) { // -e
		ar_archive->accept = control_tar_llist;
		tar_archive->action_data = data_extract_all;
		if (!extract_dir)
			extract_dir = "./DEBIAN";
	}
	if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) { // -Xx
		if (opt & DPKG_DEB_OPT_EXTRACT_VERBOSE)
			tar_archive->action_header = header_list;
		tar_archive->action_data = data_extract_all;
		if (!extract_dir)
			bb_show_usage();
	}

	/* Standard tool supports "-" */
	tar_archive->src_fd = ar_archive->src_fd = xopen_stdin(argv[0]);

	if (extract_dir) {
		mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
		xchdir(extract_dir);
	}

	/* Do it */
	unpack_ar_archive(ar_archive);

	/* Cleanup */
	if (ENABLE_FEATURE_CLEAN_UP)
		close(ar_archive->src_fd);

	return EXIT_SUCCESS;
}
Esempio n. 16
0
int
main(int argc, char **argv)
{
    char *	base		= uninit;
    char *	bp		= uninit;
    char *	cp		= uninit;
    char *	cwd		= 0;
    char *	group		= 0;
    char *	linkname	= 0;
    char *	linkprefix	= 0;
    char *	name		= uninit;
    char *	owner		= 0;
    char *	todir		= uninit;
    char *	toname		= uninit;

    int 	bnlen		= -1;
    int 	cc		= 0;
    int 	dodir		= 0;
    int 	dolink		= 0;
    int 	dorelsymlink	= 0;
    int 	dotimes		= 0;
    int 	exists		= 0;
    int 	fromfd		= -1;
    int 	len		= -1;
    int 	lplen		= 0;
    int		onlydir		= 0;
    int 	opt		= -1;
    int 	tdlen		= -1;
    int 	tofd		= -1;
    int 	wc		= -1;

    mode_t 	mode		= 0755;

    uid_t 	uid		= -1;
    gid_t 	gid		= -1;

    struct stat sb;
    struct stat tosb;
    struct utimbuf utb;
    char 	buf[BUFSIZ];

    program = strrchr(argv[0], '/');
    if (!program)
	program = strrchr(argv[0], '\\');
    program = program ? program+1 : argv[0];


    while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
	switch (opt) {
	  case 'C': cwd = optarg;	break;
	  case 'D': onlydir = 1; 	break;
	  case 'd': dodir = 1; 		break;
	  case 'l': dolink = 1;		break;
	  case 'L':
	    linkprefix = optarg;
	    lplen = strlen(linkprefix);
	    dolink = 1;
	    break;
	  case 'R': dolink = dorelsymlink = 1; break;
	  case 'm':
	    mode = strtoul(optarg, &cp, 8);
	    if (mode == 0 && cp == optarg)
		usage();
	    break;
	  case 'o': owner = optarg; 	break;
	  case 'g': group = optarg; 	break;
	  case 't': dotimes = 1; 	break;
	  default:  usage();
	}
    }

    argc -= optind;
    argv += optind;
    if (argc < 2 - onlydir)
	usage();

    todir = argv[argc-1];
    if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
	mkdirs(todir, 0777) < 0) {
	fail("cannot mkdir -p %s", todir);
    }
    if (onlydir)
	return 0;

    if (!cwd) {
	cwd = GETCWD(0, PATH_MAX);
	if (!cwd)
	    fail("could not get CWD");
    }

    /* make sure we can get into todir. */
    xchdir(todir);
    todir = GETCWD(0, PATH_MAX);
    if (!todir)
	fail("could not get CWD in todir");
    tdlen = strlen(todir);

    /* back to original directory. */
    xchdir(cwd);

    uid = owner ? touid(owner) : -1;
    gid = group ? togid(group) : -1;

    while (--argc > 0) {
	name   = *argv++;
	len    = strlen(name);
	base   = xbasename(name);
	bnlen  = strlen(base);
	toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
	sprintf(toname, "%s/%s", todir, base);
retry:
	exists = (lstat(toname, &tosb) == 0);

	if (dodir) {
	    /* -d means create a directory, always */
	    if (exists && !S_ISDIR(tosb.st_mode)) {
		int rv = unlink(toname);
		if (rv)
		    fail("cannot unlink %s", toname);
		exists = 0;
	    }
	    if (!exists && mkdir(toname, mode) < 0) {
	    	/* we probably have two nsinstall programs in a race here. */
		if (errno == EEXIST && !stat(toname, &sb) && 
		    S_ISDIR(sb.st_mode)) {
		    fprintf(stderr, "directory creation race: %s\n", toname);
		    goto retry;
	    	}
		fail("cannot make directory %s", toname);
	    }
	    if ((owner || group) && chown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
	} else if (dolink) {
	    if (*name == '/') {
		/* source is absolute pathname, link to it directly */
		linkname = 0;
	    } else {
		if (linkprefix) {
		    /* -L implies -l and prefixes names with a $cwd arg. */
		    len += lplen + 1;
		    linkname = (char*)xmalloc(len + 1);
		    sprintf(linkname, "%s/%s", linkprefix, name);
		} else if (dorelsymlink) {
		    /* Symlink the relative path from todir to source name. */
		    linkname = (char*)xmalloc(PATH_MAX);

		    if (*todir == '/') {
			/* todir is absolute: skip over common prefix. */
			lplen = relatepaths(todir, cwd, linkname);
			strcpy(linkname + lplen, name);
		    } else {
			/* todir is named by a relative path: reverse it. */
			reversepath(todir, name, len, linkname);
			xchdir(cwd);
		    }

		    len = strlen(linkname);
		}
		name = linkname;
	    }

	    /* Check for a pre-existing symlink with identical content. */
	    if (exists &&
		(!S_ISLNK(tosb.st_mode) ||
		 readlink(toname, buf, sizeof buf) != len ||
		 strncmp(buf, name, len) != 0)) {
		int rmrv;
		rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
		if (rmrv < 0) {
		    fail("destination exists, cannot remove %s", toname);
		}
		exists = 0;
	    }
	    if (!exists && symlink(name, toname) < 0) {
		if (errno == EEXIST) {
		    fprintf(stderr, "symlink creation race: %s\n", toname);
                    fail("symlink was attempted in working directory %s "
                         "from %s to %s.\n", cwd, name, toname);
		    goto retry;
		}
		diagnosePath(toname);
		fail("cannot make symbolic link %s", toname);
	    }
#ifdef HAVE_LCHOWN
	    if ((owner || group) && lchown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
#endif

	    if (linkname) {
		free(linkname);
		linkname = 0;
	    }
	} else {
	    /* Copy from name to toname, which might be the same file. */
	    fromfd = open(name, O_RDONLY);
	    if (fromfd < 0 || fstat(fromfd, &sb) < 0)
		fail("cannot access %s", name);
	    if (exists && 
	        (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
		int rmrv;
		rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
		if (rmrv < 0) {
		    fail("destination exists, cannot remove %s", toname);
		}
	    }
	    tofd = open(toname, O_CREAT | O_WRONLY, 0666);
	    if (tofd < 0)
		fail("cannot create %s", toname);

	    bp = buf;
	    while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
		while ((wc = write(tofd, bp, cc)) > 0) {
		    if ((cc -= wc) == 0)
			break;
		    bp += wc;
		}
		if (wc < 0)
		    fail("cannot write to %s", toname);
	    }
	    if (cc < 0)
		fail("cannot read from %s", name);

	    if (ftruncate(tofd, sb.st_size) < 0)
		fail("cannot truncate %s", toname);
	    /*
	    ** On OpenVMS we can't chmod() until the file is closed, and we
	    ** have to utime() last since fchown/chmod alter the timestamps.
	    */
#ifndef VMS
	    if (dotimes) {
		utb.actime = sb.st_atime;
		utb.modtime = sb.st_mtime;
		if (utime(toname, &utb) < 0)
		    fail("cannot set times of %s", toname);
	    }
#ifdef HAVE_FCHMOD
	    if (fchmod(tofd, mode) < 0)
#else
	    if (chmod(toname, mode) < 0)
#endif
		fail("cannot change mode of %s", toname);
#endif
	    if ((owner || group) && fchown(tofd, uid, gid) < 0)
		fail("cannot change owner of %s", toname);

	    /* Must check for delayed (NFS) write errors on close. */
	    if (close(tofd) < 0)
		fail("close reports write error on %s", toname);
	    close(fromfd);
#ifdef VMS
	    if (chmod(toname, mode) < 0)
		fail("cannot change mode of %s", toname);
	    if (dotimes) {
		utb.actime = sb.st_atime;
		utb.modtime = sb.st_mtime;
		if (utime(toname, &utb) < 0)
		    fail("cannot set times of %s", toname);
	    }
#endif
	}

	free(toname);
    }

    free(cwd);
    free(todir);
    return 0;
}
Esempio n. 17
0
int popmaildir_main(int argc UNUSED_PARAM, char **argv)
{
	char *buf;
	unsigned nmsg;
	char *hostname;
	pid_t pid;
	const char *retr;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
	const char *delivery;
#endif
	unsigned opt_nlines = 0;

	enum {
		OPT_b = 1 << 0,		// -b binary mode. Ignored
		OPT_d = 1 << 1,		// -d,-dd,-ddd debug. Ignored
		OPT_m = 1 << 2,		// -m show ugsed memory. Ignored
		OPT_V = 1 << 3,		// -V version. Ignored
		OPT_c = 1 << 4,		// -c use tcpclient. Ignored
		OPT_a = 1 << 5,		// -a use APOP protocol
		OPT_s = 1 << 6,		// -s skip authorization
		OPT_T = 1 << 7,		// -T get messages with TOP instead with RETR
		OPT_k = 1 << 8,		// -k keep retrieved messages on the server
		OPT_t = 1 << 9,		// -t90 set timeout to 90 sec
		OPT_R = 1 << 10,	// -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored
		OPT_Z = 1 << 11,	// -Z11-23 remove messages from 11 to 23 (dangerous). Ignored
		OPT_L = 1 << 12,	// -L50000 not retrieve new messages >= 50000 bytes. Ignored
		OPT_H = 1 << 13,	// -H30 type first 30 lines of a message; (-L12000 -H30). Ignored
		OPT_M = 1 << 14,	// -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F
		OPT_F = 1 << 15,	// -F\"program arg1 arg2 ...\"; filter by program. Treated like -M
	};

	// init global variables
	INIT_G();

	// parse options
	opt_complementary = "-1:dd:t+:R+:L+:H+";
	opts = getopt32(argv,
		"bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
		&timeout, NULL, NULL, NULL, &opt_nlines
		IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
	);
	//argc -= optind;
	argv += optind;

	// get auth info
	if (!(opts & OPT_s))
		get_cred_or_die(STDIN_FILENO);

	// goto maildir
	xchdir(*argv++);

	// launch connect helper, if any
	if (*argv)
		launch_helper((const char **)argv);

	// get server greeting
	pop3_checkr(NULL, NULL, &buf);

	// authenticate (if no -s given)
	if (!(opts & OPT_s)) {
		// server supports APOP and we want it?
		if ('<' == buf[0] && (opts & OPT_a)) {
			union { // save a bit of stack
				md5_ctx_t ctx;
				char hex[16 * 2 + 1];
			} md5;
			uint32_t res[16 / 4];

			char *s = strchr(buf, '>');
			if (s)
				s[1] = '\0';
			// get md5 sum of "<stamp>password" string
			md5_begin(&md5.ctx);
			md5_hash(&md5.ctx, buf, strlen(buf));
			md5_hash(&md5.ctx, G.pass, strlen(G.pass));
			md5_end(&md5.ctx, res);
			*bin2hex(md5.hex, (char*)res, 16) = '\0';
			// APOP
			s = xasprintf("%s %s", G.user, md5.hex);
			pop3_check("APOP %s", s);
			free(s);
			free(buf);
		// server ignores APOP -> use simple text authentication
		} else {
			// USER
			pop3_check("USER %s", G.user);
			// PASS
			pop3_check("PASS %s", G.pass);
		}
	}

	// get mailbox statistics
	pop3_checkr("STAT", NULL, &buf);

	// prepare message filename suffix
	hostname = safe_gethostname();
	pid = getpid();

	// get messages counter
	// NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
	// we only need nmsg and atoi is just exactly what we need
	// if atoi fails to convert buf into number it returns 0
	// in this case the following loop simply will not be executed
	nmsg = atoi(buf);
	free(buf);

	// loop through messages
	retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u";
	for (; nmsg; nmsg--) {

		char *filename;
		char *target;
		char *answer;
		FILE *fp;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		int rc;
#endif
		// generate unique filename
		filename  = xasprintf("tmp/%llu.%u.%s",
			monotonic_us(), (unsigned)pid, hostname);

		// retrieve message in ./tmp/ unless filter is specified
		pop3_check(retr, (const char *)(ptrdiff_t)nmsg);

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		// delivery helper ordered? -> setup pipe
		if (opts & (OPT_F|OPT_M)) {
			// helper will have $FILENAME set to filename
			xsetenv("FILENAME", filename);
			fp = popen(delivery, "w");
			unsetenv("FILENAME");
			if (!fp) {
				bb_perror_msg("delivery helper");
				break;
			}
		} else
#endif
		// create and open file filename
		fp = xfopen_for_write(filename);

		// copy stdin to fp (either filename or delivery helper)
		while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
			char *s = answer;
			if ('.' == answer[0]) {
				if ('.' == answer[1])
					s++;
				else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
					break;
			}
			//*strchrnul(s, '\r') = '\n';
			fputs(s, fp);
			free(answer);
		}

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		// analyse delivery status
		if (opts & (OPT_F|OPT_M)) {
			rc = pclose(fp);
			if (99 == rc) // 99 means bail out
				break;
//			if (rc) // !0 means skip to the next message
				goto skip;
//			// 0 means continue
		} else {
			// close filename
			fclose(fp);
		}
#endif

		// delete message from server
		if (!(opts & OPT_k))
			pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);

		// atomically move message to ./new/
		target = xstrdup(filename);
		strncpy(target, "new", 3);
		// ... or just stop receiving on failure
		if (rename_or_warn(filename, target))
			break;
		free(target);

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 skip:
#endif
		free(filename);
	}

	// Bye
	pop3_check("QUIT", NULL);

	if (ENABLE_FEATURE_CLEAN_UP) {
		free(G.user);
		free(G.pass);
	}

	return EXIT_SUCCESS;
}
Esempio n. 18
0
int makedevs_main(int argc UNUSED_PARAM, char **argv)
{
	parser_t *parser;
	char *line = (char *)"-";
	int ret = EXIT_SUCCESS;

	opt_complementary = "=1"; /* exactly one param */
	getopt32(argv, "d:", &line);
	argv += optind;

	xchdir(*argv); /* ensure root dir exists */

	umask(0);

	printf("rootdir=%s\ntable=", *argv);
	if (NOT_LONE_DASH(line)) {
		printf("'%s'\n", line);
	} else {
		puts("<stdin>");
	}

	parser = config_open(line);
	while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
		int linenum;
		char type;
		unsigned mode = 0755;
		unsigned major = 0;
		unsigned minor = 0;
		unsigned count = 0;
		unsigned increment = 0;
		unsigned start = 0;
		char name[41];
		char user[41];
		char group[41];
		char *full_name = name;
		uid_t uid;
		gid_t gid;

		linenum = parser->lineno;

		if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
					name, &type, &mode, user, group,
					&major, &minor, &start, &increment, &count))
		 || ((unsigned)(major | minor | start | count | increment) > 255)
		) {
			bb_error_msg("invalid line %d: '%s'", linenum, line);
			ret = EXIT_FAILURE;
			continue;
		}

		gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
		uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
		/* We are already in the right root dir,
		 * so make absolute paths relative */
		if ('/' == *full_name)
			full_name++;

		if (type == 'd') {
			bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
			if (chown(full_name, uid, gid) == -1) {
 chown_fail:
				bb_perror_msg("line %d: can't chown %s", linenum, full_name);
				ret = EXIT_FAILURE;
				continue;
			}
			if (chmod(full_name, mode) < 0) {
 chmod_fail:
				bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
				ret = EXIT_FAILURE;
				continue;
			}
		} else if (type == 'f') {
			struct stat st;
			if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
				bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
				ret = EXIT_FAILURE;
				continue;
			}
			if (chown(full_name, uid, gid) < 0)
				goto chown_fail;
			if (chmod(full_name, mode) < 0)
				goto chmod_fail;
		} else {
			dev_t rdev;
			unsigned i;
			char *full_name_inc;

			if (type == 'p') {
				mode |= S_IFIFO;
			} else if (type == 'c') {
				mode |= S_IFCHR;
			} else if (type == 'b') {
				mode |= S_IFBLK;
			} else {
				bb_error_msg("line %d: unsupported file type %c", linenum, type);
				ret = EXIT_FAILURE;
				continue;
			}

			full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2);
			if (count)
				count--;
			for (i = start; i <= start + count; i++) {
				sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
				rdev = makedev(major, minor + (i - start) * increment);
				if (mknod(full_name_inc, mode, rdev) != 0
				 && errno != EEXIST
				) {
					bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
					ret = EXIT_FAILURE;
				} else if (chown(full_name_inc, uid, gid) < 0) {
					bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc);
					ret = EXIT_FAILURE;
				} else if (chmod(full_name_inc, mode) < 0) {
					bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc);
					ret = EXIT_FAILURE;
				}
			}
			free(full_name_inc);
		}
	}
	if (ENABLE_FEATURE_CLEAN_UP)
		config_close(parser);

	return ret;
}
Esempio n. 19
0
int qpkg_main(int argc, char **argv)
{
	q_vdb_ctx *ctx;
	q_vdb_cat_ctx *cat_ctx;
	q_vdb_pkg_ctx *pkg_ctx;
	size_t s, pkgs_made;
	int i;
	struct stat st;
	char buf[BUFSIZE];
	const char *bindir;
	depend_atom *atom;
	int restrict_chmod = 0;
	int qclean = 0;

	while ((i = GETOPT_LONG(QPKG, qpkg, "")) != -1) {
		switch (i) {
		case 'E': eclean = qclean = 1; break;
		case 'c': qclean = 1; break;
		case 'p': pretend = 1; break;
		case 'P':
			restrict_chmod = 1;
			free(qpkg_bindir);
			qpkg_bindir = xstrdup(optarg);
			if (access(qpkg_bindir, W_OK) != 0)
				errp("%s", qpkg_bindir);
			break;
		COMMON_GETOPTS_CASES(qpkg)
		}
	}
	if (qclean)
		return qpkg_clean(qpkg_bindir == NULL ? pkgdir : qpkg_bindir);

	if (argc == optind)
		qpkg_usage(EXIT_FAILURE);

	/* setup temp dirs */
	i = 0;
	bindir = qpkg_get_bindir();
	if (*bindir != '/')
		err("'%s' is not a valid package destination", bindir);
retry_mkdir:
	if (mkdir(bindir, 0750) == -1) {
		lstat(bindir, &st);
		if (!S_ISDIR(st.st_mode)) {
			unlink(bindir);
			if (!i++) goto retry_mkdir;
			errp("could not create temp bindir '%s'", bindir);
		}
		if (!restrict_chmod)
			if (chmod(bindir, 0750))
				errp("could not chmod(0750) temp bindir '%s'", bindir);
	}

	/* we have to change to the root so that we can feed the full paths
	 * to tar when we create the binary package. */
	xchdir(portroot);

	/* first process any arguments which point to /var/db/pkg */
	pkgs_made = 0;
	s = strlen(portvdb);
	for (i = optind; i < argc; ++i) {
		size_t asize = strlen(argv[i]);
		if (asize == 0) {
			argv[i] = NULL;
			continue;
		}
		if (argv[i][asize-1] == '/')
			argv[i][asize-1] = '\0';
		if (!strncmp(portvdb, argv[i], s))
			memmove(argv[i], argv[i]+s+1, asize-s);
		else if (argv[i][0] == '/' && !strncmp(portvdb, argv[i]+1, s))
			memmove(argv[i], argv[i]+s+2, asize-s-1);
		else
			continue;

		atom = atom_explode(argv[i]);
		if (atom) {
			if (!qpkg_make(atom)) ++pkgs_made;
			atom_implode(atom);
		} else
			warn("could not explode '%s'", argv[i]);
		argv[i] = NULL;
	}

	/* now try to run through vdb and locate matches for user inputs */
	ctx = q_vdb_open(portroot, portvdb);
	if (!ctx)
		return EXIT_FAILURE;

	/* scan all the categories */
	while ((cat_ctx = q_vdb_next_cat(ctx))) {
		/* scan all the packages in this category */
		const char *catname = cat_ctx->name;
		while ((pkg_ctx = q_vdb_next_pkg(cat_ctx))) {
			const char *pkgname = pkg_ctx->name;

			/* see if user wants any of these packages */
			snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname);
			atom = atom_explode(buf);
			if (!atom) {
				warn("could not explode '%s'", buf);
				goto next_pkg;
			}
			snprintf(buf, sizeof(buf), "%s/%s", atom->CATEGORY, atom->PN);
			for (i = optind; i < argc; ++i) {
				if (!argv[i]) continue;

				if (!strcmp(argv[i], atom->PN) ||
						!strcmp(argv[i], atom->P) ||
						!strcmp(argv[i], buf) ||
						!strcmp(argv[i], "world"))
					if (!qpkg_make(atom))
						++pkgs_made;
			}
			atom_implode(atom);

 next_pkg:
			q_vdb_close_pkg(pkg_ctx);
		}
	}

	s = (argc - optind) - pkgs_made;
	if (s && !pretend)
		printf(" %s*%s %i package%s could not be matched :/\n",
				RED, NORM, (int)s, (s > 1 ? "s" : ""));
	if (pkgs_made)
		qprintf(" %s*%s Packages can be found in %s\n", GREEN, NORM, bindir);

	return (pkgs_made ? EXIT_SUCCESS : EXIT_FAILURE);
}
Esempio n. 20
0
int
main(int argc, char **argv)
{
    int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
    mode_t mode = 0755;
    char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
    uid_t uid;
    gid_t gid;
    struct stat sb, tosb, fromsb;
    struct utimbuf utb;

    program = argv[0];
    cwd = linkname = linkprefix = owner = group = 0;
    onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;

    while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
	switch (opt) {
	  case 'C':
	    cwd = optarg;
	    break;
	  case 'D':
	    onlydir = 1;
	    break;
	  case 'd':
	    dodir = 1;
	    break;
	  case 'l':
	    dolink = 1;
	    break;
	  case 'L':
	    linkprefix = optarg;
	    lplen = strlen(linkprefix);
	    dolink = 1;
	    break;
     case 'R':
	    dolink = dorelsymlink = 1;
	    break;
	  case 'm':
	    mode = strtoul(optarg, &cp, 8);
	    if (mode == 0 && cp == optarg)
		usage();
	    break;
	  case 'o':
	    owner = optarg;
	    break;
	  case 'g':
	    group = optarg;
	    break;
	  case 't':
	    dotimes = 1;
	    break;
	  default:
	    usage();
	}
    }

    argc -= optind;
    argv += optind;
    if (argc < 2 - onlydir)
	usage();

    todir = argv[argc-1];
    if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
	mkdirs(todir, 0777) < 0) {
	fail("cannot make directory %s", todir);
    }
    if (onlydir)
	return 0;

    if (!cwd) {
#ifndef NEEDS_GETCWD
#ifndef GETCWD_CANT_MALLOC
	cwd = getcwd(0, PATH_MAX);
#else
	cwd = malloc(PATH_MAX + 1);
	cwd = getcwd(cwd, PATH_MAX);
#endif
#else
	cwd = malloc(PATH_MAX + 1);
	cwd = getwd(cwd);
#endif
    }

    xchdir(todir);
#ifndef NEEDS_GETCWD
#ifndef GETCWD_CANT_MALLOC
    todir = getcwd(0, PATH_MAX);
#else
    todir = malloc(PATH_MAX + 1);
    todir = getcwd(todir, PATH_MAX);
#endif
#else
    todir = malloc(PATH_MAX + 1);
    todir = getwd(todir);
#endif
    tdlen = strlen(todir);
    xchdir(cwd);
    tdlen = strlen(todir);

    uid = owner ? touid(owner) : (uid_t)(-1);
    gid = group ? togid(group) : (gid_t)(-1);

    while (--argc > 0) {
	name = *argv++;
	len = strlen(name);
	base = xbasename(name);
	bnlen = strlen(base);
	toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1));
	sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base);
	exists = (lstat(toname, &tosb) == 0);

	if (dodir) {
	    /* -d means create a directory, always */
	    if (exists && !S_ISDIR(tosb.st_mode)) {
		(void) unlink(toname);
		exists = 0;
	    }
	    if (!exists && mkdir(toname, mode) < 0)
		fail("cannot make directory %s", toname);
	    if ((owner || group) && chown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
	} else if (dolink) {
            if (access(name, R_OK) != 0) {
                fail("cannot access %s", name);
            }
	    if (*name == '/') {
		/* source is absolute pathname, link to it directly */
		linkname = 0;
	    } else {
		if (linkprefix) {
		    /* -L implies -l and prefixes names with a $cwd arg. */
		    len += lplen + 1;
		    linkname = xmalloc((unsigned int)(len + 1));
		    sprintf(linkname, "%s/%s", linkprefix, name);
		} else if (dorelsymlink) {
		    /* Symlink the relative path from todir to source name. */
		    linkname = xmalloc(PATH_MAX);

		    if (*todir == '/') {
			/* todir is absolute: skip over common prefix. */
			lplen = relatepaths(todir, cwd, linkname);
			strcpy(linkname + lplen, name);
		    } else {
			/* todir is named by a relative path: reverse it. */
			reversepath(todir, name, len, linkname);
			xchdir(cwd);
		    }

		    len = strlen(linkname);
		}
		name = linkname;
	    }

	    /* Check for a pre-existing symlink with identical content. */
	    if ((exists && (!S_ISLNK(tosb.st_mode) ||
						readlink(toname, buf, sizeof buf) != len ||
						strncmp(buf, name, (unsigned int)len) != 0)) || 
			((stat(name, &fromsb) == 0) && 
			 (fromsb.st_mtime > tosb.st_mtime))) {
		(void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
		exists = 0;
	    }
	    if (!exists && symlink(name, toname) < 0)
		fail("cannot make symbolic link %s", toname);
#ifdef HAVE_LCHOWN
	    if ((owner || group) && lchown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
#endif

	    if (linkname) {
		free(linkname);
		linkname = 0;
	    }
	} else {
	    /* Copy from name to toname, which might be the same file. */
      if( stat(name, &sb) == 0 && S_IFDIR & sb.st_mode )
      {
        /* then is directory: must explicitly create destination dir  */
        /*  and manually copy files over                              */
        copydir( name, todir, mode, group, owner, dotimes, uid, gid );
      } 
      else
      {
        copyfile(name, toname, mode, group, owner, dotimes, uid, gid);
      }
    }

	free(toname);
    }

    free(cwd);
    free(todir);
    return 0;
}
Esempio n. 21
0
int crond_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
	unsigned opt;

	INIT_G();

	/* "-b after -f is ignored", and so on for every pair a-b */
	opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l")
			":l+:d+"; /* -l and -d have numeric param */
	opt = getopt32(argv, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"),
			&LogLevel, &LogFile, &CDir
			USE_DEBUG_CROND_OPTION(,&LogLevel));
	/* both -d N and -l N set the same variable: LogLevel */

	if (!(opt & OPT_f)) {
		/* close stdin, stdout, stderr.
		 * close unused descriptors - don't need them. */
		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
	}

	if (!DebugOpt && LogFile == NULL) {
		/* logging to syslog */
		openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
		logmode = LOGMODE_SYSLOG;
	}

	xchdir(CDir);
	//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
	setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */
	crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
	SynchronizeDir();

	/* main loop - synchronize to 1 second after the minute, minimum sleep
	 * of 1 second. */
	{
		time_t t1 = time(NULL);
		time_t t2;
		long dt;
		int rescan = 60;
		int sleep_time = 60;

		write_pidfile("/var/run/crond.pid");
		for (;;) {
			sleep((sleep_time + 1) - (time(NULL) % sleep_time));

			t2 = time(NULL);
			dt = (long)t2 - (long)t1;

			/*
			 * The file 'cron.update' is checked to determine new cron
			 * jobs.  The directory is rescanned once an hour to deal
			 * with any screwups.
			 *
			 * check for disparity.  Disparities over an hour either way
			 * result in resynchronization.  A reverse-indexed disparity
			 * less then an hour causes us to effectively sleep until we
			 * match the original time (i.e. no re-execution of jobs that
			 * have just been run).  A forward-indexed disparity less then
			 * an hour causes intermediate jobs to be run, but only once
			 * in the worst case.
			 *
			 * when running jobs, the inequality used is greater but not
			 * equal to t1, and less then or equal to t2.
			 */
			if (--rescan == 0) {
				rescan = 60;
				SynchronizeDir();
			}
			CheckUpdates();
			if (DebugOpt)
				crondlog(LVL5 "wakeup dt=%ld", dt);
			if (dt < -60 * 60 || dt > 60 * 60) {
				crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
			} else if (dt > 0) {
				TestJobs(t1, t2);
				RunJobs();
				sleep(5);
				if (CheckJobs() > 0) {
					sleep_time = 10;
				} else {
					sleep_time = 60;
				}
			}
			t1 = t2;
		}
	}
	return 0; /* not reached */
}
Esempio n. 22
0
int qgrep_main(int argc, char **argv)
{
	int i;
	int count = 0;
	char *p;
	char do_count, do_regex, do_eclass, do_installed, do_list;
	char show_filename, skip_comments, invert_list, show_name;
	char per_file_output;
	FILE *fp = NULL;
	DIR *eclass_dir = NULL;
	DIR *vdb_dir = NULL;
	DIR *cat_dir = NULL;
	struct dirent *dentry;
	char ebuild[_Q_PATH_MAX];
	char name[_Q_PATH_MAX];
	char *label;
	int reflags = 0;
	char invert_match = 0;
	regex_t preg, skip_preg;
	char *skip_pattern = NULL;
	depend_atom** include_atoms = NULL;
	unsigned long int context_optarg;
	char num_lines_before = 0;
	char num_lines_after = 0;
	qgrep_buf_t *buf_list;
	int need_separator = 0;
	char status = 1;

	QGREP_STR_FUNC strfunc = strstr;

	do_count = do_regex = do_eclass = do_installed = do_list = 0;
	show_filename = skip_comments = invert_list = show_name = 0;

	while ((i = GETOPT_LONG(QGREP, qgrep, "")) != -1) {
		switch (i) {
		case 'I': invert_match = 1; break;
		case 'i':
			strfunc = strcasestr;
			reflags |= REG_ICASE;
			break;
		case 'c': do_count = 1; break;
		case 'l': do_list = 1; break;
		case 'L': do_list = invert_list = 1; break;
		case 'e': do_regex = 1; break;
		case 'x':
			do_regex = 1;
			reflags |= REG_EXTENDED;
			break;
		case 'J': do_installed = 1; break;
		case 'E': do_eclass = 1; break;
		case 'H': show_filename = 1; break;
		case 'N': show_name = 1; break;
		case 's': skip_comments = 1; break;
		case 'S': skip_pattern = optarg; break;
		case 'B':
		case 'A':
			errno = 0;
			context_optarg = strtol(optarg, &p, 10);
			if (errno != 0)
				errp("%s: not a valid integer", optarg);
			else if (p == optarg || *p != '\0')
				err("%s: not a valid integer", optarg);
			if (context_optarg > 254)
				err("%s: silly value!", optarg);
			if (i == 'B')
				num_lines_before = context_optarg;
			else
				num_lines_after = context_optarg;
			break;
		COMMON_GETOPTS_CASES(qgrep)
		}
	}
	if (argc == optind)
		qgrep_usage(EXIT_FAILURE);

	if (do_list && do_count) {
		warn("%s and --count are incompatible options. The former wins.",
				(invert_list ? "--invert-list" : "--list"));
		do_count = 0;
	}

	if (show_name && show_filename) {
		warn("--with-name and --with-filename are incompatible options. The former wins.");
		show_filename = 0;
	}

	if (do_list && num_lines_before) {
		warn("%s and --before are incompatible options. The former wins.",
				(invert_list ? "--invert-list" : "--list"));
		num_lines_before = 0;
	}

	if (do_list && num_lines_after) {
		warn("%s and --after are incompatible options. The former wins.",
				(invert_list ? "--invert-list" : "--list"));
		num_lines_after = 0;
	}

	if (do_count && num_lines_before) {
		warn("--count and --before are incompatible options. The former wins.");
		num_lines_before = 0;
	}

	if (do_count && num_lines_after) {
		warn("--count and --after are incompatible options. The former wins.");
		num_lines_after = 0;
	}

	if (do_installed && do_eclass) {
		warn("--installed and --eclass are incompatible options. The former wins.");
		do_eclass = 0;
	}

	/* do we report results once per file or per line ? */
	per_file_output = do_count || (do_list && (!verbose || invert_list));
	/* label for prefixing matching lines or listing matching files */
	label = (show_name ? name : ((verbose || show_filename || do_list) ? ebuild : NULL));

	if (argc > (optind + 1)) {
		include_atoms = xcalloc(sizeof(depend_atom*), (argc - optind - 1));
		for (i = (optind + 1); i < argc; i++)
			if ((include_atoms[i - optind - 1] = atom_explode(argv[i])) == NULL)
				warn("%s: invalid atom, will be ignored", argv[i]);
	}

	/* pre-compile regexps once for all */
	if (do_regex) {
		if (invert_match || *RED == '\0')
			reflags |= REG_NOSUB;
		xregcomp(&preg, argv[optind], reflags);
		reflags |= REG_NOSUB;
		if (skip_pattern)
			xregcomp(&skip_preg, skip_pattern, reflags);
	}

	/* allocate a circular buffers list for --before */
	buf_list = qgrep_buf_list_alloc(num_lines_before + 1);

	size_t n;
	char *overlay;
	array_for_each(overlays, n, overlay) {

		/* go look either in ebuilds or eclasses or VDB */
		if (!do_eclass && !do_installed) {
			fp = fopen(initialize_flat(overlay, CACHE_EBUILD, false), "re");
			if (fp == NULL)
				continue;
			xchdir(overlay);
		} else if (do_eclass) {
			xchdir(overlay);
			if ((eclass_dir = opendir("eclass")) == NULL) {
				if (errno != ENOENT)
					warnp("opendir(\"%s/eclass\") failed", overlay);
				continue;
			}
		} else { /* if (do_install) */
			char buf[_Q_PATH_MAX];
			snprintf(buf, sizeof(buf), "%s/%s", portroot, portvdb);
			xchdir(buf);
			if ((vdb_dir = opendir(".")) == NULL)
				errp("could not opendir(%s/%s) for ROOT/VDB", portroot, portvdb);
		}

		/* iteration is either over ebuilds or eclasses */
		while (do_eclass
				? ((dentry = readdir(eclass_dir))
					&& snprintf(ebuild, sizeof(ebuild), "eclass/%s", dentry->d_name))
				: (do_installed
					? (get_next_installed_ebuild(ebuild, vdb_dir, &dentry, &cat_dir) != NULL)
					: (fgets(ebuild, sizeof(ebuild), fp) != NULL))) {
			FILE *newfp;

			/* filter badly named files, prepare eclass or package name, etc. */
			if (do_eclass) {
				if ((p = strrchr(ebuild, '.')) == NULL)
					continue;
				if (strcmp(p, ".eclass"))
					continue;
				if (show_name || (include_atoms != NULL)) {
					/* cut ".eclass" */
					*p = '\0';
					/* and skip "eclass/" */
					snprintf(name, sizeof(name), "%s", ebuild + 7);
					/* restore the filepath */
					*p = '.';
				}
			} else {
				if ((p = strchr(ebuild, '\n')) != NULL)
					*p = '\0';
				if (show_name || (include_atoms != NULL)) {
					/* cut ".ebuild" */
					if (p == NULL)
						p = ebuild + strlen(ebuild);
					*(p-7) = '\0';
					/* cut "/foo/" from "cat/foo/foo-x.y" */
					if ((p = strchr(ebuild, '/')) == NULL)
						continue;
					*(p++) = '\0';
					/* find head of the ebuild basename */
					if ((p = strchr(p, '/')) == NULL)
						continue;
					/* find	start of the pkg name */
					snprintf(name, sizeof(name), "%s/%s", ebuild, (p+1));
					/* restore the filepath */
					*p = '/';
					*(p + strlen(p)) = '.';
					ebuild[strlen(ebuild)] = '/';
				}
			}

			/* filter the files we grep when there are extra args */
			if (include_atoms != NULL)
				if (!qgrep_name_match(name, (argc - optind - 1), include_atoms))
					continue;

			if ((newfp = fopen(ebuild, "r")) != NULL) {
				int lineno = 0;
				char remaining_after_context = 0;
				count = 0;
				/* if there have been some matches already, then a separator will be needed */
				need_separator = (!status) && (num_lines_before || num_lines_after);
				/* whatever is in the circular buffers list is no more a valid context */
				qgrep_buf_list_invalidate(buf_list);

				/* reading a new line always happen in the next buffer of the list */
				while ((buf_list = buf_list->next)
						&& (fgets(buf_list->buf, sizeof(buf_list->buf), newfp)) != NULL) {
					lineno++;
					buf_list->valid = 1;

					/* cleanup EOL */
					if ((p = strrchr(buf_list->buf, '\n')) != NULL)
						*p = 0;
					if ((p = strrchr(buf_list->buf, '\r')) != NULL)
						*p = 0;

					if (skip_comments) {
						/* reject comments line ("^[ \t]*#") */
						p = buf_list->buf;
						while (*p == ' ' || *p == '\t') p++;
						if (*p == '#')
							goto print_after_context;
					}

					if (skip_pattern) {
						/* reject some other lines which match an optional pattern */
						if (!do_regex) {
							if (strfunc(buf_list->buf, skip_pattern) != NULL)
								goto print_after_context;
						} else {
							if (regexec(&skip_preg, buf_list->buf, 0, NULL, 0) == 0)
								goto print_after_context;
						}
					}

					/* four ways to match a line (with/without inversion and regexp) */
					if (!invert_match) {
						if (do_regex == 0) {
							if (strfunc(buf_list->buf, argv[optind]) == NULL)
								goto print_after_context;
						} else {
							if (regexec(&preg, buf_list->buf, 0, NULL, 0) != 0)
								goto print_after_context;
						}
					} else {
						if (do_regex == 0) {
							if (strfunc(buf_list->buf, argv[optind]) != NULL)
								goto print_after_context;
						} else {
							if (regexec(&preg, buf_list->buf, 0, NULL, 0) == 0)
								goto print_after_context;
						}
					}

					count++;
					status = 0; /* got a match, exit status should be 0 */
					if (per_file_output)
						continue; /* matching files are listed out of this loop */

					if ((need_separator > 0)
							&& (num_lines_before || num_lines_after))
						printf("--\n");
					/* "need_separator" is not a flag, but a counter, so that
					 * adjacent contextes are not separated */
					need_separator = 0 - num_lines_before;
					if (!do_list) {
						/* print the leading context */
						qgrep_print_before_context(buf_list, num_lines_before, label,
								((verbose > 1) ? lineno : -1));
						/* print matching line */
						if (invert_match || *RED == '\0')
							qgrep_print_matching_line_nocolor(buf_list, label,
								((verbose > 1) ? lineno : -1));
						else if (do_regex)
							qgrep_print_matching_line_regcolor(buf_list, label,
								((verbose > 1) ? lineno : -1), &preg);
						else
							qgrep_print_matching_line_strcolor(buf_list, label,
								((verbose > 1) ? lineno : -1), strfunc, argv[optind]);
					} else {
						/* in verbose do_list mode, list the file once per match */
						printf("%s", label);
						if (verbose > 1)
							printf(":%d", lineno);
						putchar('\n');
					}
					/* init count down of trailing context lines */
					remaining_after_context = num_lines_after;
					continue;

 print_after_context:
					/* print some trailing context lines when needed */
					if (!remaining_after_context) {
						if (!status)
							/* we're getting closer to the need of a separator between
							 * current match block and the next one */
							++need_separator;
					} else {
						qgrep_print_context_line(buf_list, label,
								((verbose > 1) ? lineno : -1));
						--remaining_after_context;
					}
				}
				fclose(newfp);
				if (!per_file_output)
					continue; /* matches were already displayed, line per line */
				if (do_count && count) {
					if (label != NULL)
						/* -c without -v/-N/-H only outputs
						 * the matches count of the file */
						printf("%s:", label);
					printf("%d\n", count);
				} else if ((count && !invert_list) || (!count && invert_list))
					printf("%s\n", label); /* do_list == 1, or we wouldn't be here */
			}
		}
		if (do_eclass)
			closedir(eclass_dir);
		else if (!do_installed)
			fclose(fp);

		if (do_installed)
			break;
	}

	if (do_regex)
		regfree(&preg);
	if (do_regex && skip_pattern)
		regfree(&skip_preg);
	if (include_atoms != NULL) {
		for (i = 0; i < (argc - optind - 1); i++)
			if (include_atoms[i] != NULL)
				atom_implode(include_atoms[i]);
		free(include_atoms);
	}
	qgrep_buf_list_free(buf_list);

	return status;
}
Esempio n. 23
0
/* Open directory 'BDMV', change directory to it.
 * Then open directory 'STREAM', change directory to it.
 * Get the filename has the biggest size in it.
 */
int bluray(const char *dir_name)
{
	DIR *dir;
	struct stat buf;
	struct dirent *dirp;
	char *rval = NULL;
	pid_t pid;

	if (!dir_name || !*dir_name)
		return -1;

	if (is_absolute_path(dir_name)) {
		xchdir(dir_name);
		dir = opendir(BLURAY_PATH);
		if (dir == NULL) {
			fprintf(stderr, "\nopen directory '%s' failure: %s\n",
				BLURAY_PATH, strerror(errno));
			xchdir(dir_name);
			xchdir("..");
			return -1;
		}
		xchdir(BLURAY_PATH);

		while ((dirp = readdir(dir)) != NULL) {
			xstat(dirp->d_name, &buf);
			if ((buf.st_size / 1024 / 1024) > FILE_SIZE) {
				size_t len = strlen(dirp->d_name) + 1;
				rval = xmalloc(len);
				strncpy(rval, dirp->d_name, len);
			}
		}
	} else {
		dir = opendir(BLURAY_PATH);
		if (dir == NULL) {
			fprintf(stderr, "\nopen directory '%s' failure: %s\n",
				BLURAY_PATH, strerror(errno));
			return -1;
		}
		xchdir(BLURAY_PATH);

		while ((dirp = readdir(dir)) != NULL) {
			xstat(dirp->d_name, &buf);
			if ((buf.st_size / 1024 / 1024) > FILE_SIZE) {
				size_t len = strlen(dirp->d_name) + 1;
				rval = xmalloc(len);
				strncpy(rval, dirp->d_name, len);
			}
		}
	}

	pid = xfork();
	if (pid == 0) {
		execlp("mplayer", "mplayer", rval, (char *)0);
		perror("execlp error");
		exit(EXIT_FAILURE);
	}

	xchdir(dir_name);
	xchdir("..");
	return 0;
}
Esempio n. 24
0
int switch_root_main(int argc UNUSED_PARAM, char **argv)
{
	char *newroot, *console = NULL;
	struct stat st;
	struct statfs stfs;
	dev_t rootdev;

	// Parse args (-c console)
	opt_complementary = "-2"; // minimum 2 params
	getopt32(argv, "+c:", &console); // '+': stop at first non-option
	argv += optind;
	newroot = *argv++;

	// Change to new root directory and verify it's a different fs
	xchdir(newroot);
	xstat("/", &st);
	rootdev = st.st_dev;
	xstat(".", &st);
	if (st.st_dev == rootdev || getpid() != 1) {
		// Show usage, it says new root must be a mountpoint
		// and we must be PID 1
		bb_show_usage();
	}

	// Additional sanity checks: we're about to rm -rf /, so be REALLY SURE
	// we mean it. I could make this a CONFIG option, but I would get email
	// from all the people who WILL destroy their filesystems.
	if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) {
		bb_error_msg_and_die("/init is not a regular file");
	}
	statfs("/", &stfs); // this never fails
	if ((unsigned)stfs.f_type != RAMFS_MAGIC
	 && (unsigned)stfs.f_type != TMPFS_MAGIC
	) {
		bb_error_msg_and_die("root filesystem is not ramfs/tmpfs");
	}

	// Zap everything out of rootdev
	delete_contents("/", rootdev);

	// Overmount / with newdir and chroot into it
	if (mount(".", "/", NULL, MS_MOVE, NULL)) {
		// For example, fails when newroot is not a mountpoint
		bb_perror_msg_and_die("error moving root");
	}
	xchroot(".");
	// The chdir is needed to recalculate "." and ".." links
	/*xchdir("/"); - done in xchroot */

	// If a new console specified, redirect stdin/stdout/stderr to it
	if (console) {
		close(0);
		xopen(console, O_RDWR);
		xdup2(0, 1);
		xdup2(0, 2);
	}

	// Exec real init
	execv(argv[0], argv);
	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
}
int
main(int argc, char **argv)
{
    int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
    mode_t mode = 0755;
    char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
    uid_t uid;
    gid_t gid;
    struct stat sb, tosb;
    struct utimbuf utb;

    program = argv[0];
    cwd = linkname = linkprefix = owner = group = 0;
    onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;

    while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
	switch (opt) {
	  case 'C':
	    cwd = optarg;
	    break;
	  case 'D':
	    onlydir = 1;
	    break;
	  case 'd':
	    dodir = 1;
	    break;
	  case 'l':
	    dolink = 1;
	    break;
	  case 'L':
	    linkprefix = optarg;
	    lplen = strlen(linkprefix);
	    dolink = 1;
	    break;
	  case 'R':
	    dolink = dorelsymlink = 1;
	    break;
	  case 'm':
	    mode = strtoul(optarg, &cp, 8);
	    if (mode == 0 && cp == optarg)
		usage();
	    break;
	  case 'o':
	    owner = optarg;
	    break;
	  case 'g':
	    group = optarg;
	    break;
	  case 't':
	    dotimes = 1;
	    break;
	  default:
	    usage();
	}
    }

    argc -= optind;
    argv += optind;
    if (argc < 2 - onlydir)
	usage();

    todir = argv[argc-1];
    if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
	mkdirs(todir, 0777) < 0) {
	fail("cannot make directory %s", todir);
    }
    if (onlydir)
	return 0;

    if (!cwd) {
#ifdef GETCWD_CAN_MALLOC
	cwd = getcwd(0, PATH_MAX);
#else
	cwd = malloc(PATH_MAX + 1);
	cwd = getcwd(cwd, PATH_MAX);
#endif
    }
    xchdir(todir);
#ifdef GETCWD_CAN_MALLOC
    todir = getcwd(0, PATH_MAX);
#else
    todir = malloc(PATH_MAX + 1);
    todir = getcwd(todir, PATH_MAX);
#endif
    tdlen = strlen(todir);
    xchdir(cwd);
    tdlen = strlen(todir);

    uid = owner ? touid(owner) : -1;
    gid = group ? togid(group) : -1;

    while (--argc > 0) {
	name = *argv++;
	len = strlen(name);
	base = xbasename(name);
	bnlen = strlen(base);
	toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
	sprintf(toname, "%s/%s", todir, base);
	exists = (lstat(toname, &tosb) == 0);

	if (dodir) {
	    /* -d means create a directory, always */
	    if (exists && !S_ISDIR(tosb.st_mode)) {
		(void) unlink(toname);
		exists = 0;
	    }
	    if (!exists && mkdir(toname, mode) < 0)
		fail("cannot make directory %s", toname);
	    if ((owner || group) && chown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
	} else if (dolink) {
	    if (*name == '/') {
		/* source is absolute pathname, link to it directly */
		linkname = 0;
	    } else {
		if (linkprefix) {
		    /* -L implies -l and prefixes names with a $cwd arg. */
		    len += lplen + 1;
		    linkname = (char*)xmalloc(len + 1);
		    sprintf(linkname, "%s/%s", linkprefix, name);
		} else if (dorelsymlink) {
		    /* Symlink the relative path from todir to source name. */
		    linkname = (char*)xmalloc(PATH_MAX);

		    if (*todir == '/') {
			/* todir is absolute: skip over common prefix. */
			lplen = relatepaths(todir, cwd, linkname);
			strcpy(linkname + lplen, name);
		    } else {
			/* todir is named by a relative path: reverse it. */
			reversepath(todir, name, len, linkname);
			xchdir(cwd);
		    }

		    len = strlen(linkname);
		}
		name = linkname;
	    }

	    /* Check for a pre-existing symlink with identical content. */
	    if (exists &&
		(!S_ISLNK(tosb.st_mode) ||
		 readlink(toname, buf, sizeof buf) != len ||
		 strncmp(buf, name, len) != 0)) {
		(void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
		exists = 0;
	    }
	    if (!exists && symlink(name, toname) < 0)
		fail("cannot make symbolic link %s", toname);
#ifdef HAVE_LCHOWN
	    if ((owner || group) && lchown(toname, uid, gid) < 0)
		fail("cannot change owner of %s", toname);
#endif

	    if (linkname) {
		free(linkname);
		linkname = 0;
	    }
	} else {
	    /* Copy from name to toname, which might be the same file. */
	    fromfd = open(name, O_RDONLY);
	    if (fromfd < 0 || fstat(fromfd, &sb) < 0)
		fail("cannot access %s", name);
	    if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0))
		(void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
	    tofd = open(toname, O_CREAT | O_WRONLY, 0666);
	    if (tofd < 0)
		fail("cannot create %s", toname);

	    bp = buf;
	    while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
		while ((wc = write(tofd, bp, cc)) > 0) {
		    if ((cc -= wc) == 0)
			break;
		    bp += wc;
		}
		if (wc < 0)
		    fail("cannot write to %s", toname);
	    }
	    if (cc < 0)
		fail("cannot read from %s", name);

	    if (ftruncate(tofd, sb.st_size) < 0)
		fail("cannot truncate %s", toname);
	    if (dotimes) {
		utb.actime = sb.st_atime;
		utb.modtime = sb.st_mtime;
		if (utime(toname, &utb) < 0)
		    fail("cannot set times of %s", toname);
	    }
#ifdef HAVE_FCHMOD
	    if (fchmod(tofd, mode) < 0)
#else
	    if (chmod(toname, mode) < 0)
#endif
		fail("cannot change mode of %s", toname);
	    if ((owner || group) && fchown(tofd, uid, gid) < 0)
		fail("cannot change owner of %s", toname);

	    /* Must check for delayed (NFS) write errors on close. */
	    if (close(tofd) < 0)
		fail("cannot write to %s", toname);
	    close(fromfd);
	}

	free(toname);
    }

    free(cwd);
    free(todir);
    return 0;
}
Esempio n. 26
0
static void startservice(struct svdir *s)
{
	int p;
	const char *arg[4];
	char exitcode[sizeof(int)*3 + 2];

	if (s->state == S_FINISH) {
/* Two arguments are given to ./finish. The first one is ./run exit code,
 * or -1 if ./run didnt exit normally. The second one is
 * the least significant byte of the exit status as determined by waitpid;
 * for instance it is 0 if ./run exited normally, and the signal number
 * if ./run was terminated by a signal. If runsv cannot start ./run
 * for some reason, the exit code is 111 and the status is 0.
 */
		arg[0] = "./finish";
		arg[1] = "-1";
		if (WIFEXITED(s->wstat)) {
			*utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
			arg[1] = exitcode;
		}
		//arg[2] = "0";
		//if (WIFSIGNALED(s->wstat)) {
			arg[2] = utoa(WTERMSIG(s->wstat));
		//}
		arg[3] = NULL;
	} else {
		arg[0] = "./run";
		arg[1] = NULL;
		custom(s, 'u');
	}

	if (s->pid != 0)
		stopservice(s); /* should never happen */
	while ((p = vfork()) == -1) {
		warn_cannot("vfork, sleeping");
		sleep(5);
	}
	if (p == 0) {
		/* child */
		if (haslog) {
			/* NB: bug alert! right order is close, then dup2 */
			if (s->islog) {
				xchdir("./log");
				close(logpipe.wr);
				xdup2(logpipe.rd, 0);
			} else {
				close(logpipe.rd);
				xdup2(logpipe.wr, 1);
			}
		}
		/* Non-ignored signals revert to SIG_DFL on exec anyway */
		/*bb_signals(0
			+ (1 << SIGCHLD)
			+ (1 << SIGTERM)
			, SIG_DFL);*/
		sig_unblock(SIGCHLD);
		sig_unblock(SIGTERM);
		execv(arg[0], (char**) arg);
		fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
	}
	/* parent */
	if (s->state != S_FINISH) {
		gettimeofday_ns(&s->start);
		s->state = S_RUN;
	}
	s->pid = p;
	pidchanged = 1;
	s->ctrl = C_NOOP;
	update_status(s);
}
Esempio n. 27
0
/* crontab
 */
main(int argc, char **argv)
{
    int i;
    int opt;
    int edit=0,remove=0,list=0;
    char *user = 0;
    char whoami[20];
    char *what = "update";
    struct passwd *pwd;

    opterr = 1;

#if HAVE_BASENAME
    pgm = basename(argv[0]);
#else
    if (pgm = strrchr(argv[0], '/') )
	++pgm;
    else
	pgm = argv[0];
#endif

    if ( (pwd = getpwuid(getuid())) == 0 )
	fatal("you don't seem to have a password entry?\n");
    strncpy(whoami, pwd->pw_name, sizeof whoami);

    while ( (opt=getopt(argc,argv, "elru:")) != EOF ) {
	switch (opt) {
	case 'e': edit = 1; what = "edit"; break;
	case 'l': list = 1; what = "list"; break;
	case 'r': remove = 1; what = "remove"; break;
	case 'u': user = optarg; break;
	default:  usage();
	}
    }
    if ( edit + list + remove > 1 )
	usage();

    if (user) {
	if ( (pwd = getpwnam(user)) == 0 )
	    fatal("user %s does not have a password entry.\n", user);
    }
    else if ( (pwd = getpwuid(getuid())) == 0 )
	fatal("you don't seem to have a password entry?\n");

    if ( (getuid() != 0) && (pwd->pw_uid != getuid()) )
	fatal("you may not %s %s's crontab.\n", what, pwd->pw_name);

    argc -= optind;
    argv += optind;

    interactive=0;
    info((strcmp(whoami,pwd->pw_name) == 0) ? "(%s) %s" : "(%s) %s (%s)",
				whoami, what, pwd->pw_name);
    if (list) {
	if ( !cat(pwd->pw_name) )
	    fatal("no crontab for %s", pwd->pw_name);
    }
    else if (remove) {
	superpowers();
	xchdir(CRONDIR);
	if (unlink(pwd->pw_name) == -1)
	    fatal("%s: %s", pwd->pw_name, strerror(errno));
    }
    else if (edit)
	visual(pwd);
    else if ( !newcrontab( argc ? argv[0] : "-", pwd->pw_name) )
	exit(1);
    exit(0);
}
Esempio n. 28
0
int runsv_main(int argc UNUSED_PARAM, char **argv)
{
	struct stat s;
	int fd;
	int r;
	char buf[256];

	INIT_G();

	dir = single_argv(argv);

	xpiped_pair(selfpipe);
	close_on_exec_on(selfpipe.rd);
	close_on_exec_on(selfpipe.wr);
	ndelay_on(selfpipe.rd);
	ndelay_on(selfpipe.wr);

	sig_block(SIGCHLD);
	bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
	sig_block(SIGTERM);
	bb_signals_recursive_norestart(1 << SIGTERM, s_term);

	xchdir(dir);
	/* bss: svd[0].pid = 0; */
	if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
	if (C_NOOP) svd[0].ctrl = C_NOOP;
	if (W_UP) svd[0].sd_want = W_UP;
	/* bss: svd[0].islog = 0; */
	/* bss: svd[1].pid = 0; */
	gettimeofday_ns(&svd[0].start);
	if (stat("down", &s) != -1)
		svd[0].sd_want = W_DOWN;

	if (stat("log", &s) == -1) {
		if (errno != ENOENT)
			warn_cannot("stat ./log");
	} else {
		if (!S_ISDIR(s.st_mode)) {
			errno = 0;
			warn_cannot("stat log/down: log is not a directory");
		} else {
			haslog = 1;
			svd[1].state = S_DOWN;
			svd[1].ctrl = C_NOOP;
			svd[1].sd_want = W_UP;
			svd[1].islog = 1;
			gettimeofday_ns(&svd[1].start);
			if (stat("log/down", &s) != -1)
				svd[1].sd_want = W_DOWN;
			xpiped_pair(logpipe);
			close_on_exec_on(logpipe.rd);
			close_on_exec_on(logpipe.wr);
		}
	}

	if (mkdir("supervise", 0700) == -1) {
		r = readlink("supervise", buf, sizeof(buf));
		if (r != -1) {
			if (r == sizeof(buf))
				fatal2x_cannot("readlink ./supervise", ": name too long");
			buf[r] = 0;
			mkdir(buf, 0700);
		} else {
			if ((errno != ENOENT) && (errno != EINVAL))
				fatal_cannot("readlink ./supervise");
		}
	}
	svd[0].fdlock = xopen3("log/supervise/lock"+4,
			O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
	if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
		fatal_cannot("lock supervise/lock");
	close_on_exec_on(svd[0].fdlock);
	if (haslog) {
		if (mkdir("log/supervise", 0700) == -1) {
			r = readlink("log/supervise", buf, 256);
			if (r != -1) {
				if (r == 256)
					fatal2x_cannot("readlink ./log/supervise", ": name too long");
				buf[r] = 0;
				fd = xopen(".", O_RDONLY|O_NDELAY);
				xchdir("./log");
				mkdir(buf, 0700);
				if (fchdir(fd) == -1)
					fatal_cannot("change back to service directory");
				close(fd);
			}
			else {
				if ((errno != ENOENT) && (errno != EINVAL))
					fatal_cannot("readlink ./log/supervise");
			}
		}
		svd[1].fdlock = xopen3("log/supervise/lock",
				O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
		if (flock(svd[1].fdlock, LOCK_EX) == -1)
			fatal_cannot("lock log/supervise/lock");
		close_on_exec_on(svd[1].fdlock);
	}

	mkfifo("log/supervise/control"+4, 0600);
	svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
	close_on_exec_on(svd[0].fdcontrol);
	svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
	close_on_exec_on(svd[0].fdcontrolwrite);
	update_status(&svd[0]);
	if (haslog) {
		mkfifo("log/supervise/control", 0600);
		svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
		close_on_exec_on(svd[1].fdcontrol);
		svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
		close_on_exec_on(svd[1].fdcontrolwrite);
		update_status(&svd[1]);
	}
	mkfifo("log/supervise/ok"+4, 0600);
	fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
	close_on_exec_on(fd);
	if (haslog) {
		mkfifo("log/supervise/ok", 0600);
		fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
		close_on_exec_on(fd);
	}
	for (;;) {
		struct pollfd x[3];
		unsigned deadline;
		char ch;

		if (haslog)
			if (!svd[1].pid && svd[1].sd_want == W_UP)
				startservice(&svd[1]);
		if (!svd[0].pid)
			if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
				startservice(&svd[0]);

		x[0].fd = selfpipe.rd;
		x[0].events = POLLIN;
		x[1].fd = svd[0].fdcontrol;
		x[1].events = POLLIN;
		/* x[2] is used only if haslog == 1 */
		x[2].fd = svd[1].fdcontrol;
		x[2].events = POLLIN;
		sig_unblock(SIGTERM);
		sig_unblock(SIGCHLD);
		poll(x, 2 + haslog, 3600*1000);
		sig_block(SIGTERM);
		sig_block(SIGCHLD);

		while (read(selfpipe.rd, &ch, 1) == 1)
			continue;

		for (;;) {
			pid_t child;
			int wstat;

			child = wait_any_nohang(&wstat);
			if (!child)
				break;
			if ((child == -1) && (errno != EINTR))
				break;
			if (child == svd[0].pid) {
				svd[0].wstat = wstat;
				svd[0].pid = 0;
				pidchanged = 1;
				svd[0].ctrl &= ~C_TERM;
				if (svd[0].state != S_FINISH) {
					fd = open("finish", O_RDONLY|O_NDELAY);
					if (fd != -1) {
						close(fd);
						svd[0].state = S_FINISH;
						update_status(&svd[0]);
						continue;
					}
				}
				svd[0].state = S_DOWN;
				deadline = svd[0].start.tv_sec + 1;
				gettimeofday_ns(&svd[0].start);
				update_status(&svd[0]);
				if (LESS(svd[0].start.tv_sec, deadline))
					sleep(1);
			}
			if (haslog) {
				if (child == svd[1].pid) {
					svd[0].wstat = wstat;
					svd[1].pid = 0;
					pidchanged = 1;
					svd[1].state = S_DOWN;
					svd[1].ctrl &= ~C_TERM;
					deadline = svd[1].start.tv_sec + 1;
					gettimeofday_ns(&svd[1].start);
					update_status(&svd[1]);
					if (LESS(svd[1].start.tv_sec, deadline))
						sleep(1);
				}
			}
		} /* for (;;) */
		if (read(svd[0].fdcontrol, &ch, 1) == 1)
			ctrl(&svd[0], ch);
		if (haslog)
			if (read(svd[1].fdcontrol, &ch, 1) == 1)
				ctrl(&svd[1], ch);

		if (sigterm) {
			ctrl(&svd[0], 'x');
			sigterm = 0;
		}

		if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
			if (svd[1].pid == 0)
				_exit(EXIT_SUCCESS);
			if (svd[1].sd_want != W_EXIT) {
				svd[1].sd_want = W_EXIT;
				/* stopservice(&svd[1]); */
				update_status(&svd[1]);
				close(logpipe.wr);
				close(logpipe.rd);
			}
		}
	} /* for (;;) */
	/* not reached */
	return 0;
}
Esempio n. 29
0
int crond_main(int argc UNUSED_PARAM, char **argv)
{
	time_t t2;
	unsigned rescan;
	unsigned sleep_time;
	unsigned opts;

	INIT_G();

	/* "-b after -f is ignored", and so on for every pair a-b */
	opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
			/* -l and -d have numeric param */
			":l+" IF_FEATURE_CROND_D(":d+");
	opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
			&G.log_level, &G.log_filename, &G.crontab_dir_name
			IF_FEATURE_CROND_D(,&G.log_level));
	/* both -d N and -l N set the same variable: G.log_level */

	if (!(opts & OPT_f)) {
		/* close stdin, stdout, stderr.
		 * close unused descriptors - don't need them. */
		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
	}

	if (!(opts & OPT_d) && G.log_filename == NULL) {
		/* logging to syslog */
		openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
		logmode = LOGMODE_SYSLOG;
	}

	//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */

	reopen_logfile_to_stderr();
	xchdir(G.crontab_dir_name);
	log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
	rescan_crontab_dir();
	write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");

	/* Main loop */
	t2 = time(NULL);
	rescan = 60;
	sleep_time = 60;
	for (;;) {
		struct stat sbuf;
		time_t t1;
		long dt;

		/* Synchronize to 1 minute, minimum 1 second */
		t1 = t2;
		sleep(sleep_time - (time(NULL) % sleep_time));
		t2 = time(NULL);
		dt = (long)t2 - (long)t1;

		reopen_logfile_to_stderr();

		/*
		 * The file 'cron.update' is checked to determine new cron
		 * jobs.  The directory is rescanned once an hour to deal
		 * with any screwups.
		 *
		 * Check for time jump.  Disparities over an hour either way
		 * result in resynchronization.  A negative disparity
		 * less than an hour causes us to effectively sleep until we
		 * match the original time (i.e. no re-execution of jobs that
		 * have just been run).  A positive disparity less than
		 * an hour causes intermediate jobs to be run, but only once
		 * in the worst case.
		 *
		 * When running jobs, the inequality used is greater but not
		 * equal to t1, and less then or equal to t2.
		 */
		if (stat(G.crontab_dir_name, &sbuf) != 0)
			sbuf.st_mtime = 0; /* force update (once) if dir was deleted */
		if (G.crontab_dir_mtime != sbuf.st_mtime) {
			G.crontab_dir_mtime = sbuf.st_mtime;
			rescan = 1;
		}
		if (--rescan == 0) {
			rescan = 60;
			rescan_crontab_dir();
		}
		process_cron_update_file();
		log5("wakeup dt=%ld", dt);
		if (dt < -60 * 60 || dt > 60 * 60) {
			bb_error_msg("time disparity of %ld minutes detected", dt / 60);
			/* and we do not run any jobs in this case */
		} else if (dt > 0) {
			/* Usual case: time advances forward, as expected */
			flag_starting_jobs(t1, t2);
			start_jobs();
			sleep_time = 60;
			if (check_completions() > 0) {
				/* some jobs are still running */
				sleep_time = 10;
			}
		}
		/* else: time jumped back, do not run any jobs */
	} /* for (;;) */

	return 0; /* not reached */
}
Esempio n. 30
0
int acpid_main(int argc UNUSED_PARAM, char **argv)
{
	int nfd;
	int opts;
	struct pollfd *pfd;
	const char *opt_dir = "/etc/acpi";
	const char *opt_input = "/dev/input/event";
	const char *opt_logfile = "/var/log/acpid.log";
	const char *opt_action = "/etc/acpid.conf";
	const char *opt_map = "/etc/acpi.map";
#if ENABLE_FEATURE_PIDFILE
	const char *opt_pidfile = "/var/run/acpid.pid";
#endif

	INIT_G();

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

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

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

	parse_conf_file(opt_action);
	parse_map_file(opt_map);

	xchdir(opt_dir);

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

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

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

	write_pidfile(opt_pidfile);

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

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

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

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

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

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

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

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

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

	return EXIT_SUCCESS;
}