| File: | src/usr.bin/tmux/spawn.c |
| Warning: | line 183, column 8 Access to field 'name' results in a dereference of a null pointer (loaded from variable 'w') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: spawn.c,v 1.33 2023/07/10 09:24:53 nicm Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2019 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(*__errno()).h> | |||
| 22 | #include <paths.h> | |||
| 23 | #include <signal.h> | |||
| 24 | #include <stdlib.h> | |||
| 25 | #include <string.h> | |||
| 26 | #include <unistd.h> | |||
| 27 | #include <util.h> | |||
| 28 | ||||
| 29 | #include "tmux.h" | |||
| 30 | ||||
| 31 | /* | |||
| 32 | * Set up the environment and create a new window and pane or a new pane. | |||
| 33 | * | |||
| 34 | * We need to set up the following items: | |||
| 35 | * | |||
| 36 | * - history limit, comes from the session; | |||
| 37 | * | |||
| 38 | * - base index, comes from the session; | |||
| 39 | * | |||
| 40 | * - current working directory, may be specified - if it isn't it comes from | |||
| 41 | * either the client or the session; | |||
| 42 | * | |||
| 43 | * - PATH variable, comes from the client if any, otherwise from the session | |||
| 44 | * environment; | |||
| 45 | * | |||
| 46 | * - shell, comes from default-shell; | |||
| 47 | * | |||
| 48 | * - termios, comes from the session; | |||
| 49 | * | |||
| 50 | * - remaining environment, comes from the session. | |||
| 51 | */ | |||
| 52 | ||||
| 53 | static void | |||
| 54 | spawn_log(const char *from, struct spawn_context *sc) | |||
| 55 | { | |||
| 56 | struct session *s = sc->s; | |||
| 57 | struct winlink *wl = sc->wl; | |||
| 58 | struct window_pane *wp0 = sc->wp0; | |||
| 59 | const char *name = cmdq_get_name(sc->item); | |||
| 60 | char tmp[128]; | |||
| 61 | ||||
| 62 | log_debug("%s: %s, flags=%#x", from, name, sc->flags); | |||
| 63 | ||||
| 64 | if (wl != NULL((void *)0) && wp0 != NULL((void *)0)) | |||
| 65 | xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); | |||
| 66 | else if (wl != NULL((void *)0)) | |||
| 67 | xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx); | |||
| 68 | else if (wp0 != NULL((void *)0)) | |||
| 69 | xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id); | |||
| 70 | else | |||
| 71 | xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); | |||
| 72 | log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); | |||
| 73 | log_debug("%s: name=%s", from, sc->name == NULL((void *)0) ? "none" : sc->name); | |||
| 74 | } | |||
| 75 | ||||
| 76 | struct winlink * | |||
| 77 | spawn_window(struct spawn_context *sc, char **cause) | |||
| 78 | { | |||
| 79 | struct cmdq_item *item = sc->item; | |||
| 80 | struct client *c = cmdq_get_client(item); | |||
| 81 | struct session *s = sc->s; | |||
| 82 | struct window *w; | |||
| 83 | struct window_pane *wp; | |||
| 84 | struct winlink *wl; | |||
| 85 | int idx = sc->idx; | |||
| 86 | u_int sx, sy, xpixel, ypixel; | |||
| 87 | ||||
| 88 | spawn_log(__func__, sc); | |||
| 89 | ||||
| 90 | /* | |||
| 91 | * If the window already exists, we are respawning, so destroy all the | |||
| 92 | * panes except one. | |||
| 93 | */ | |||
| 94 | if (sc->flags & SPAWN_RESPAWN0x4) { | |||
| ||||
| 95 | w = sc->wl->window; | |||
| 96 | if (~sc->flags & SPAWN_KILL0x1) { | |||
| 97 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
| 98 | if (wp->fd != -1) | |||
| 99 | break; | |||
| 100 | } | |||
| 101 | if (wp != NULL((void *)0)) { | |||
| 102 | xasprintf(cause, "window %s:%d still active", | |||
| 103 | s->name, sc->wl->idx); | |||
| 104 | return (NULL((void *)0)); | |||
| 105 | } | |||
| 106 | } | |||
| 107 | ||||
| 108 | sc->wp0 = TAILQ_FIRST(&w->panes)((&w->panes)->tqh_first); | |||
| 109 | TAILQ_REMOVE(&w->panes, sc->wp0, entry)do { if (((sc->wp0)->entry.tqe_next) != ((void *)0)) (sc ->wp0)->entry.tqe_next->entry.tqe_prev = (sc->wp0 )->entry.tqe_prev; else (&w->panes)->tqh_last = ( sc->wp0)->entry.tqe_prev; *(sc->wp0)->entry.tqe_prev = (sc->wp0)->entry.tqe_next; ; ; } while (0); | |||
| 110 | ||||
| 111 | layout_free(w); | |||
| 112 | window_destroy_panes(w); | |||
| 113 | ||||
| 114 | TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry)do { if (((sc->wp0)->entry.tqe_next = (&w->panes )->tqh_first) != ((void *)0)) (&w->panes)->tqh_first ->entry.tqe_prev = &(sc->wp0)->entry.tqe_next; else (&w->panes)->tqh_last = &(sc->wp0)->entry .tqe_next; (&w->panes)->tqh_first = (sc->wp0); ( sc->wp0)->entry.tqe_prev = &(&w->panes)-> tqh_first; } while (0); | |||
| 115 | window_pane_resize(sc->wp0, w->sx, w->sy); | |||
| 116 | ||||
| 117 | layout_init(w, sc->wp0); | |||
| 118 | w->active = NULL((void *)0); | |||
| 119 | window_set_active_pane(w, sc->wp0, 0); | |||
| 120 | } | |||
| 121 | ||||
| 122 | /* | |||
| 123 | * Otherwise we have no window so we will need to create one. First | |||
| 124 | * check if the given index already exists and destroy it if so. | |||
| 125 | */ | |||
| 126 | if ((~sc->flags & SPAWN_RESPAWN0x4) && idx != -1) { | |||
| 127 | wl = winlink_find_by_index(&s->windows, idx); | |||
| 128 | if (wl != NULL((void *)0) && (~sc->flags & SPAWN_KILL0x1)) { | |||
| 129 | xasprintf(cause, "index %d in use", idx); | |||
| 130 | return (NULL((void *)0)); | |||
| 131 | } | |||
| 132 | if (wl != NULL((void *)0)) { | |||
| 133 | /* | |||
| 134 | * Can't use session_detach as it will destroy session | |||
| 135 | * if this makes it empty. | |||
| 136 | */ | |||
| 137 | wl->flags &= ~WINLINK_ALERTFLAGS(0x1|0x2|0x4); | |||
| 138 | notify_session_window("window-unlinked", s, wl->window); | |||
| 139 | winlink_stack_remove(&s->lastw, wl); | |||
| 140 | winlink_remove(&s->windows, wl); | |||
| 141 | ||||
| 142 | if (s->curw == wl) { | |||
| 143 | s->curw = NULL((void *)0); | |||
| 144 | sc->flags &= ~SPAWN_DETACHED0x2; | |||
| 145 | } | |||
| 146 | } | |||
| 147 | } | |||
| 148 | ||||
| 149 | /* Then create a window if needed. */ | |||
| 150 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
| 151 | if (idx == -1) | |||
| 152 | idx = -1 - options_get_number(s->options, "base-index"); | |||
| 153 | if ((sc->wl = winlink_add(&s->windows, idx)) == NULL((void *)0)) { | |||
| 154 | xasprintf(cause, "couldn't add window %d", idx); | |||
| 155 | return (NULL((void *)0)); | |||
| 156 | } | |||
| 157 | default_window_size(sc->tc, s, NULL((void *)0), &sx, &sy, &xpixel, &ypixel, | |||
| 158 | -1); | |||
| 159 | if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL((void *)0)) { | |||
| 160 | winlink_remove(&s->windows, sc->wl); | |||
| 161 | xasprintf(cause, "couldn't create window %d", idx); | |||
| 162 | return (NULL((void *)0)); | |||
| 163 | } | |||
| 164 | if (s->curw == NULL((void *)0)) | |||
| 165 | s->curw = sc->wl; | |||
| 166 | sc->wl->session = s; | |||
| 167 | w->latest = sc->tc; | |||
| 168 | winlink_set_window(sc->wl, w); | |||
| 169 | } else | |||
| 170 | w = NULL((void *)0); | |||
| 171 | sc->flags |= SPAWN_NONOTIFY0x10; | |||
| 172 | ||||
| 173 | /* Spawn the pane. */ | |||
| 174 | wp = spawn_pane(sc, cause); | |||
| 175 | if (wp == NULL((void *)0)) { | |||
| 176 | if (~sc->flags & SPAWN_RESPAWN0x4) | |||
| 177 | winlink_remove(&s->windows, sc->wl); | |||
| 178 | return (NULL((void *)0)); | |||
| 179 | } | |||
| 180 | ||||
| 181 | /* Set the name of the new window. */ | |||
| 182 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
| 183 | free(w->name); | |||
| ||||
| 184 | if (sc->name != NULL((void *)0)) { | |||
| 185 | w->name = format_single(item, sc->name, c, s, NULL((void *)0), | |||
| 186 | NULL((void *)0)); | |||
| 187 | options_set_number(w->options, "automatic-rename", 0); | |||
| 188 | } else | |||
| 189 | w->name = default_window_name(w); | |||
| 190 | } | |||
| 191 | ||||
| 192 | /* Switch to the new window if required. */ | |||
| 193 | if (~sc->flags & SPAWN_DETACHED0x2) | |||
| 194 | session_select(s, sc->wl->idx); | |||
| 195 | ||||
| 196 | /* Fire notification if new window. */ | |||
| 197 | if (~sc->flags & SPAWN_RESPAWN0x4) | |||
| 198 | notify_session_window("window-linked", s, w); | |||
| 199 | ||||
| 200 | session_group_synchronize_from(s); | |||
| 201 | return (sc->wl); | |||
| 202 | } | |||
| 203 | ||||
| 204 | struct window_pane * | |||
| 205 | spawn_pane(struct spawn_context *sc, char **cause) | |||
| 206 | { | |||
| 207 | struct cmdq_item *item = sc->item; | |||
| 208 | struct cmd_find_state *target = cmdq_get_target(item); | |||
| 209 | struct client *c = cmdq_get_client(item); | |||
| 210 | struct session *s = sc->s; | |||
| 211 | struct window *w = sc->wl->window; | |||
| 212 | struct window_pane *new_wp; | |||
| 213 | struct environ *child; | |||
| 214 | struct environ_entry *ee; | |||
| 215 | char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd; | |||
| 216 | const char *cmd, *tmp; | |||
| 217 | int argc; | |||
| 218 | u_int idx; | |||
| 219 | struct termios now; | |||
| 220 | u_int hlimit; | |||
| 221 | struct winsize ws; | |||
| 222 | sigset_t set, oldset; | |||
| 223 | key_code key; | |||
| 224 | ||||
| 225 | spawn_log(__func__, sc); | |||
| 226 | ||||
| 227 | /* | |||
| 228 | * Work out the current working directory. If respawning, use | |||
| 229 | * the pane's stored one unless specified. | |||
| 230 | */ | |||
| 231 | if (sc->cwd != NULL((void *)0)) { | |||
| 232 | cwd = format_single(item, sc->cwd, c, target->s, NULL((void *)0), NULL((void *)0)); | |||
| 233 | if (*cwd != '/') { | |||
| 234 | xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c, | |||
| 235 | target->s), cwd); | |||
| 236 | free(cwd); | |||
| 237 | cwd = new_cwd; | |||
| 238 | } | |||
| 239 | } else if (~sc->flags & SPAWN_RESPAWN0x4) | |||
| 240 | cwd = xstrdup(server_client_get_cwd(c, target->s)); | |||
| 241 | else | |||
| 242 | cwd = NULL((void *)0); | |||
| 243 | ||||
| 244 | /* | |||
| 245 | * If we are respawning then get rid of the old process. Otherwise | |||
| 246 | * either create a new cell or assign to the one we are given. | |||
| 247 | */ | |||
| 248 | hlimit = options_get_number(s->options, "history-limit"); | |||
| 249 | if (sc->flags & SPAWN_RESPAWN0x4) { | |||
| 250 | if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL0x1)) { | |||
| 251 | window_pane_index(sc->wp0, &idx); | |||
| 252 | xasprintf(cause, "pane %s:%d.%u still active", | |||
| 253 | s->name, sc->wl->idx, idx); | |||
| 254 | free(cwd); | |||
| 255 | return (NULL((void *)0)); | |||
| 256 | } | |||
| 257 | if (sc->wp0->fd != -1) { | |||
| 258 | bufferevent_free(sc->wp0->event); | |||
| 259 | close(sc->wp0->fd); | |||
| 260 | } | |||
| 261 | window_pane_reset_mode_all(sc->wp0); | |||
| 262 | screen_reinit(&sc->wp0->base); | |||
| 263 | input_free(sc->wp0->ictx); | |||
| 264 | sc->wp0->ictx = NULL((void *)0); | |||
| 265 | new_wp = sc->wp0; | |||
| 266 | new_wp->flags &= ~(PANE_STATUSREADY0x200|PANE_STATUSDRAWN0x400); | |||
| 267 | } else if (sc->lc == NULL((void *)0)) { | |||
| 268 | new_wp = window_add_pane(w, NULL((void *)0), hlimit, sc->flags); | |||
| 269 | layout_init(w, new_wp); | |||
| 270 | } else { | |||
| 271 | new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); | |||
| 272 | if (sc->flags & SPAWN_ZOOM0x80) | |||
| 273 | layout_assign_pane(sc->lc, new_wp, 1); | |||
| 274 | else | |||
| 275 | layout_assign_pane(sc->lc, new_wp, 0); | |||
| 276 | } | |||
| 277 | ||||
| 278 | /* | |||
| 279 | * Now we have a pane with nothing running in it ready for the new | |||
| 280 | * process. Work out the command and arguments and store the working | |||
| 281 | * directory. | |||
| 282 | */ | |||
| 283 | if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN0x4)) { | |||
| 284 | cmd = options_get_string(s->options, "default-command"); | |||
| 285 | if (cmd != NULL((void *)0) && *cmd != '\0') { | |||
| 286 | argc = 1; | |||
| 287 | argv = (char **)&cmd; | |||
| 288 | } else { | |||
| 289 | argc = 0; | |||
| 290 | argv = NULL((void *)0); | |||
| 291 | } | |||
| 292 | } else { | |||
| 293 | argc = sc->argc; | |||
| 294 | argv = sc->argv; | |||
| 295 | } | |||
| 296 | if (cwd != NULL((void *)0)) { | |||
| 297 | free(new_wp->cwd); | |||
| 298 | new_wp->cwd = cwd; | |||
| 299 | } | |||
| 300 | ||||
| 301 | /* | |||
| 302 | * Replace the stored arguments if there are new ones. If not, the | |||
| 303 | * existing ones will be used (they will only exist for respawn). | |||
| 304 | */ | |||
| 305 | if (argc > 0) { | |||
| 306 | cmd_free_argv(new_wp->argc, new_wp->argv); | |||
| 307 | new_wp->argc = argc; | |||
| 308 | new_wp->argv = cmd_copy_argv(argc, argv); | |||
| 309 | } | |||
| 310 | ||||
| 311 | /* Create an environment for this pane. */ | |||
| 312 | child = environ_for_session(s, 0); | |||
| 313 | if (sc->environ != NULL((void *)0)) | |||
| 314 | environ_copy(sc->environ, child); | |||
| 315 | environ_set(child, "TMUX_PANE", 0, "%%%u", new_wp->id); | |||
| 316 | ||||
| 317 | /* | |||
| 318 | * Then the PATH environment variable. The session one is replaced from | |||
| 319 | * the client if there is one because otherwise running "tmux new | |||
| 320 | * myprogram" wouldn't work if myprogram isn't in the session's path. | |||
| 321 | */ | |||
| 322 | if (c != NULL((void *)0) && c->session == NULL((void *)0)) { /* only unattached clients */ | |||
| 323 | ee = environ_find(c->environ, "PATH"); | |||
| 324 | if (ee != NULL((void *)0)) | |||
| 325 | environ_set(child, "PATH", 0, "%s", ee->value); | |||
| 326 | } | |||
| 327 | if (environ_find(child, "PATH") == NULL((void *)0)) | |||
| 328 | environ_set(child, "PATH", 0, "%s", _PATH_DEFPATH"/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin"); | |||
| 329 | ||||
| 330 | /* Then the shell. If respawning, use the old one. */ | |||
| 331 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
| 332 | tmp = options_get_string(s->options, "default-shell"); | |||
| 333 | if (!checkshell(tmp)) | |||
| 334 | tmp = _PATH_BSHELL"/bin/sh"; | |||
| 335 | free(new_wp->shell); | |||
| 336 | new_wp->shell = xstrdup(tmp); | |||
| 337 | } | |||
| 338 | environ_set(child, "SHELL", 0, "%s", new_wp->shell); | |||
| 339 | ||||
| 340 | /* Log the arguments we are going to use. */ | |||
| 341 | log_debug("%s: shell=%s", __func__, new_wp->shell); | |||
| 342 | if (new_wp->argc != 0) { | |||
| 343 | cp = cmd_stringify_argv(new_wp->argc, new_wp->argv); | |||
| 344 | log_debug("%s: cmd=%s", __func__, cp); | |||
| 345 | free(cp); | |||
| 346 | } | |||
| 347 | log_debug("%s: cwd=%s", __func__, new_wp->cwd); | |||
| 348 | cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); | |||
| 349 | environ_log(child, "%s: environment ", __func__); | |||
| 350 | ||||
| 351 | /* Initialize the window size. */ | |||
| 352 | memset(&ws, 0, sizeof ws); | |||
| 353 | ws.ws_col = screen_size_x(&new_wp->base)((&new_wp->base)->grid->sx); | |||
| 354 | ws.ws_row = screen_size_y(&new_wp->base)((&new_wp->base)->grid->sy); | |||
| 355 | ws.ws_xpixel = w->xpixel * ws.ws_col; | |||
| 356 | ws.ws_ypixel = w->ypixel * ws.ws_row; | |||
| 357 | ||||
| 358 | /* Block signals until fork has completed. */ | |||
| 359 | sigfillset(&set); | |||
| 360 | sigprocmask(SIG_BLOCK1, &set, &oldset); | |||
| 361 | ||||
| 362 | /* If the command is empty, don't fork a child process. */ | |||
| 363 | if (sc->flags & SPAWN_EMPTY0x40) { | |||
| 364 | new_wp->flags |= PANE_EMPTY0x800; | |||
| 365 | new_wp->base.mode &= ~MODE_CURSOR0x1; | |||
| 366 | new_wp->base.mode |= MODE_CRLF0x4000; | |||
| 367 | goto complete; | |||
| 368 | } | |||
| 369 | ||||
| 370 | /* Fork the new process. */ | |||
| 371 | new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL((void *)0), &ws); | |||
| 372 | if (new_wp->pid == -1) { | |||
| 373 | xasprintf(cause, "fork failed: %s", strerror(errno(*__errno()))); | |||
| 374 | new_wp->fd = -1; | |||
| 375 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
| 376 | server_client_remove_pane(new_wp); | |||
| 377 | layout_close_pane(new_wp); | |||
| 378 | window_remove_pane(w, new_wp); | |||
| 379 | } | |||
| 380 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
| 381 | environ_free(child); | |||
| 382 | return (NULL((void *)0)); | |||
| 383 | } | |||
| 384 | ||||
| 385 | /* In the parent process, everything is done now. */ | |||
| 386 | if (new_wp->pid != 0) | |||
| 387 | goto complete; | |||
| 388 | ||||
| 389 | /* | |||
| 390 | * Child process. Change to the working directory or home if that | |||
| 391 | * fails. | |||
| 392 | */ | |||
| 393 | if (chdir(new_wp->cwd) == 0) | |||
| 394 | environ_set(child, "PWD", 0, "%s", new_wp->cwd); | |||
| 395 | else if ((tmp = find_home()) != NULL((void *)0) && chdir(tmp) == 0) | |||
| 396 | environ_set(child, "PWD", 0, "%s", tmp); | |||
| 397 | else if (chdir("/") == 0) | |||
| 398 | environ_set(child, "PWD", 0, "/"); | |||
| 399 | else | |||
| 400 | fatal("chdir failed"); | |||
| 401 | ||||
| 402 | /* | |||
| 403 | * Update terminal escape characters from the session if available and | |||
| 404 | * force VERASE to tmux's backspace. | |||
| 405 | */ | |||
| 406 | if (tcgetattr(STDIN_FILENO0, &now) != 0) | |||
| 407 | _exit(1); | |||
| 408 | if (s->tio != NULL((void *)0)) | |||
| 409 | memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); | |||
| 410 | key = options_get_number(global_options, "backspace"); | |||
| 411 | if (key >= 0x7f) | |||
| 412 | now.c_cc[VERASE3] = '\177'; | |||
| 413 | else | |||
| 414 | now.c_cc[VERASE3] = key; | |||
| 415 | if (tcsetattr(STDIN_FILENO0, TCSANOW0, &now) != 0) | |||
| 416 | _exit(1); | |||
| 417 | ||||
| 418 | /* Clean up file descriptors and signals and update the environment. */ | |||
| 419 | proc_clear_signals(server_proc, 1); | |||
| 420 | closefrom(STDERR_FILENO2 + 1); | |||
| 421 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
| 422 | log_close(); | |||
| 423 | environ_push(child); | |||
| 424 | ||||
| 425 | /* | |||
| 426 | * If given multiple arguments, use execvp(). Copy the arguments to | |||
| 427 | * ensure they end in a NULL. | |||
| 428 | */ | |||
| 429 | if (new_wp->argc != 0 && new_wp->argc != 1) { | |||
| 430 | argvp = cmd_copy_argv(new_wp->argc, new_wp->argv); | |||
| 431 | execvp(argvp[0], argvp); | |||
| 432 | _exit(1); | |||
| 433 | } | |||
| 434 | ||||
| 435 | /* | |||
| 436 | * If one argument, pass it to $SHELL -c. Otherwise create a login | |||
| 437 | * shell. | |||
| 438 | */ | |||
| 439 | cp = strrchr(new_wp->shell, '/'); | |||
| 440 | if (new_wp->argc == 1) { | |||
| 441 | tmp = new_wp->argv[0]; | |||
| 442 | if (cp != NULL((void *)0) && cp[1] != '\0') | |||
| 443 | xasprintf(&argv0, "%s", cp + 1); | |||
| 444 | else | |||
| 445 | xasprintf(&argv0, "%s", new_wp->shell); | |||
| 446 | execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL((void *)0)); | |||
| 447 | _exit(1); | |||
| 448 | } | |||
| 449 | if (cp != NULL((void *)0) && cp[1] != '\0') | |||
| 450 | xasprintf(&argv0, "-%s", cp + 1); | |||
| 451 | else | |||
| 452 | xasprintf(&argv0, "-%s", new_wp->shell); | |||
| 453 | execl(new_wp->shell, argv0, (char *)NULL((void *)0)); | |||
| 454 | _exit(1); | |||
| 455 | ||||
| 456 | complete: | |||
| 457 | new_wp->flags &= ~PANE_EXITED0x100; | |||
| 458 | ||||
| 459 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
| 460 | window_pane_set_event(new_wp); | |||
| 461 | ||||
| 462 | environ_free(child); | |||
| 463 | ||||
| 464 | if (sc->flags & SPAWN_RESPAWN0x4) | |||
| 465 | return (new_wp); | |||
| 466 | if ((~sc->flags & SPAWN_DETACHED0x2) || w->active == NULL((void *)0)) { | |||
| 467 | if (sc->flags & SPAWN_NONOTIFY0x10) | |||
| 468 | window_set_active_pane(w, new_wp, 0); | |||
| 469 | else | |||
| 470 | window_set_active_pane(w, new_wp, 1); | |||
| 471 | } | |||
| 472 | if (~sc->flags & SPAWN_NONOTIFY0x10) | |||
| 473 | notify_window("window-layout-changed", w); | |||
| 474 | return (new_wp); | |||
| 475 | } |