4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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>
29 * Create a new session and attach to the current terminal unless -d is given.
32 int cmd_new_session_parse(struct cmd
*, int, char **, char **);
33 int cmd_new_session_exec(struct cmd
*, struct cmd_ctx
*);
34 void cmd_new_session_free(struct cmd
*);
35 void cmd_new_session_init(struct cmd
*, int);
36 size_t cmd_new_session_print(struct cmd
*, char *, size_t);
38 struct cmd_new_session_data
{
46 const struct cmd_entry cmd_new_session_entry
= {
48 "[-d] [-n window-name] [-s session-name] [-t target-session] [command]",
49 CMD_STARTSERVER
|CMD_CANTNEST
|CMD_SENDENVIRON
, "",
51 cmd_new_session_parse
,
59 cmd_new_session_init(struct cmd
*self
, unused
int arg
)
61 struct cmd_new_session_data
*data
;
63 self
->data
= data
= xmalloc(sizeof *data
);
64 data
->flag_detached
= 0;
72 cmd_new_session_parse(struct cmd
*self
, int argc
, char **argv
, char **cause
)
74 struct cmd_new_session_data
*data
;
77 self
->entry
->init(self
, KEYC_NONE
);
80 while ((opt
= getopt(argc
, argv
, "ds:t:n:")) != -1) {
83 data
->flag_detached
= 1;
86 if (data
->newname
== NULL
)
87 data
->newname
= xstrdup(optarg
);
90 if (data
->target
== NULL
)
91 data
->target
= xstrdup(optarg
);
94 if (data
->winname
== NULL
)
95 data
->winname
= xstrdup(optarg
);
103 if (argc
!= 0 && argc
!= 1)
106 if (data
->target
!= NULL
&& (argc
== 1 || data
->winname
!= NULL
))
110 data
->cmd
= xstrdup(argv
[0]);
115 xasprintf(cause
, "usage: %s %s", self
->entry
->name
, self
->entry
->usage
);
117 self
->entry
->free(self
);
122 cmd_new_session_exec(struct cmd
*self
, struct cmd_ctx
*ctx
)
124 struct cmd_new_session_data
*data
= self
->data
;
125 struct session
*s
, *groupwith
;
127 struct window_pane
*wp
;
129 struct termios tio
, *tiop
;
131 const char *update
, *cwd
;
132 char *overrides
, *cmd
, *cause
;
136 if (data
->newname
!= NULL
&& session_find(data
->newname
) != NULL
) {
137 ctx
->error(ctx
, "duplicate session: %s", data
->newname
);
142 if (data
->target
!= NULL
&&
143 (groupwith
= cmd_find_session(ctx
, data
->target
)) == NULL
)
147 * There are three cases:
149 * 1. If cmdclient is non-NULL, new-session has been called from the
150 * command-line - cmdclient is to become a new attached, interactive
151 * client. Unless -d is given, the terminal must be opened and then
152 * the client sent MSG_READY.
154 * 2. If cmdclient is NULL, new-session has been called from an
155 * existing client (such as a key binding).
157 * 3. Both are NULL, the command was in the configuration file. Treat
158 * this as if -d was given even if it was not.
160 * In all cases, a new additional session needs to be created and
161 * (unless -d) set as the current session for the client.
164 /* Set -d if no client. */
165 detached
= data
->flag_detached
;
166 if (ctx
->cmdclient
== NULL
&& ctx
->curclient
== NULL
)
170 * Save the termios settings, part of which is used for new windows in
173 * This is read again with tcgetattr() rather than using tty.tio as if
174 * detached, tty_open won't be called. Because of this, it must be done
175 * before opening the terminal as that calls tcsetattr() to prepare for
178 if (ctx
->cmdclient
!= NULL
&& ctx
->cmdclient
->tty
.fd
!= -1) {
179 if (tcgetattr(ctx
->cmdclient
->tty
.fd
, &tio
) != 0)
180 fatal("tcgetattr failed");
185 /* Open the terminal if necessary. */
186 if (!detached
&& ctx
->cmdclient
!= NULL
) {
187 if (!(ctx
->cmdclient
->flags
& CLIENT_TERMINAL
)) {
188 ctx
->error(ctx
, "not a terminal");
193 options_get_string(&global_s_options
, "terminal-overrides");
194 if (tty_open(&ctx
->cmdclient
->tty
, overrides
, &cause
) != 0) {
195 ctx
->error(ctx
, "open terminal failed: %s", cause
);
201 /* Get the new session working directory. */
202 if (ctx
->cmdclient
!= NULL
&& ctx
->cmdclient
->cwd
!= NULL
)
203 cwd
= ctx
->cmdclient
->cwd
;
205 pw
= getpwuid(getuid());
206 if (pw
->pw_dir
!= NULL
&& *pw
->pw_dir
!= '\0')
212 /* Find new session size. */
216 } else if (ctx
->cmdclient
!= NULL
) {
217 sx
= ctx
->cmdclient
->tty
.sx
;
218 sy
= ctx
->cmdclient
->tty
.sy
;
220 sx
= ctx
->curclient
->tty
.sx
;
221 sy
= ctx
->curclient
->tty
.sy
;
223 if (sy
> 0 && options_get_number(&global_s_options
, "status"))
230 /* Figure out the command for the new window. */
231 if (data
->target
!= NULL
)
233 else if (data
->cmd
!= NULL
)
236 cmd
= options_get_string(&global_s_options
, "default-command");
238 /* Construct the environment. */
240 update
= options_get_string(&global_s_options
, "update-environment");
241 if (ctx
->cmdclient
!= NULL
)
242 environ_update(update
, &ctx
->cmdclient
->environ
, &env
);
244 /* Create the new session. */
245 idx
= -1 - options_get_number(&global_s_options
, "base-index");
247 data
->newname
, cmd
, cwd
, &env
, tiop
, idx
, sx
, sy
, &cause
);
249 ctx
->error(ctx
, "create session failed: %s", cause
);
255 /* Set the initial window name if one given. */
256 if (cmd
!= NULL
&& data
->winname
!= NULL
) {
260 w
->name
= xstrdup(data
->winname
);
262 options_set_number(&w
->options
, "automatic-rename", 0);
266 * If a target session is given, this is to be part of a session group,
267 * so add it to the group and synchronize.
269 if (groupwith
!= NULL
) {
270 session_group_add(groupwith
, s
);
271 session_group_synchronize_to(s
);
272 session_select(s
, RB_ROOT(&s
->windows
)->idx
);
276 * Set the client to the new session. If a command client exists, it is
277 * taking this session and needs to get MSG_READY and stay around.
280 if (ctx
->cmdclient
!= NULL
) {
281 server_write_client(ctx
->cmdclient
, MSG_READY
, NULL
, 0);
282 ctx
->cmdclient
->session
= s
;
283 server_redraw_client(ctx
->cmdclient
);
285 ctx
->curclient
->session
= s
;
286 server_redraw_client(ctx
->curclient
);
290 server_update_socket();
293 * If there are still configuration file errors to display, put the new
294 * session's current window into more mode and display them now.
296 if (cfg_finished
&& !ARRAY_EMPTY(&cfg_causes
)) {
297 wp
= s
->curw
->window
->active
;
298 window_pane_set_mode(wp
, &window_copy_mode
);
299 window_copy_init_for_output(wp
);
300 for (i
= 0; i
< ARRAY_LENGTH(&cfg_causes
); i
++) {
301 cause
= ARRAY_ITEM(&cfg_causes
, i
);
302 window_copy_add(wp
, "%s", cause
);
305 ARRAY_FREE(&cfg_causes
);
308 return (!detached
); /* 1 means don't tell command client to exit */
312 cmd_new_session_free(struct cmd
*self
)
314 struct cmd_new_session_data
*data
= self
->data
;
316 if (data
->newname
!= NULL
)
317 xfree(data
->newname
);
318 if (data
->winname
!= NULL
)
319 xfree(data
->winname
);
320 if (data
->cmd
!= NULL
)
326 cmd_new_session_print(struct cmd
*self
, char *buf
, size_t len
)
328 struct cmd_new_session_data
*data
= self
->data
;
331 off
+= xsnprintf(buf
, len
, "%s", self
->entry
->name
);
334 if (off
< len
&& data
->flag_detached
)
335 off
+= xsnprintf(buf
+ off
, len
- off
, " -d");
336 if (off
< len
&& data
->winname
!= NULL
)
337 off
+= cmd_prarg(buf
+ off
, len
- off
, " -n ", data
->winname
);
338 if (off
< len
&& data
->newname
!= NULL
)
339 off
+= cmd_prarg(buf
+ off
, len
- off
, " -s ", data
->newname
);
340 if (off
< len
&& data
->target
!= NULL
)
341 off
+= cmd_prarg(buf
+ off
, len
- off
, " -t ", data
->target
);
342 if (off
< len
&& data
->cmd
!= NULL
)
343 off
+= cmd_prarg(buf
+ off
, len
- off
, " ", data
->cmd
);