File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / tmux / cmd-new-session.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:22:44 2017 UTC (6 years, 11 months ago) by misho
Branches: tmux, MAIN
CVS tags: v2_4p0, v2_4, HEAD
tmux 2.4

    1: /* $OpenBSD$ */
    2: 
    3: /*
    4:  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
    5:  *
    6:  * Permission to use, copy, modify, and distribute this software for any
    7:  * purpose with or without fee is hereby granted, provided that the above
    8:  * copyright notice and this permission notice appear in all copies.
    9:  *
   10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
   15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
   16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17:  */
   18: 
   19: #include <sys/types.h>
   20: 
   21: #include <errno.h>
   22: #include <fcntl.h>
   23: #include <stdlib.h>
   24: #include <string.h>
   25: #include <termios.h>
   26: #include <unistd.h>
   27: 
   28: #include "tmux.h"
   29: 
   30: /*
   31:  * Create a new session and attach to the current terminal unless -d is given.
   32:  */
   33: 
   34: #define NEW_SESSION_TEMPLATE "#{session_name}:"
   35: 
   36: static enum cmd_retval	cmd_new_session_exec(struct cmd *, struct cmdq_item *);
   37: 
   38: const struct cmd_entry cmd_new_session_entry = {
   39: 	.name = "new-session",
   40: 	.alias = "new",
   41: 
   42: 	.args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 },
   43: 	.usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] "
   44: 		 "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
   45: 		 "[-y height] [command]",
   46: 
   47: 	.tflag = CMD_SESSION_CANFAIL,
   48: 
   49: 	.flags = CMD_STARTSERVER,
   50: 	.exec = cmd_new_session_exec
   51: };
   52: 
   53: const struct cmd_entry cmd_has_session_entry = {
   54: 	.name = "has-session",
   55: 	.alias = "has",
   56: 
   57: 	.args = { "t:", 0, 0 },
   58: 	.usage = CMD_TARGET_SESSION_USAGE,
   59: 
   60: 	.tflag = CMD_SESSION,
   61: 
   62: 	.flags = 0,
   63: 	.exec = cmd_new_session_exec
   64: };
   65: 
   66: static enum cmd_retval
   67: cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
   68: {
   69: 	struct args		*args = self->args;
   70: 	struct client		*c = item->client;
   71: 	struct session		*s, *as, *groupwith;
   72: 	struct window		*w;
   73: 	struct environ		*env;
   74: 	struct termios		 tio, *tiop;
   75: 	struct session_group	*sg;
   76: 	const char		*newname, *errstr, *template, *group, *prefix;
   77: 	const char		*path, *cmd, *cwd, *to_free = NULL;
   78: 	char		       **argv, *cause, *cp;
   79: 	int			 detached, already_attached, idx, argc;
   80: 	u_int			 sx, sy;
   81: 	struct environ_entry	*envent;
   82: 	struct cmd_find_state	 fs;
   83: 
   84: 	if (self->entry == &cmd_has_session_entry) {
   85: 		/*
   86: 		 * cmd_prepare() will fail if the session cannot be found,
   87: 		 * hence always return success here.
   88: 		 */
   89: 		return (CMD_RETURN_NORMAL);
   90: 	}
   91: 
   92: 	if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) {
   93: 		cmdq_error(item, "command or window name given with target");
   94: 		return (CMD_RETURN_ERROR);
   95: 	}
   96: 
   97: 	newname = args_get(args, 's');
   98: 	if (newname != NULL) {
   99: 		if (!session_check_name(newname)) {
  100: 			cmdq_error(item, "bad session name: %s", newname);
  101: 			return (CMD_RETURN_ERROR);
  102: 		}
  103: 		if ((as = session_find(newname)) != NULL) {
  104: 			if (args_has(args, 'A')) {
  105: 				/*
  106: 				 * This item is now destined for
  107: 				 * attach-session. Because attach-session will
  108: 				 * have already been prepared, copy this
  109: 				 * session into its tflag so it can be used.
  110: 				 */
  111: 				cmd_find_from_session(&item->state.tflag, as);
  112: 				return (cmd_attach_session(item,
  113: 				    args_has(args, 'D'), 0, NULL,
  114: 				    args_has(args, 'E')));
  115: 			}
  116: 			cmdq_error(item, "duplicate session: %s", newname);
  117: 			return (CMD_RETURN_ERROR);
  118: 		}
  119: 	}
  120: 
  121: 	/* Is this going to be part of a session group? */
  122: 	group = args_get(args, 't');
  123: 	if (group != NULL) {
  124: 		groupwith = item->state.tflag.s;
  125: 		if (groupwith == NULL) {
  126: 			if (!session_check_name(group)) {
  127: 				cmdq_error(item, "bad group name: %s", group);
  128: 				goto error;
  129: 			}
  130: 			sg = session_group_find(group);
  131: 		} else
  132: 			sg = session_group_contains(groupwith);
  133: 		if (sg != NULL)
  134: 			prefix = sg->name;
  135: 		else if (groupwith != NULL)
  136: 			prefix = groupwith->name;
  137: 		else
  138: 			prefix = group;
  139: 	} else {
  140: 		groupwith = NULL;
  141: 		sg = NULL;
  142: 		prefix = NULL;
  143: 	}
  144: 
  145: 	/* Set -d if no client. */
  146: 	detached = args_has(args, 'd');
  147: 	if (c == NULL)
  148: 		detached = 1;
  149: 
  150: 	/* Is this client already attached? */
  151: 	already_attached = 0;
  152: 	if (c != NULL && c->session != NULL)
  153: 		already_attached = 1;
  154: 
  155: 	/* Get the new session working directory. */
  156: 	if (args_has(args, 'c')) {
  157: 		cwd = args_get(args, 'c');
  158: 		to_free = cwd = format_single(item, cwd, c, NULL, NULL, NULL);
  159: 	} else if (c != NULL && c->session == NULL && c->cwd != NULL)
  160: 		cwd = c->cwd;
  161: 	else
  162: 		cwd = ".";
  163: 
  164: 	/*
  165: 	 * If this is a new client, check for nesting and save the termios
  166: 	 * settings (part of which is used for new windows in this session).
  167: 	 *
  168: 	 * tcgetattr() is used rather than using tty.tio since if the client is
  169: 	 * detached, tty_open won't be called. It must be done before opening
  170: 	 * the terminal as that calls tcsetattr() to prepare for tmux taking
  171: 	 * over.
  172: 	 */
  173: 	if (!detached && !already_attached && c->tty.fd != -1) {
  174: 		if (server_client_check_nested(item->client)) {
  175: 			cmdq_error(item, "sessions should be nested with care, "
  176: 			    "unset $TMUX to force");
  177: 			return (CMD_RETURN_ERROR);
  178: 		}
  179: 		if (tcgetattr(c->tty.fd, &tio) != 0)
  180: 			fatal("tcgetattr failed");
  181: 		tiop = &tio;
  182: 	} else
  183: 		tiop = NULL;
  184: 
  185: 	/* Open the terminal if necessary. */
  186: 	if (!detached && !already_attached) {
  187: 		if (server_client_open(c, &cause) != 0) {
  188: 			cmdq_error(item, "open terminal failed: %s", cause);
  189: 			free(cause);
  190: 			goto error;
  191: 		}
  192: 	}
  193: 
  194: 	/* Find new session size. */
  195: 	if (c != NULL) {
  196: 		sx = c->tty.sx;
  197: 		sy = c->tty.sy;
  198: 	} else {
  199: 		sx = 80;
  200: 		sy = 24;
  201: 	}
  202: 	if (detached && args_has(args, 'x')) {
  203: 		sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
  204: 		if (errstr != NULL) {
  205: 			cmdq_error(item, "width %s", errstr);
  206: 			goto error;
  207: 		}
  208: 	}
  209: 	if (detached && args_has(args, 'y')) {
  210: 		sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
  211: 		if (errstr != NULL) {
  212: 			cmdq_error(item, "height %s", errstr);
  213: 			goto error;
  214: 		}
  215: 	}
  216: 	if (sy > 0 && options_get_number(global_s_options, "status"))
  217: 		sy--;
  218: 	if (sx == 0)
  219: 		sx = 1;
  220: 	if (sy == 0)
  221: 		sy = 1;
  222: 
  223: 	/* Figure out the command for the new window. */
  224: 	argc = -1;
  225: 	argv = NULL;
  226: 	if (!args_has(args, 't') && args->argc != 0) {
  227: 		argc = args->argc;
  228: 		argv = args->argv;
  229: 	} else if (sg == NULL && groupwith == NULL) {
  230: 		cmd = options_get_string(global_s_options, "default-command");
  231: 		if (cmd != NULL && *cmd != '\0') {
  232: 			argc = 1;
  233: 			argv = (char **)&cmd;
  234: 		} else {
  235: 			argc = 0;
  236: 			argv = NULL;
  237: 		}
  238: 	}
  239: 
  240: 	path = NULL;
  241: 	if (c != NULL && c->session == NULL)
  242: 		envent = environ_find(c->environ, "PATH");
  243: 	else
  244: 		envent = environ_find(global_environ, "PATH");
  245: 	if (envent != NULL)
  246: 		path = envent->value;
  247: 
  248: 	/* Construct the environment. */
  249: 	env = environ_create();
  250: 	if (c != NULL && !args_has(args, 'E'))
  251: 		environ_update(global_s_options, c->environ, env);
  252: 
  253: 	/* Create the new session. */
  254: 	idx = -1 - options_get_number(global_s_options, "base-index");
  255: 	s = session_create(prefix, newname, argc, argv, path, cwd, env, tiop,
  256: 	    idx, sx, sy, &cause);
  257: 	environ_free(env);
  258: 	if (s == NULL) {
  259: 		cmdq_error(item, "create session failed: %s", cause);
  260: 		free(cause);
  261: 		goto error;
  262: 	}
  263: 
  264: 	/* Set the initial window name if one given. */
  265: 	if (argc >= 0 && args_has(args, 'n')) {
  266: 		w = s->curw->window;
  267: 		window_set_name(w, args_get(args, 'n'));
  268: 		options_set_number(w->options, "automatic-rename", 0);
  269: 	}
  270: 
  271: 	/*
  272: 	 * If a target session is given, this is to be part of a session group,
  273: 	 * so add it to the group and synchronize.
  274: 	 */
  275: 	if (group != NULL) {
  276: 		if (sg == NULL) {
  277: 			if (groupwith != NULL) {
  278: 				sg = session_group_new(groupwith->name);
  279: 				session_group_add(sg, groupwith);
  280: 			} else
  281: 				sg = session_group_new(group);
  282: 		}
  283: 		session_group_add(sg, s);
  284: 		session_group_synchronize_to(s);
  285: 		session_select(s, RB_MIN(winlinks, &s->windows)->idx);
  286: 	}
  287: 	notify_session("session-created", s);
  288: 
  289: 	/*
  290: 	 * Set the client to the new session. If a command client exists, it is
  291: 	 * taking this session and needs to get MSG_READY and stay around.
  292: 	 */
  293: 	if (!detached) {
  294: 		if (!already_attached) {
  295: 			if (~c->flags & CLIENT_CONTROL)
  296: 				proc_send(c->peer, MSG_READY, -1, NULL, 0);
  297: 		} else if (c->session != NULL)
  298: 			c->last_session = c->session;
  299: 		c->session = s;
  300: 		if (!item->repeat)
  301: 			server_client_set_key_table(c, NULL);
  302: 		status_timer_start(c);
  303: 		notify_client("client-session-changed", c);
  304: 		session_update_activity(s, NULL);
  305: 		gettimeofday(&s->last_attached_time, NULL);
  306: 		server_redraw_client(c);
  307: 	}
  308: 	recalculate_sizes();
  309: 	server_update_socket();
  310: 
  311: 	/*
  312: 	 * If there are still configuration file errors to display, put the new
  313: 	 * session's current window into more mode and display them now.
  314: 	 */
  315: 	if (cfg_finished)
  316: 		cfg_show_causes(s);
  317: 
  318: 	/* Print if requested. */
  319: 	if (args_has(args, 'P')) {
  320: 		if ((template = args_get(args, 'F')) == NULL)
  321: 			template = NEW_SESSION_TEMPLATE;
  322: 		cp = format_single(item, template, c, s, NULL, NULL);
  323: 		cmdq_print(item, "%s", cp);
  324: 		free(cp);
  325: 	}
  326: 
  327: 	if (!detached)
  328: 		c->flags |= CLIENT_ATTACHED;
  329: 
  330: 	if (to_free != NULL)
  331: 		free((void *)to_free);
  332: 
  333: 	cmd_find_from_session(&fs, s);
  334: 	hooks_insert(s->hooks, item, &fs, "after-new-session");
  335: 
  336: 	return (CMD_RETURN_NORMAL);
  337: 
  338: error:
  339: 	if (to_free != NULL)
  340: 		free((void *)to_free);
  341: 	return (CMD_RETURN_ERROR);
  342: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>