1 /* $OpenBSD: cmd-new-session.c,v 1.21 2009/10/10 10:02:48 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 <string.h> 22 #include <termios.h> 23 24 #include "tmux.h" 25 26 /* 27 * Create a new session and attach to the current terminal unless -d is given. 28 */ 29 30 int cmd_new_session_parse(struct cmd *, int, char **, char **); 31 int cmd_new_session_exec(struct cmd *, struct cmd_ctx *); 32 void cmd_new_session_free(struct cmd *); 33 void cmd_new_session_init(struct cmd *, int); 34 size_t cmd_new_session_print(struct cmd *, char *, size_t); 35 36 struct cmd_new_session_data { 37 char *target; 38 char *newname; 39 char *winname; 40 char *cmd; 41 int flag_detached; 42 }; 43 44 const struct cmd_entry cmd_new_session_entry = { 45 "new-session", "new", 46 "[-d] [-n window-name] [-s session-name] [-t target-session] [command]", 47 CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, 0, 48 cmd_new_session_init, 49 cmd_new_session_parse, 50 cmd_new_session_exec, 51 cmd_new_session_free, 52 cmd_new_session_print 53 }; 54 55 void 56 cmd_new_session_init(struct cmd *self, unused int arg) 57 { 58 struct cmd_new_session_data *data; 59 60 self->data = data = xmalloc(sizeof *data); 61 data->flag_detached = 0; 62 data->target = NULL; 63 data->newname = NULL; 64 data->winname = NULL; 65 data->cmd = NULL; 66 } 67 68 int 69 cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause) 70 { 71 struct cmd_new_session_data *data; 72 int opt; 73 74 self->entry->init(self, KEYC_NONE); 75 data = self->data; 76 77 while ((opt = getopt(argc, argv, "ds:t:n:")) != -1) { 78 switch (opt) { 79 case 'd': 80 data->flag_detached = 1; 81 break; 82 case 's': 83 if (data->newname == NULL) 84 data->newname = xstrdup(optarg); 85 break; 86 case 't': 87 if (data->target == NULL) 88 data->target = xstrdup(optarg); 89 break; 90 case 'n': 91 if (data->winname == NULL) 92 data->winname = xstrdup(optarg); 93 break; 94 default: 95 goto usage; 96 } 97 } 98 argc -= optind; 99 argv += optind; 100 if (argc != 0 && argc != 1) 101 goto usage; 102 103 if (data->target != NULL && (argc == 1 || data->winname != NULL)) 104 goto usage; 105 106 if (argc == 1) 107 data->cmd = xstrdup(argv[0]); 108 109 return (0); 110 111 usage: 112 xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); 113 114 self->entry->free(self); 115 return (-1); 116 } 117 118 int 119 cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) 120 { 121 struct cmd_new_session_data *data = self->data; 122 struct session *s, *groupwith; 123 struct window *w; 124 struct environ env; 125 struct termios tio, *tiop; 126 const char *update; 127 char *overrides, *cmd, *cwd, *cause; 128 int detached, idx; 129 u_int sx, sy; 130 131 if (data->newname != NULL && session_find(data->newname) != NULL) { 132 ctx->error(ctx, "duplicate session: %s", data->newname); 133 return (-1); 134 } 135 136 groupwith = NULL; 137 if (data->target != NULL && 138 (groupwith = cmd_find_session(ctx, data->target)) == NULL) 139 return (-1); 140 141 /* 142 * There are three cases: 143 * 144 * 1. If cmdclient is non-NULL, new-session has been called from the 145 * command-line - cmdclient is to become a new attached, interactive 146 * client. Unless -d is given, the terminal must be opened and then 147 * the client sent MSG_READY. 148 * 149 * 2. If cmdclient is NULL, new-session has been called from an 150 * existing client (such as a key binding). 151 * 152 * 3. Both are NULL, the command was in the configuration file. Treat 153 * this as if -d was given even if it was not. 154 * 155 * In all cases, a new additional session needs to be created and 156 * (unless -d) set as the current session for the client. 157 */ 158 159 /* Set -d if no client. */ 160 detached = data->flag_detached; 161 if (ctx->cmdclient == NULL && ctx->curclient == NULL) 162 detached = 1; 163 164 /* 165 * Save the termios settings, part of which is used for new windows in 166 * this session. 167 * 168 * This is read again with tcgetattr() rather than using tty.tio as if 169 * detached, tty_open won't be called. Because of this, it must be done 170 * before opening the terminal as that calls tcsetattr() to prepare for 171 * tmux taking over. 172 */ 173 if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) { 174 if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0) 175 fatal("tcgetattr failed"); 176 tiop = &tio; 177 } else 178 tiop = NULL; 179 180 /* Open the terminal if necessary. */ 181 if (!detached && ctx->cmdclient != NULL) { 182 if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { 183 ctx->error(ctx, "not a terminal"); 184 return (-1); 185 } 186 187 overrides = 188 options_get_string(&global_s_options, "terminal-overrides"); 189 if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { 190 ctx->error(ctx, "open terminal failed: %s", cause); 191 xfree(cause); 192 return (-1); 193 } 194 } 195 196 /* Get the new session working directory. */ 197 if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) 198 cwd = ctx->cmdclient->cwd; 199 else 200 cwd = options_get_string(&global_s_options, "default-path"); 201 202 /* Find new session size. */ 203 if (detached) { 204 sx = 80; 205 sy = 24; 206 } else if (ctx->cmdclient != NULL) { 207 sx = ctx->cmdclient->tty.sx; 208 sy = ctx->cmdclient->tty.sy; 209 } else { 210 sx = ctx->curclient->tty.sx; 211 sy = ctx->curclient->tty.sy; 212 } 213 if (sy > 0 && options_get_number(&global_s_options, "status")) 214 sy--; 215 if (sx == 0) 216 sx = 1; 217 if (sy == 0) 218 sy = 1; 219 220 /* Figure out the command for the new window. */ 221 if (data->target != NULL) 222 cmd = NULL; 223 else if (data->cmd != NULL) 224 cmd = data->cmd; 225 else 226 cmd = options_get_string(&global_s_options, "default-command"); 227 228 /* Construct the environment. */ 229 environ_init(&env); 230 update = options_get_string(&global_s_options, "update-environment"); 231 if (ctx->cmdclient != NULL) 232 environ_update(update, &ctx->cmdclient->environ, &env); 233 234 /* Create the new session. */ 235 idx = -1 - options_get_number(&global_s_options, "base-index"); 236 s = session_create( 237 data->newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); 238 if (s == NULL) { 239 ctx->error(ctx, "create session failed: %s", cause); 240 xfree(cause); 241 return (-1); 242 } 243 environ_free(&env); 244 245 /* Set the initial window name if one given. */ 246 if (cmd != NULL && data->winname != NULL) { 247 w = s->curw->window; 248 249 xfree(w->name); 250 w->name = xstrdup(data->winname); 251 252 options_set_number(&w->options, "automatic-rename", 0); 253 } 254 255 /* 256 * If a target session is given, this is to be part of a session group, 257 * so add it to the group and synchronize. 258 */ 259 if (groupwith != NULL) { 260 session_group_add(groupwith, s); 261 session_group_synchronize_to(s); 262 session_select(s, RB_ROOT(&s->windows)->idx); 263 } 264 265 /* 266 * Set the client to the new session. If a command client exists, it is 267 * taking this session and needs to get MSG_READY and stay around. 268 */ 269 if (!detached) { 270 if (ctx->cmdclient != NULL) { 271 server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); 272 ctx->cmdclient->session = s; 273 server_redraw_client(ctx->cmdclient); 274 } else { 275 ctx->curclient->session = s; 276 server_redraw_client(ctx->curclient); 277 } 278 } 279 recalculate_sizes(); 280 281 return (!detached); /* 1 means don't tell command client to exit */ 282 } 283 284 void 285 cmd_new_session_free(struct cmd *self) 286 { 287 struct cmd_new_session_data *data = self->data; 288 289 if (data->newname != NULL) 290 xfree(data->newname); 291 if (data->winname != NULL) 292 xfree(data->winname); 293 if (data->cmd != NULL) 294 xfree(data->cmd); 295 xfree(data); 296 } 297 298 size_t 299 cmd_new_session_print(struct cmd *self, char *buf, size_t len) 300 { 301 struct cmd_new_session_data *data = self->data; 302 size_t off = 0; 303 304 off += xsnprintf(buf, len, "%s", self->entry->name); 305 if (data == NULL) 306 return (off); 307 if (off < len && data->flag_detached) 308 off += xsnprintf(buf + off, len - off, " -d"); 309 if (off < len && data->newname != NULL) 310 off += cmd_prarg(buf + off, len - off, " -s ", data->newname); 311 if (off < len && data->winname != NULL) 312 off += cmd_prarg(buf + off, len - off, " -n ", data->winname); 313 if (off < len && data->cmd != NULL) 314 off += cmd_prarg(buf + off, len - off, " ", data->cmd); 315 return (off); 316 } 317