4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
31 * Create a new session and attach to the current terminal unless -d is given.
34 #define NEW_SESSION_TEMPLATE "#{session_name}:"
36 static enum cmd_retval
cmd_new_session_exec(struct cmd
*, struct cmdq_item
*);
38 const struct cmd_entry cmd_new_session_entry
= {
39 .name
= "new-session",
42 .args
= { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL
},
43 .usage
= "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
44 "[-f flags] [-n window-name] [-s session-name] "
45 CMD_TARGET_SESSION_USAGE
" [-x width] [-y height] "
48 .target
= { 't', CMD_FIND_SESSION
, CMD_FIND_CANFAIL
},
50 .flags
= CMD_STARTSERVER
,
51 .exec
= cmd_new_session_exec
54 const struct cmd_entry cmd_has_session_entry
= {
55 .name
= "has-session",
58 .args
= { "t:", 0, 0, NULL
},
59 .usage
= CMD_TARGET_SESSION_USAGE
,
61 .target
= { 't', CMD_FIND_SESSION
, 0 },
64 .exec
= cmd_new_session_exec
67 static enum cmd_retval
68 cmd_new_session_exec(struct cmd
*self
, struct cmdq_item
*item
)
70 struct args
*args
= cmd_get_args(self
);
71 struct cmd_find_state
*current
= cmdq_get_current(item
);
72 struct cmd_find_state
*target
= cmdq_get_target(item
);
73 struct client
*c
= cmdq_get_client(item
);
74 struct session
*s
, *as
, *groupwith
= NULL
;
77 struct termios tio
, *tiop
;
78 struct session_group
*sg
= NULL
;
79 const char *errstr
, *template, *group
, *tmp
;
80 char *cause
, *cwd
= NULL
, *cp
, *newname
= NULL
;
81 char *name
, *prefix
= NULL
;
82 int detached
, already_attached
, is_control
= 0;
83 u_int sx
, sy
, dsx
, dsy
, count
= args_count(args
);
84 struct spawn_context sc
= { 0 };
85 enum cmd_retval retval
;
86 struct cmd_find_state fs
;
87 struct args_value
*av
;
89 if (cmd_get_entry(self
) == &cmd_has_session_entry
) {
91 * cmd_find_target() will fail if the session cannot be found,
92 * so always return success here.
94 return (CMD_RETURN_NORMAL
);
97 if (args_has(args
, 't') && (count
!= 0 || args_has(args
, 'n'))) {
98 cmdq_error(item
, "command or window name given with target");
99 return (CMD_RETURN_ERROR
);
102 tmp
= args_get(args
, 's');
104 name
= format_single(item
, tmp
, c
, NULL
, NULL
, NULL
);
105 newname
= session_check_name(name
);
106 if (newname
== NULL
) {
107 cmdq_error(item
, "invalid session: %s", name
);
109 return (CMD_RETURN_ERROR
);
113 if (args_has(args
, 'A')) {
115 as
= session_find(newname
);
119 retval
= cmd_attach_session(item
, as
->name
,
120 args_has(args
, 'D'), args_has(args
, 'X'), 0, NULL
,
121 args_has(args
, 'E'), args_get(args
, 'f'));
126 if (newname
!= NULL
&& session_find(newname
) != NULL
) {
127 cmdq_error(item
, "duplicate session: %s", newname
);
131 /* Is this going to be part of a session group? */
132 group
= args_get(args
, 't');
134 groupwith
= target
->s
;
135 if (groupwith
== NULL
)
136 sg
= session_group_find(group
);
138 sg
= session_group_contains(groupwith
);
140 prefix
= xstrdup(sg
->name
);
141 else if (groupwith
!= NULL
)
142 prefix
= xstrdup(groupwith
->name
);
144 prefix
= session_check_name(group
);
145 if (prefix
== NULL
) {
146 cmdq_error(item
, "invalid session group: %s",
153 /* Set -d if no client. */
154 detached
= args_has(args
, 'd');
157 else if (c
->flags
& CLIENT_CONTROL
)
160 /* Is this client already attached? */
161 already_attached
= 0;
162 if (c
!= NULL
&& c
->session
!= NULL
)
163 already_attached
= 1;
165 /* Get the new session working directory. */
166 if ((tmp
= args_get(args
, 'c')) != NULL
)
167 cwd
= format_single(item
, tmp
, c
, NULL
, NULL
, NULL
);
169 cwd
= xstrdup(server_client_get_cwd(c
, NULL
));
172 * If this is a new client, check for nesting and save the termios
173 * settings (part of which is used for new windows in this session).
175 * tcgetattr() is used rather than using tty.tio since if the client is
176 * detached, tty_open won't be called. It must be done before opening
177 * the terminal as that calls tcsetattr() to prepare for tmux taking
183 (~c
->flags
& CLIENT_CONTROL
)) {
184 if (server_client_check_nested(cmdq_get_client(item
))) {
185 cmdq_error(item
, "sessions should be nested with care, "
186 "unset $TMUX to force");
189 if (tcgetattr(c
->fd
, &tio
) != 0)
190 fatal("tcgetattr failed");
195 /* Open the terminal if necessary. */
196 if (!detached
&& !already_attached
) {
197 if (server_client_open(c
, &cause
) != 0) {
198 cmdq_error(item
, "open terminal failed: %s", cause
);
204 /* Get default session size. */
205 if (args_has(args
, 'x')) {
206 tmp
= args_get(args
, 'x');
207 if (strcmp(tmp
, "-") == 0) {
213 dsx
= strtonum(tmp
, 1, USHRT_MAX
, &errstr
);
214 if (errstr
!= NULL
) {
215 cmdq_error(item
, "width %s", errstr
);
221 if (args_has(args
, 'y')) {
222 tmp
= args_get(args
, 'y');
223 if (strcmp(tmp
, "-") == 0) {
229 dsy
= strtonum(tmp
, 1, USHRT_MAX
, &errstr
);
230 if (errstr
!= NULL
) {
231 cmdq_error(item
, "height %s", errstr
);
238 /* Find new session size. */
239 if (!detached
&& !is_control
) {
242 if (sy
> 0 && options_get_number(global_s_options
, "status"))
245 tmp
= options_get_string(global_s_options
, "default-size");
246 if (sscanf(tmp
, "%ux%u", &sx
, &sy
) != 2) {
250 if (args_has(args
, 'x'))
252 if (args_has(args
, 'y'))
261 /* Create the new session. */
262 oo
= options_create(global_s_options
);
263 if (args_has(args
, 'x') || args_has(args
, 'y')) {
264 if (!args_has(args
, 'x'))
266 if (!args_has(args
, 'y'))
268 options_set_string(oo
, "default-size", 0, "%ux%u", dsx
, dsy
);
270 env
= environ_create();
271 if (c
!= NULL
&& !args_has(args
, 'E'))
272 environ_update(global_s_options
, c
->environ
, env
);
273 av
= args_first_value(args
, 'e');
275 environ_put(env
, av
->string
, 0);
276 av
= args_next_value(av
);
278 s
= session_create(prefix
, newname
, cwd
, env
, oo
, tiop
);
280 /* Spawn the initial window. */
286 sc
.name
= args_get(args
, 'n');
287 args_to_vector(args
, &sc
.argc
, &sc
.argv
);
290 sc
.cwd
= args_get(args
, 'c');
294 if (spawn_window(&sc
, &cause
) == NULL
) {
295 session_destroy(s
, 0, __func__
);
296 cmdq_error(item
, "create window failed: %s", cause
);
302 * If a target session is given, this is to be part of a session group,
303 * so add it to the group and synchronize.
307 if (groupwith
!= NULL
) {
308 sg
= session_group_new(groupwith
->name
);
309 session_group_add(sg
, groupwith
);
311 sg
= session_group_new(group
);
313 session_group_add(sg
, s
);
314 session_group_synchronize_to(s
);
315 session_select(s
, RB_MIN(winlinks
, &s
->windows
)->idx
);
317 notify_session("session-created", s
);
320 * Set the client to the new session. If a command client exists, it is
321 * taking this session and needs to get MSG_READY and stay around.
324 if (args_has(args
, 'f'))
325 server_client_set_flags(c
, args_get(args
, 'f'));
326 if (!already_attached
) {
327 if (~c
->flags
& CLIENT_CONTROL
)
328 proc_send(c
->peer
, MSG_READY
, -1, NULL
, 0);
329 } else if (c
->session
!= NULL
)
330 c
->last_session
= c
->session
;
331 server_client_set_session(c
, s
);
332 if (~cmdq_get_flags(item
) & CMDQ_STATE_REPEAT
)
333 server_client_set_key_table(c
, NULL
);
336 /* Print if requested. */
337 if (args_has(args
, 'P')) {
338 if ((template = args_get(args
, 'F')) == NULL
)
339 template = NEW_SESSION_TEMPLATE
;
340 cp
= format_single(item
, template, c
, s
, s
->curw
, NULL
);
341 cmdq_print(item
, "%s", cp
);
346 c
->flags
|= CLIENT_ATTACHED
;
347 if (!args_has(args
, 'd'))
348 cmd_find_from_session(current
, s
, 0);
350 cmd_find_from_session(&fs
, s
, 0);
351 cmdq_insert_hook(s
, item
, &fs
, "after-new-session");
357 cmd_free_argv(sc
.argc
, sc
.argv
);
361 return (CMD_RETURN_NORMAL
);
365 cmd_free_argv(sc
.argc
, sc
.argv
);
369 return (CMD_RETURN_ERROR
);