Пример #1
0
enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
{
	struct args		*args = self->args;
	struct winlink		*wl;
	struct window		*w;
	struct window_pane	*wp;
	struct session		*s;
	struct environ		*env;
	const char		*path;
	char			*cause;
	u_int			 idx;
	struct environ_entry	*envent;

	if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL)
		return (CMD_RETURN_ERROR);
	w = wl->window;

	if (!args_has(self->args, 'k') && wp->fd != -1) {
		if (window_pane_index(wp, &idx) != 0)
			fatalx("index not found");
		cmdq_error(cmdq, "pane still active: %s:%d.%u",
		    s->name, wl->idx, idx);
		return (CMD_RETURN_ERROR);
	}

	env = environ_create();
	environ_copy(global_environ, env);
	environ_copy(s->environ, env);
	server_fill_environ(s, env);

	window_pane_reset_mode(wp);
	screen_reinit(&wp->base);
	input_init(wp);

	path = NULL;
	if (cmdq->client != NULL && cmdq->client->session == NULL)
		envent = environ_find(cmdq->client->environ, "PATH");
	else
		envent = environ_find(s->environ, "PATH");
	if (envent != NULL)
		path = envent->value;

	if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env,
	    s->tio, &cause) != 0) {
		cmdq_error(cmdq, "respawn pane failed: %s", cause);
		free(cause);
		environ_free(env);
		return (CMD_RETURN_ERROR);
	}
	wp->flags |= PANE_REDRAW;
	server_status_window(w);

	environ_free(env);
	return (CMD_RETURN_NORMAL);
}
Пример #2
0
int
cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct winlink		*wl;
	struct window		*w;
	struct window_pane	*wp;
	struct session		*s;
	struct environ		 env;
	const char		*cmd;
	char			*cause;
	u_int			 idx;

	if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
		return (-1);
	w = wl->window;

	if (!args_has(self->args, 'k') && wp->fd != -1) {
		if (window_pane_index(wp, &idx) != 0)
			fatalx("index not found");
		ctx->error(ctx, "pane still active: %s:%u.%u",
		    s->name, wl->idx, idx);
		return (-1);
	}

	environ_init(&env);
	environ_copy(&global_environ, &env);
	environ_copy(&s->environ, &env);
	server_fill_environ(s, &env);

	window_pane_reset_mode(wp);
	screen_reinit(&wp->base);
	input_init(wp);

	if (args->argc != 0)
		cmd = args->argv[0];
	else
		cmd = NULL;
	if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) {
		ctx->error(ctx, "respawn pane failed: %s", cause);
		xfree(cause);
		environ_free(&env);
		return (-1);
	}
	wp->flags |= PANE_REDRAW;
	server_status_window(w);

	environ_free(&env);
	return (0);
}
Пример #3
0
static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{
	struct args		*args = self->args;
	struct winlink		*wl = item->target.wl;
	struct window		*w = wl->window;
	struct window_pane	*wp = item->target.wp;
	struct session		*s = item->target.s;
	struct environ		*env;
	const char		*path;
	char			*cause;
	u_int			 idx;
	struct environ_entry	*envent;

	if (!args_has(self->args, 'k') && wp->fd != -1) {
		if (window_pane_index(wp, &idx) != 0)
			fatalx("index not found");
		cmdq_error(item, "pane still active: %s:%d.%u",
		    s->name, wl->idx, idx);
		return (CMD_RETURN_ERROR);
	}

	window_pane_reset_mode(wp);
	screen_reinit(&wp->base);
	input_init(wp);

	path = NULL;
	if (item->client != NULL && item->client->session == NULL)
		envent = environ_find(item->client->environ, "PATH");
	else
		envent = environ_find(s->environ, "PATH");
	if (envent != NULL)
		path = envent->value;

	env = environ_for_session(s, 0);
	if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env,
	    s->tio, &cause) != 0) {
		cmdq_error(item, "respawn pane failed: %s", cause);
		free(cause);
		environ_free(env);
		return (CMD_RETURN_ERROR);
	}
	environ_free(env);

	wp->flags |= PANE_REDRAW;
	server_status_window(w);

	return (CMD_RETURN_NORMAL);
}
Пример #4
0
static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{
	struct args		*args = self->args;
	struct spawn_context	 sc;
	struct session		*s = item->target.s;
	struct winlink		*wl = item->target.wl;
	char			*cause = NULL;
	const char		*add;
	struct args_value	*value;

	memset(&sc, 0, sizeof sc);
	sc.item = item;
	sc.s = s;
	sc.wl = wl;

	sc.name = NULL;
	sc.argc = args->argc;
	sc.argv = args->argv;
	sc.environ = environ_create();

	add = args_first_value(args, 'e', &value);
	while (add != NULL) {
		environ_put(sc.environ, add);
		add = args_next_value(&value);
	}

	sc.idx = -1;
	sc.cwd = args_get(args, 'c');

	sc.flags = SPAWN_RESPAWN;
	if (args_has(args, 'k'))
		sc.flags |= SPAWN_KILL;

	if (spawn_window(&sc, &cause) == NULL) {
		cmdq_error(item, "respawn window failed: %s", cause);
		free(cause);
		return (CMD_RETURN_ERROR);
	}

	server_redraw_window(wl->window);

	environ_free(sc.environ);
	return (CMD_RETURN_NORMAL);
}
Пример #5
0
int
cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct cmd_split_window_data	*data = self->data;
	struct session			*s;
	struct winlink			*wl;
	struct window			*w;
	struct window_pane		*wp, *new_wp = NULL;
	struct environ			 env;
	char		 		*cmd, *cwd, *cause;
	const char			*shell;
	u_int				 hlimit;
	int				 size;
	enum layout_type		 type;
	struct layout_cell		*lc;

	if ((wl = cmd_find_pane(ctx, data->target, &s, &wp)) == NULL)
		return (-1);
	w = wl->window;

	environ_init(&env);
	environ_copy(&global_environ, &env);
	environ_copy(&s->environ, &env);
	server_fill_environ(s, &env);

	cmd = data->cmd;
	if (cmd == NULL)
		cmd = options_get_string(&s->options, "default-command");
	if (ctx->cmdclient == NULL || ctx->cmdclient->cwd == NULL)
		cwd = options_get_string(&s->options, "default-path");
	else
		cwd = ctx->cmdclient->cwd;

	type = LAYOUT_TOPBOTTOM;
	if (data->flag_horizontal)
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (data->size != -1)
		size = data->size;
	else if (data->percentage != -1) {
		if (type == LAYOUT_TOPBOTTOM)
			size = (wp->sy * data->percentage) / 100;
		else
			size = (wp->sx * data->percentage) / 100;
	}
	hlimit = options_get_number(&s->options, "history-limit");

	shell = options_get_string(&s->options, "default-shell");
	if (*shell == '\0' || areshell(shell))
		shell = _PATH_BSHELL;

	if ((lc = layout_split_pane(wp, type, size)) == NULL) {
		cause = xstrdup("pane too small");
		goto error;
	}
	new_wp = window_add_pane(w, hlimit);
	if (window_pane_spawn(
	    new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0)
		goto error;
	layout_assign_pane(lc, new_wp);

	server_redraw_window(w);

	if (!data->flag_detached) {
		window_set_active_pane(w, new_wp);
		session_select(s, wl->idx);
		server_redraw_session(s);
	} else
		server_status_session(s);

	environ_free(&env);
	return (0);

error:
	environ_free(&env);
	if (new_wp != NULL)
		window_remove_pane(w, new_wp);
	ctx->error(ctx, "create pane failed: %s", cause);
	xfree(cause);
	return (-1);
}
Пример #6
0
int
cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct session		*s;
	struct winlink		*wl;
	struct window		*w;
	struct window_pane	*wp, *new_wp = NULL;
	struct environ		 env;
	char		 	*cmd, *cwd, *cause;
	const char		*shell;
	u_int			 hlimit, paneidx;
	int			 size, percentage;
	enum layout_type	 type;
	struct layout_cell	*lc;

	if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
		return (-1);
	w = wl->window;

	environ_init(&env);
	environ_copy(&global_environ, &env);
	environ_copy(&s->environ, &env);
	server_fill_environ(s, &env);

	if (args->argc == 0)
		cmd = options_get_string(&s->options, "default-command");
	else
		cmd = args->argv[0];
	cwd = options_get_string(&s->options, "default-path");
	if (*cwd == '\0') {
		if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
			cwd = ctx->cmdclient->cwd;
		else
			cwd = s->cwd;
	}

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 's')) {
		size = args_strtonum(args, 's', 0, INT_MAX, &cause);
		if (cause != NULL) {
			ctx->error(ctx, "size %s", cause);
			xfree(cause);
			return (-1);
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
		if (cause != NULL) {
			ctx->error(ctx, "percentage %s", cause);
			xfree(cause);
			return (-1);
		}
		if (type == LAYOUT_TOPBOTTOM)
			size = (wp->sy * percentage) / 100;
		else
			size = (wp->sx * percentage) / 100;
	}
	hlimit = options_get_number(&s->options, "history-limit");

	shell = options_get_string(&s->options, "default-shell");
	if (*shell == '\0' || areshell(shell))
		shell = _PATH_BSHELL;

	if ((lc = layout_split_pane(wp, type, size)) == NULL) {
		cause = xstrdup("pane too small");
		goto error;
	}
	new_wp = window_add_pane(w, hlimit);
	if (window_pane_spawn(
	    new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0)
		goto error;
	layout_assign_pane(lc, new_wp);

	server_redraw_window(w);

	if (!args_has(args, 'd')) {
		window_set_active_pane(w, new_wp);
		session_select(s, wl->idx);
		server_redraw_session(s);
	} else
		server_status_session(s);

	environ_free(&env);

	if (args_has(args, 'P')) {
		paneidx = window_pane_index(wl->window, new_wp);
		ctx->print(ctx, "%s:%u.%u", s->name, wl->idx, paneidx);
	}
	return (0);

error:
	environ_free(&env);
	if (new_wp != NULL)
		window_remove_pane(w, new_wp);
	ctx->error(ctx, "create pane failed: %s", cause);
	xfree(cause);
	return (-1);
}
Пример #7
0
/* Start a job running, if it isn't already. */
struct job *
job_run(const char *cmd,
    void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
{
	struct job	*job;
	struct environ	 env;
	pid_t		 pid;
	int		 nullfd, out[2];

	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
		return (NULL);

	environ_init(&env);
	environ_copy(&global_environ, &env);
	server_fill_environ(NULL, &env);

	switch (pid = fork()) {
	case -1:
		environ_free(&env);
		return (NULL);
	case 0:		/* child */
		clear_signals(1);

		environ_push(&env);
		environ_free(&env);

		if (dup2(out[1], STDOUT_FILENO) == -1)
			fatal("dup2 failed");
		if (out[1] != STDOUT_FILENO)
			close(out[1]);
		close(out[0]);

		nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
		if (nullfd < 0)
			fatal("open failed");
		if (dup2(nullfd, STDIN_FILENO) == -1)
			fatal("dup2 failed");
		if (dup2(nullfd, STDERR_FILENO) == -1)
			fatal("dup2 failed");
		if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO)
			close(nullfd);

		closefrom(STDERR_FILENO + 1);

		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
		fatal("execl failed");
	}

	/* parent */
	environ_free(&env);
	close(out[1]);

	job = xmalloc(sizeof *job);
	job->cmd = xstrdup(cmd);
	job->pid = pid;
	job->status = 0;

	LIST_INSERT_HEAD(&all_jobs, job, lentry);

	job->callbackfn = callbackfn;
	job->freefn = freefn;
	job->data = data;

	job->fd = out[0];
	setblocking(job->fd, 0);

	job->event = bufferevent_new(job->fd, NULL, NULL, job_callback, job);
	bufferevent_enable(job->event, EV_READ);

	log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
	return (job);
}
Пример #8
0
int
cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct session		*s, *old_s, *groupwith;
	struct window		*w;
	struct window_pane	*wp;
	struct environ		 env;
	struct termios		 tio, *tiop;
	struct passwd		*pw;
	const char		*newname, *target, *update, *cwd;
	char			*overrides, *cmd, *cause;
	int			 detached, idx;
	u_int			 sx, sy, i;

	newname = args_get(args, 's');
	if (newname != NULL && session_find(newname) != NULL) {
		ctx->error(ctx, "duplicate session: %s", newname);
		return (-1);
	}

	target = args_get(args, 't');
	if (target != NULL) {
		groupwith = cmd_find_session(ctx, target);
		if (groupwith == NULL)
			return (-1);
	} else
		groupwith = NULL;

	/*
	 * There are three cases:
	 *
	 * 1. If cmdclient is non-NULL, new-session has been called from the
	 *    command-line - cmdclient is to become a new attached, interactive
	 *    client. Unless -d is given, the terminal must be opened and then
	 *    the client sent MSG_READY.
	 *
	 * 2. If cmdclient is NULL, new-session has been called from an
	 *    existing client (such as a key binding).
	 *
	 * 3. Both are NULL, the command was in the configuration file. Treat
	 *    this as if -d was given even if it was not.
	 *
	 * In all cases, a new additional session needs to be created and
	 * (unless -d) set as the current session for the client.
	 */

	/* Set -d if no client. */
	detached = args_has(args, 'd');
	if (ctx->cmdclient == NULL && ctx->curclient == NULL)
		detached = 1;

	/*
	 * Save the termios settings, part of which is used for new windows in
	 * this session.
	 *
	 * This is read again with tcgetattr() rather than using tty.tio as if
	 * detached, tty_open won't be called. Because of this, it must be done
	 * before opening the terminal as that calls tcsetattr() to prepare for
	 * tmux taking over.
	 */
	if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) {
		if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0)
			fatal("tcgetattr failed");
		tiop = &tio;
	} else
		tiop = NULL;

	/* Open the terminal if necessary. */
	if (!detached && ctx->cmdclient != NULL) {
		if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) {
			ctx->error(ctx, "not a terminal");
			return (-1);
		}

		overrides =
		    options_get_string(&global_s_options, "terminal-overrides");
		if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) {
			ctx->error(ctx, "open terminal failed: %s", cause);
			xfree(cause);
			return (-1);
		}
	}

	/* Get the new session working directory. */
	if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL)
		cwd = ctx->cmdclient->cwd;
	else {
		pw = getpwuid(getuid());
		if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
			cwd = pw->pw_dir;
		else
			cwd = "/";
	}

	/* Find new session size. */
	if (detached) {
		sx = 80;
		sy = 24;
	} else if (ctx->cmdclient != NULL) {
		sx = ctx->cmdclient->tty.sx;
		sy = ctx->cmdclient->tty.sy;
	} else {
		sx = ctx->curclient->tty.sx;
		sy = ctx->curclient->tty.sy;
	}
	if (sy > 0 && options_get_number(&global_s_options, "status"))
		sy--;
	if (sx == 0)
		sx = 1;
	if (sy == 0)
		sy = 1;

	/* Figure out the command for the new window. */
	if (target != NULL)
		cmd = NULL;
	else if (args->argc != 0)
		cmd = args->argv[0];
	else
		cmd = options_get_string(&global_s_options, "default-command");

	/* Construct the environment. */
	environ_init(&env);
	update = options_get_string(&global_s_options, "update-environment");
	if (ctx->cmdclient != NULL)
		environ_update(update, &ctx->cmdclient->environ, &env);

	/* Create the new session. */
	idx = -1 - options_get_number(&global_s_options, "base-index");
	s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause);
	if (s == NULL) {
		ctx->error(ctx, "create session failed: %s", cause);
		xfree(cause);
		return (-1);
	}
	environ_free(&env);

	/* Set the initial window name if one given. */
	if (cmd != NULL && args_has(args, 'n')) {
		w = s->curw->window;

		xfree(w->name);
		w->name = xstrdup(args_get(args, 'n'));

		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_ROOT(&s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (ctx->cmdclient != NULL) {
			server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);

			old_s = ctx->cmdclient->session;
			if (old_s != NULL)
				ctx->cmdclient->last_session = old_s;
			ctx->cmdclient->session = s;
			session_update_activity(s);
			server_redraw_client(ctx->cmdclient);
		} else {
			old_s = ctx->curclient->session;
			if (old_s != NULL)
				ctx->curclient->last_session = old_s;
			ctx->curclient->session = s;
			session_update_activity(s);
			server_redraw_client(ctx->curclient);
		}
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) {
		wp = s->curw->window->active;
		window_pane_set_mode(wp, &window_copy_mode);
		window_copy_init_for_output(wp);
		for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
			cause = ARRAY_ITEM(&cfg_causes, i);
			window_copy_add(wp, "%s", cause);
			xfree(cause);
		}
		ARRAY_FREE(&cfg_causes);
	}

	return (!detached);	/* 1 means don't tell command client to exit */
}
Пример #9
0
enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
{
	struct args		*args = self->args;
	struct session		*s;
	struct winlink		*wl;
	struct window		*w;
	struct window_pane	*wp, *new_wp = NULL;
	struct environ		 env;
	const char		*cmd, *shell, *template;
	char			*cause, *new_cause, *cp;
	u_int			 hlimit;
	int			 size, percentage, cwd, fd = -1;
	enum layout_type	 type;
	struct layout_cell	*lc;
	struct client		*c;
	struct format_tree	*ft;

	if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL)
		return (CMD_RETURN_ERROR);
	w = wl->window;
	server_unzoom_window(w);

	environ_init(&env);
	environ_copy(&global_environ, &env);
	environ_copy(&s->environ, &env);
	server_fill_environ(s, &env);

	if (args->argc == 0)
		cmd = options_get_string(&s->options, "default-command");
	else
		cmd = args->argv[0];

	if (args_has(args, 'c')) {
		ft = format_create();
		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
			format_client(ft, c);
		format_session(ft, s);
		format_winlink(ft, s, s->curw);
		format_window_pane(ft, s->curw->window->active);
		cp = format_expand(ft, args_get(args, 'c'));
		format_free(ft);

		fd = open(cp, O_RDONLY|O_DIRECTORY);
		free(cp);
		if (fd == -1) {
			cmdq_error(cmdq, "bad working directory: %s",
			    strerror(errno));
			return (CMD_RETURN_ERROR);
		}
		cwd = fd;
	} else if (cmdq->client != NULL && cmdq->client->session == NULL)
		cwd = cmdq->client->cwd;
	else
		cwd = s->cwd;

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 'l')) {
		size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
		if (cause != NULL) {
			xasprintf(&new_cause, "size %s", cause);
			free(cause);
			cause = new_cause;
			goto error;
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
		if (cause != NULL) {
			xasprintf(&new_cause, "percentage %s", cause);
			free(cause);
			cause = new_cause;
			goto error;
		}
		if (type == LAYOUT_TOPBOTTOM)
			size = (wp->sy * percentage) / 100;
		else
			size = (wp->sx * percentage) / 100;
	}
	hlimit = options_get_number(&s->options, "history-limit");

	shell = options_get_string(&s->options, "default-shell");
	if (*shell == '\0' || areshell(shell))
		shell = _PATH_BSHELL;

	if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) {
		cause = xstrdup("pane too small");
		goto error;
	}
	new_wp = window_add_pane(w, hlimit);
	if (window_pane_spawn(
	    new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0)
		goto error;
	layout_assign_pane(lc, new_wp);

	server_redraw_window(w);

	if (!args_has(args, 'd')) {
		window_set_active_pane(w, new_wp);
		session_select(s, wl->idx);
		server_redraw_session(s);
	} else
		server_status_session(s);

	environ_free(&env);

	if (args_has(args, 'P')) {
		if ((template = args_get(args, 'F')) == NULL)
Пример #10
0
enum cmd_retval
cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
{
	struct args		*args = self->args;
	struct client		*c = cmdq->client, *c0;
	struct session		*s, *groupwith;
	struct window		*w;
	struct environ		 env;
	struct termios		 tio, *tiop;
	const char		*newname, *target, *update, *errstr, *template;
	const char		*path;
	char		       **argv, *cmd, *cause, *cp;
	int			 detached, already_attached, idx, cwd, fd = -1;
	int			 argc;
	u_int			 sx, sy;
	struct format_tree	*ft;
	struct environ_entry	*envent;

	if (self->entry == &cmd_has_session_entry) {
		if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL)
			return (CMD_RETURN_ERROR);
		return (CMD_RETURN_NORMAL);
	}

	if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) {
		cmdq_error(cmdq, "command or window name given with target");
		return (CMD_RETURN_ERROR);
	}

	newname = args_get(args, 's');
	if (newname != NULL) {
		if (!session_check_name(newname)) {
			cmdq_error(cmdq, "bad session name: %s", newname);
			return (CMD_RETURN_ERROR);
		}
		if (session_find(newname) != NULL) {
			if (args_has(args, 'A')) {
				return (cmd_attach_session(cmdq, newname,
				    args_has(args, 'D'), 0, NULL,
				    args_has(args, 'E')));
			}
			cmdq_error(cmdq, "duplicate session: %s", newname);
			return (CMD_RETURN_ERROR);
		}
	}

	target = args_get(args, 't');
	if (target != NULL) {
		groupwith = cmd_find_session(cmdq, target, 0);
		if (groupwith == NULL)
			return (CMD_RETURN_ERROR);
	} else
		groupwith = NULL;

	/* Set -d if no client. */
	detached = args_has(args, 'd');
	if (c == NULL)
		detached = 1;

	/* Is this client already attached? */
	already_attached = 0;
	if (c != NULL && c->session != NULL)
		already_attached = 1;

	/* Get the new session working directory. */
	if (args_has(args, 'c')) {
		ft = format_create();
		format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL,
		    NULL);
		cp = format_expand(ft, args_get(args, 'c'));
		format_free(ft);

		if (cp != NULL && *cp != '\0') {
			fd = open(cp, O_RDONLY|O_DIRECTORY);
			free(cp);
			if (fd == -1) {
				cmdq_error(cmdq, "bad working directory: %s",
				    strerror(errno));
				return (CMD_RETURN_ERROR);
			}
		} else if (cp != NULL)
			free(cp);
		cwd = fd;
	} else if (c != NULL && c->session == NULL)
		cwd = c->cwd;
	else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL)
		cwd = c0->session->cwd;
	else {
		fd = open(".", O_RDONLY);
		cwd = fd;
	}

	/*
	 * If this is a new client, check for nesting and save the termios
	 * settings (part of which is used for new windows in this session).
	 *
	 * tcgetattr() is used rather than using tty.tio since if the client is
	 * detached, tty_open won't be called. It must be done before opening
	 * the terminal as that calls tcsetattr() to prepare for tmux taking
	 * over.
	 */
	if (!detached && !already_attached && c->tty.fd != -1) {
		if (server_client_check_nested(cmdq->client)) {
			cmdq_error(cmdq, "sessions should be nested with care, "
			    "unset $TMUX to force");
			return (CMD_RETURN_ERROR);
		}
		if (tcgetattr(c->tty.fd, &tio) != 0)
			fatal("tcgetattr failed");
		tiop = &tio;
	} else
		tiop = NULL;

	/* Open the terminal if necessary. */
	if (!detached && !already_attached) {
		if (server_client_open(c, &cause) != 0) {
			cmdq_error(cmdq, "open terminal failed: %s", cause);
			free(cause);
			goto error;
		}
	}

	/* Find new session size. */
	if (c != NULL) {
		sx = c->tty.sx;
		sy = c->tty.sy;
	} else {
		sx = 80;
		sy = 24;
	}
	if (detached && args_has(args, 'x')) {
		sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
		if (errstr != NULL) {
			cmdq_error(cmdq, "width %s", errstr);
			goto error;
		}
	}
	if (detached && args_has(args, 'y')) {
		sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
		if (errstr != NULL) {
			cmdq_error(cmdq, "height %s", errstr);
			goto error;
		}
	}
	if (sy > 0 && options_get_number(&global_s_options, "status"))
		sy--;
	if (sx == 0)
		sx = 1;
	if (sy == 0)
		sy = 1;

	/* Figure out the command for the new window. */
	argc = -1;
	argv = NULL;
	if (target == NULL && args->argc != 0) {
		argc = args->argc;
		argv = args->argv;
	} else if (target == NULL) {
		cmd = options_get_string(&global_s_options, "default-command");
		if (cmd != NULL && *cmd != '\0') {
			argc = 1;
			argv = &cmd;
		} else {
			argc = 0;
			argv = NULL;
		}
	}

	path = NULL;
	if (c != NULL && c->session == NULL)
		envent = environ_find(&c->environ, "PATH");
	else
		envent = environ_find(&global_environ, "PATH");
	if (envent != NULL)
		path = envent->value;

	/* Construct the environment. */
	environ_init(&env);
	if (c != NULL && !args_has(args, 'E')) {
		update = options_get_string(&global_s_options,
		    "update-environment");
		environ_update(update, &c->environ, &env);
	}

	/* Create the new session. */
	idx = -1 - options_get_number(&global_s_options, "base-index");
	s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx,
	    sy, &cause);
	if (s == NULL) {
		cmdq_error(cmdq, "create session failed: %s", cause);
		free(cause);
		goto error;
	}
	environ_free(&env);

	/* Set the initial window name if one given. */
	if (argc >= 0 && args_has(args, 'n')) {
		w = s->curw->window;
		window_set_name(w, args_get(args, 'n'));
		options_set_number(&w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_MIN(winlinks, &s->windows)->idx);
	}

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (!already_attached)
			server_write_ready(c);
		else if (c->session != NULL)
			c->last_session = c->session;
		c->session = s;
		status_timer_start(c);
		notify_attached_session_changed(c);
		session_update_activity(s);
		server_redraw_client(c);
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished)
		cfg_show_causes(s);

	/* Print if requested. */
	if (args_has(args, 'P')) {
		if ((template = args_get(args, 'F')) == NULL)
Пример #11
0
enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
{
	struct args		*args = self->args;
	struct session		*s;
	struct winlink		*wl;
	struct window		*w;
	struct window_pane	*wp, *new_wp = NULL;
	struct environ		 env;
	const char		*cmd, *cwd, *shell;
	char			*cause, *new_cause;
	u_int			 hlimit;
	int			 size, percentage;
	enum layout_type	 type;
	struct layout_cell	*lc;
	const char		*template;
	struct client		*c;
	struct format_tree	*ft;
	char			*cp;

	if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL)
		return (CMD_RETURN_ERROR);
	w = wl->window;

	environ_init(&env);
	environ_copy(&global_environ, &env);
	environ_copy(&s->environ, &env);
	server_fill_environ(s, &env);

	if (args->argc == 0)
		cmd = options_get_string(&s->options, "default-command");
	else
		cmd = args->argv[0];
	cwd = cmd_get_default_path(ctx, args_get(args, 'c'));

	type = LAYOUT_TOPBOTTOM;
	if (args_has(args, 'h'))
		type = LAYOUT_LEFTRIGHT;

	size = -1;
	if (args_has(args, 'l')) {
		size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
		if (cause != NULL) {
			xasprintf(&new_cause, "size %s", cause);
			free(cause);
			cause = new_cause;
			goto error;
		}
	} else if (args_has(args, 'p')) {
		percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
		if (cause != NULL) {
			xasprintf(&new_cause, "percentage %s", cause);
			free(cause);
			cause = new_cause;
			goto error;
		}
		if (type == LAYOUT_TOPBOTTOM)
			size = (wp->sy * percentage) / 100;
		else
			size = (wp->sx * percentage) / 100;
	}
	hlimit = options_get_number(&s->options, "history-limit");

	shell = options_get_string(&s->options, "default-shell");
	if (*shell == '\0' || areshell(shell))
		shell = _PATH_BSHELL;

	if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) {
		cause = xstrdup("pane too small");
		goto error;
	}
	new_wp = window_add_pane(w, hlimit);
	if (window_pane_spawn(
	    new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0)
		goto error;
	layout_assign_pane(lc, new_wp);

	server_redraw_window(w);

	if (!args_has(args, 'd')) {
		window_set_active_pane(w, new_wp);
		session_select(s, wl->idx);
		server_redraw_session(s);
	} else
		server_status_session(s);

	environ_free(&env);

	if (args_has(args, 'P')) {
		if ((template = args_get(args, 'F')) == NULL)
Пример #12
0
static enum cmd_retval
cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
{
	struct args		*args = self->args;
	struct client		*c = item->client;
	struct session		*s, *as;
	struct session		*groupwith = item->state.tflag.s;
	struct window		*w;
	struct environ		*env;
	struct termios		 tio, *tiop;
	const char		*newname, *target, *update, *errstr, *template;
	const char		*path, *cwd, *to_free = NULL;
	char		       **argv, *cmd, *cause, *cp;
	int			 detached, already_attached, idx, argc;
	u_int			 sx, sy;
	struct format_tree	*ft;
	struct environ_entry	*envent;
	struct cmd_find_state	 fs;

	if (self->entry == &cmd_has_session_entry) {
		/*
		 * cmd_prepare() will fail if the session cannot be found,
		 * hence always return success here.
		 */
		return (CMD_RETURN_NORMAL);
	}

	if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) {
		cmdq_error(item, "command or window name given with target");
		return (CMD_RETURN_ERROR);
	}

	newname = args_get(args, 's');
	if (newname != NULL) {
		if (!session_check_name(newname)) {
			cmdq_error(item, "bad session name: %s", newname);
			return (CMD_RETURN_ERROR);
		}
		if ((as = session_find(newname)) != NULL) {
			if (args_has(args, 'A')) {
				/*
				 * This item is now destined for
				 * attach-session. Because attach-session will
				 * have already been prepared, copy this
				 * session into its tflag so it can be used.
				 */
				cmd_find_from_session(&item->state.tflag, as);
				return (cmd_attach_session(item,
				    args_has(args, 'D'), 0, NULL,
				    args_has(args, 'E')));
			}
			cmdq_error(item, "duplicate session: %s", newname);
			return (CMD_RETURN_ERROR);
		}
	}

	if ((target = args_get(args, 't')) != NULL) {
		if (groupwith == NULL) {
			cmdq_error(item, "no such session: %s", target);
			goto error;
		}
	} else
		groupwith = NULL;

	/* Set -d if no client. */
	detached = args_has(args, 'd');
	if (c == NULL)
		detached = 1;

	/* Is this client already attached? */
	already_attached = 0;
	if (c != NULL && c->session != NULL)
		already_attached = 1;

	/* Get the new session working directory. */
	if (args_has(args, 'c')) {
		ft = format_create(item, 0);
		format_defaults(ft, c, NULL, NULL, NULL);
		to_free = cwd = format_expand(ft, args_get(args, 'c'));
		format_free(ft);
	} else if (c != NULL && c->session == NULL && c->cwd != NULL)
		cwd = c->cwd;
	else
		cwd = ".";

	/*
	 * If this is a new client, check for nesting and save the termios
	 * settings (part of which is used for new windows in this session).
	 *
	 * tcgetattr() is used rather than using tty.tio since if the client is
	 * detached, tty_open won't be called. It must be done before opening
	 * the terminal as that calls tcsetattr() to prepare for tmux taking
	 * over.
	 */
	if (!detached && !already_attached && c->tty.fd != -1) {
		if (server_client_check_nested(item->client)) {
			cmdq_error(item, "sessions should be nested with care, "
			    "unset $TMUX to force");
			return (CMD_RETURN_ERROR);
		}
		if (tcgetattr(c->tty.fd, &tio) != 0)
			fatal("tcgetattr failed");
		tiop = &tio;
	} else
		tiop = NULL;

	/* Open the terminal if necessary. */
	if (!detached && !already_attached) {
		if (server_client_open(c, &cause) != 0) {
			cmdq_error(item, "open terminal failed: %s", cause);
			free(cause);
			goto error;
		}
	}

	/* Find new session size. */
	if (c != NULL) {
		sx = c->tty.sx;
		sy = c->tty.sy;
	} else {
		sx = 80;
		sy = 24;
	}
	if (detached && args_has(args, 'x')) {
		sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
		if (errstr != NULL) {
			cmdq_error(item, "width %s", errstr);
			goto error;
		}
	}
	if (detached && args_has(args, 'y')) {
		sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
		if (errstr != NULL) {
			cmdq_error(item, "height %s", errstr);
			goto error;
		}
	}
	if (sy > 0 && options_get_number(global_s_options, "status"))
		sy--;
	if (sx == 0)
		sx = 1;
	if (sy == 0)
		sy = 1;

	/* Figure out the command for the new window. */
	argc = -1;
	argv = NULL;
	if (!args_has(args, 't') && args->argc != 0) {
		argc = args->argc;
		argv = args->argv;
	} else if (groupwith == NULL) {
		cmd = options_get_string(global_s_options, "default-command");
		if (cmd != NULL && *cmd != '\0') {
			argc = 1;
			argv = &cmd;
		} else {
			argc = 0;
			argv = NULL;
		}
	}

	path = NULL;
	if (c != NULL && c->session == NULL)
		envent = environ_find(c->environ, "PATH");
	else
		envent = environ_find(global_environ, "PATH");
	if (envent != NULL)
		path = envent->value;

	/* Construct the environment. */
	env = environ_create();
	if (c != NULL && !args_has(args, 'E')) {
		update = options_get_string(global_s_options,
		    "update-environment");
		environ_update(update, c->environ, env);
	}

	/* Create the new session. */
	idx = -1 - options_get_number(global_s_options, "base-index");
	s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx,
	    sy, &cause);
	environ_free(env);
	if (s == NULL) {
		cmdq_error(item, "create session failed: %s", cause);
		free(cause);
		goto error;
	}

	/* Set the initial window name if one given. */
	if (argc >= 0 && args_has(args, 'n')) {
		w = s->curw->window;
		window_set_name(w, args_get(args, 'n'));
		options_set_number(w->options, "automatic-rename", 0);
	}

	/*
	 * If a target session is given, this is to be part of a session group,
	 * so add it to the group and synchronize.
	 */
	if (groupwith != NULL) {
		session_group_add(groupwith, s);
		session_group_synchronize_to(s);
		session_select(s, RB_MIN(winlinks, &s->windows)->idx);
	}
	notify_session("session-created", s);

	/*
	 * Set the client to the new session. If a command client exists, it is
	 * taking this session and needs to get MSG_READY and stay around.
	 */
	if (!detached) {
		if (!already_attached) {
			if (~c->flags & CLIENT_CONTROL)
				proc_send(c->peer, MSG_READY, -1, NULL, 0);
		} else if (c->session != NULL)
			c->last_session = c->session;
		c->session = s;
		server_client_set_key_table(c, NULL);
		status_timer_start(c);
		notify_client("client-session-changed", c);
		session_update_activity(s, NULL);
		gettimeofday(&s->last_attached_time, NULL);
		server_redraw_client(c);
	}
	recalculate_sizes();
	server_update_socket();

	/*
	 * If there are still configuration file errors to display, put the new
	 * session's current window into more mode and display them now.
	 */
	if (cfg_finished)
		cfg_show_causes(s);

	/* Print if requested. */
	if (args_has(args, 'P')) {
		if ((template = args_get(args, 'F')) == NULL)
Пример #13
0
/* Start a job running, if it isn't already. */
struct job *
job_run(const char *cmd, struct session *s, const char *cwd,
    job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb,
    void *data)
{
	struct job	*job;
	struct environ	*env;
	pid_t		 pid;
	int		 nullfd, out[2];
	const char	*home;

	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
		return (NULL);

	/*
	 * Do not set TERM during .tmux.conf, it is nice to be able to use
	 * if-shell to decide on default-terminal based on outside TERM.
	 */
	env = environ_for_session(s, !cfg_finished);

	switch (pid = fork()) {
	case -1:
		environ_free(env);
		close(out[0]);
		close(out[1]);
		return (NULL);
	case 0:		/* child */
		clear_signals(1);

		if (cwd == NULL || chdir(cwd) != 0) {
			if ((home = find_home()) == NULL || chdir(home) != 0)
				chdir("/");
		}

		environ_push(env);
		environ_free(env);

		if (dup2(out[1], STDIN_FILENO) == -1)
			fatal("dup2 failed");
		if (dup2(out[1], STDOUT_FILENO) == -1)
			fatal("dup2 failed");
		if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
			close(out[1]);
		close(out[0]);

		nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
		if (nullfd < 0)
			fatal("open failed");
		if (dup2(nullfd, STDERR_FILENO) == -1)
			fatal("dup2 failed");
		if (nullfd != STDERR_FILENO)
			close(nullfd);

		closefrom(STDERR_FILENO + 1);

		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
		fatal("execl failed");
	}

	/* parent */
	environ_free(env);
	close(out[1]);

	job = xmalloc(sizeof *job);
	job->state = JOB_RUNNING;

	job->cmd = xstrdup(cmd);
	job->pid = pid;
	job->status = 0;

	LIST_INSERT_HEAD(&all_jobs, job, entry);

	job->updatecb = updatecb;
	job->completecb = completecb;
	job->freecb = freecb;
	job->data = data;

	job->fd = out[0];
	setblocking(job->fd, 0);

	job->event = bufferevent_new(job->fd, job_read_callback,
	    job_write_callback, job_error_callback, job);
	bufferevent_enable(job->event, EV_READ|EV_WRITE);

	log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
	return (job);
}