1 /* $Id: cmd-new-session.c,v 1.1.1.1 2011/03/10 09:15:37 jmmv 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 <pwd.h> 22 #include <string.h> 23 #include <termios.h> 24 #include <unistd.h> 25 26 #include "tmux.h" 27 28 /* 29 * Create a new session and attach to the current terminal unless -d is given. 30 */ 31 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); 37 38 struct cmd_new_session_data { 39 char *target; 40 char *newname; 41 char *winname; 42 char *cmd; 43 int flag_detached; 44 }; 45 46 const struct cmd_entry cmd_new_session_entry = { 47 "new-session", "new", 48 "[-d] [-n window-name] [-s session-name] [-t target-session] [command]", 49 CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, "", 50 cmd_new_session_init, 51 cmd_new_session_parse, 52 cmd_new_session_exec, 53 cmd_new_session_free, 54 cmd_new_session_print 55 }; 56 57 /* ARGSUSED */ 58 void 59 cmd_new_session_init(struct cmd *self, unused int arg) 60 { 61 struct cmd_new_session_data *data; 62 63 self->data = data = xmalloc(sizeof *data); 64 data->flag_detached = 0; 65 data->target = NULL; 66 data->newname = NULL; 67 data->winname = NULL; 68 data->cmd = NULL; 69 } 70 71 int 72 cmd_new_session_parse(struct cmd *self, int argc, char **argv, char **cause) 73 { 74 struct cmd_new_session_data *data; 75 int opt; 76 77 self->entry->init(self, KEYC_NONE); 78 data = self->data; 79 80 while ((opt = getopt(argc, argv, "ds:t:n:")) != -1) { 81 switch (opt) { 82 case 'd': 83 data->flag_detached = 1; 84 break; 85 case 's': 86 if (data->newname == NULL) 87 data->newname = xstrdup(optarg); 88 break; 89 case 't': 90 if (data->target == NULL) 91 data->target = xstrdup(optarg); 92 break; 93 case 'n': 94 if (data->winname == NULL) 95 data->winname = xstrdup(optarg); 96 break; 97 default: 98 goto usage; 99 } 100 } 101 argc -= optind; 102 argv += optind; 103 if (argc != 0 && argc != 1) 104 goto usage; 105 106 if (data->target != NULL && (argc == 1 || data->winname != NULL)) 107 goto usage; 108 109 if (argc == 1) 110 data->cmd = xstrdup(argv[0]); 111 112 return (0); 113 114 usage: 115 xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); 116 117 self->entry->free(self); 118 return (-1); 119 } 120 121 int 122 cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) 123 { 124 struct cmd_new_session_data *data = self->data; 125 struct session *s, *old_s, *groupwith; 126 struct window *w; 127 struct window_pane *wp; 128 struct environ env; 129 struct termios tio, *tiop; 130 struct passwd *pw; 131 const char *update, *cwd; 132 char *overrides, *cmd, *cause; 133 int detached, idx; 134 u_int sx, sy, i; 135 136 if (data->newname != NULL && session_find(data->newname) != NULL) { 137 ctx->error(ctx, "duplicate session: %s", data->newname); 138 return (-1); 139 } 140 141 groupwith = NULL; 142 if (data->target != NULL && 143 (groupwith = cmd_find_session(ctx, data->target)) == NULL) 144 return (-1); 145 146 /* 147 * There are three cases: 148 * 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. 153 * 154 * 2. If cmdclient is NULL, new-session has been called from an 155 * existing client (such as a key binding). 156 * 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. 159 * 160 * In all cases, a new additional session needs to be created and 161 * (unless -d) set as the current session for the client. 162 */ 163 164 /* Set -d if no client. */ 165 detached = data->flag_detached; 166 if (ctx->cmdclient == NULL && ctx->curclient == NULL) 167 detached = 1; 168 169 /* 170 * Save the termios settings, part of which is used for new windows in 171 * this session. 172 * 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 176 * tmux taking over. 177 */ 178 if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) { 179 if (tcgetattr(ctx->cmdclient->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 && ctx->cmdclient != NULL) { 187 if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { 188 ctx->error(ctx, "not a terminal"); 189 return (-1); 190 } 191 192 overrides = 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); 196 xfree(cause); 197 return (-1); 198 } 199 } 200 201 /* Get the new session working directory. */ 202 if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) 203 cwd = ctx->cmdclient->cwd; 204 else { 205 pw = getpwuid(getuid()); 206 if (pw->pw_dir != NULL && *pw->pw_dir != '\0') 207 cwd = pw->pw_dir; 208 else 209 cwd = "/"; 210 } 211 212 /* Find new session size. */ 213 if (detached) { 214 sx = 80; 215 sy = 24; 216 } else if (ctx->cmdclient != NULL) { 217 sx = ctx->cmdclient->tty.sx; 218 sy = ctx->cmdclient->tty.sy; 219 } else { 220 sx = ctx->curclient->tty.sx; 221 sy = ctx->curclient->tty.sy; 222 } 223 if (sy > 0 && options_get_number(&global_s_options, "status")) 224 sy--; 225 if (sx == 0) 226 sx = 1; 227 if (sy == 0) 228 sy = 1; 229 230 /* Figure out the command for the new window. */ 231 if (data->target != NULL) 232 cmd = NULL; 233 else if (data->cmd != NULL) 234 cmd = data->cmd; 235 else 236 cmd = options_get_string(&global_s_options, "default-command"); 237 238 /* Construct the environment. */ 239 environ_init(&env); 240 update = options_get_string(&global_s_options, "update-environment"); 241 if (ctx->cmdclient != NULL) 242 environ_update(update, &ctx->cmdclient->environ, &env); 243 244 /* Create the new session. */ 245 idx = -1 - options_get_number(&global_s_options, "base-index"); 246 s = session_create( 247 data->newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); 248 if (s == NULL) { 249 ctx->error(ctx, "create session failed: %s", cause); 250 xfree(cause); 251 return (-1); 252 } 253 environ_free(&env); 254 255 /* Set the initial window name if one given. */ 256 if (cmd != NULL && data->winname != NULL) { 257 w = s->curw->window; 258 259 xfree(w->name); 260 w->name = xstrdup(data->winname); 261 262 options_set_number(&w->options, "automatic-rename", 0); 263 } 264 265 /* 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. 268 */ 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); 273 } 274 275 /* 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. 278 */ 279 if (!detached) { 280 if (ctx->cmdclient != NULL) { 281 server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); 282 283 old_s = ctx->cmdclient->session; 284 if (old_s != NULL) 285 ctx->cmdclient->last_session = old_s; 286 ctx->cmdclient->session = s; 287 server_redraw_client(ctx->cmdclient); 288 } else { 289 old_s = ctx->curclient->session; 290 if (old_s != NULL) 291 ctx->curclient->last_session = old_s; 292 ctx->curclient->session = s; 293 server_redraw_client(ctx->curclient); 294 } 295 } 296 recalculate_sizes(); 297 server_update_socket(); 298 299 /* 300 * If there are still configuration file errors to display, put the new 301 * session's current window into more mode and display them now. 302 */ 303 if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) { 304 wp = s->curw->window->active; 305 window_pane_set_mode(wp, &window_copy_mode); 306 window_copy_init_for_output(wp); 307 for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { 308 cause = ARRAY_ITEM(&cfg_causes, i); 309 window_copy_add(wp, "%s", cause); 310 xfree(cause); 311 } 312 ARRAY_FREE(&cfg_causes); 313 } 314 315 return (!detached); /* 1 means don't tell command client to exit */ 316 } 317 318 void 319 cmd_new_session_free(struct cmd *self) 320 { 321 struct cmd_new_session_data *data = self->data; 322 323 if (data->newname != NULL) 324 xfree(data->newname); 325 if (data->winname != NULL) 326 xfree(data->winname); 327 if (data->cmd != NULL) 328 xfree(data->cmd); 329 xfree(data); 330 } 331 332 size_t 333 cmd_new_session_print(struct cmd *self, char *buf, size_t len) 334 { 335 struct cmd_new_session_data *data = self->data; 336 size_t off = 0; 337 338 off += xsnprintf(buf, len, "%s", self->entry->name); 339 if (data == NULL) 340 return (off); 341 if (off < len && data->flag_detached) 342 off += xsnprintf(buf + off, len - off, " -d"); 343 if (off < len && data->winname != NULL) 344 off += cmd_prarg(buf + off, len - off, " -n ", data->winname); 345 if (off < len && data->newname != NULL) 346 off += cmd_prarg(buf + off, len - off, " -s ", data->newname); 347 if (off < len && data->target != NULL) 348 off += cmd_prarg(buf + off, len - off, " -t ", data->target); 349 if (off < len && data->cmd != NULL) 350 off += cmd_prarg(buf + off, len - off, " ", data->cmd); 351 return (off); 352 } 353