💾 Archived View for thrig.me › tech › openbsd › few.c captured on 2024-12-17 at 12:12:34.

View Raw

More Information

-=-=-=-=-=-=-

// few - a shell that only allows a few programs to be run
//
//   CFLAGS="-ledit -lcurses" make few

#include <sys/wait.h>

#include <ctype.h>
#include <err.h>
#include <histedit.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

EditLine *elp;

char *prompt(EditLine *e);
void resize(int nsig);
int runit(const char *arg, int interactive);

int
main(int argc, char *const *argv)
{
	int ch, status = EXIT_SUCCESS;

	while ((ch = getopt(argc, argv, "abCefhimnuvxo:c:s")) != -1) {
		switch (ch) {
		case 'c':
			status = runit(optarg, 0);
			if (status == -1) {
				exit(EXIT_SUCCESS);
			} else {
				exit(status);
			}
		}
	}
	//argc -= optind;
	//argv += optind;

	elp = el_init(getprogname(), stdin, stdout, stderr);
	if (!elp) err(1, "el_init");

	el_parse(elp, argc, (const char **) argv);
	el_set(elp, EL_EDITOR, "vi");
	el_set(elp, EL_PROMPT, prompt);
	el_set(elp, EL_TERMINAL, NULL); // use $TERM
	el_source(elp, NULL);           // reads ~/.editrc

	// One may want more complicated control+c handling than this,
	// but this isn't much of a shell. Complicated things like
	// pipelines and job control are not offered.
	signal(SIGINT, SIG_IGN);
	signal(SIGTSTP, SIG_IGN);
	signal(SIGWINCH, resize);

#ifdef __OpenBSD__
	if (pledge("exec proc stdio tty unveil", NULL) == -1) err(1, "pledge");
	if (unveil("/usr/libexec/sftp-server", "x") == -1) err(1, "unveil");
	if (unveil("/usr/bin/uptime", "x") == -1) err(1, "unveil");
	if (unveil(NULL, NULL) == -1) err(1, "unveil");
#endif

	while (1) {
		int nchars;
		const char *line = el_gets(elp, &nchars);
		if (!line) {
			if (nchars == -1) err(1, "el_gets");
			continue;
		}
		if (nchars <= 1) continue; // probably only "\n"
		status = runit(line, 1);
		if (status == -1) exit(EXIT_SUCCESS);
	}

	el_end(elp);
	exit(status);
}

char *
prompt(EditLine *e)
{
	return "] ";
}

void
resize(int nsig)
{
	el_resize(elp);
}

int
runit(const char *arg, int interactive)
{
	int status    = EXIT_SUCCESS;
	char *command = strdup(arg);
	if (!command) err(1, "strdup");

	char *cp = command;
	while (isspace(*cp))
		cp++;

	char *cmd = strsep(&cp, " \f\n\r\t\v");
	if (!cmd) goto CLEANUP;

	if (strcmp(cmd, "exit") == 0) {
		status = -1;
	} else if (strcmp(cmd, "/usr/libexec/sftp-server") == 0) {
		if (interactive) {
			warnx("use sftp for SFTP access");
			status = 42;
		} else {
			execl("/usr/libexec/sftp-server", "sftp-server",
			      (char *) 0);
			err(1, "execl");
		}
	} else if (strcmp(cmd, "uptime") == 0) {
		pid_t pid = fork();
		switch (pid) {
		case -1:
			warn("fork");
			break;
		case 0:
			execl("/usr/bin/uptime", "uptime", (char *) 0);
			err(1, "execl");
		default:
			wait(&status);
		}
	} else {
		status = 42;
	}

CLEANUP:
	free(command);
	return status;
}