199e242abSchristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4f26e8bc9Schristos * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5698d5317Sjmmv * 6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 8698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 9698d5317Sjmmv * 10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17698d5317Sjmmv */ 18698d5317Sjmmv 19698d5317Sjmmv #include <sys/types.h> 20698d5317Sjmmv #include <sys/socket.h> 21e271dbb8Schristos #include <sys/uio.h> 22698d5317Sjmmv #include <sys/un.h> 23698d5317Sjmmv #include <sys/wait.h> 24c7e17de0Schristos #include <sys/file.h> 25698d5317Sjmmv 26698d5317Sjmmv #include <errno.h> 27698d5317Sjmmv #include <fcntl.h> 2899e242abSchristos #include <signal.h> 29698d5317Sjmmv #include <stdlib.h> 30698d5317Sjmmv #include <string.h> 31698d5317Sjmmv #include <unistd.h> 32698d5317Sjmmv 33698d5317Sjmmv #include "tmux.h" 34698d5317Sjmmv 35e9a2d6faSchristos static struct tmuxproc *client_proc; 36e9a2d6faSchristos static struct tmuxpeer *client_peer; 37e271dbb8Schristos static uint64_t client_flags; 38e271dbb8Schristos static int client_suspended; 39e9a2d6faSchristos static enum { 40928fc495Schristos CLIENT_EXIT_NONE, 41928fc495Schristos CLIENT_EXIT_DETACHED, 42928fc495Schristos CLIENT_EXIT_DETACHED_HUP, 43928fc495Schristos CLIENT_EXIT_LOST_TTY, 44928fc495Schristos CLIENT_EXIT_TERMINATED, 45928fc495Schristos CLIENT_EXIT_LOST_SERVER, 46928fc495Schristos CLIENT_EXIT_EXITED, 47928fc495Schristos CLIENT_EXIT_SERVER_EXITED, 48e271dbb8Schristos CLIENT_EXIT_MESSAGE_PROVIDED 49928fc495Schristos } client_exitreason = CLIENT_EXIT_NONE; 5068e6ba84Schristos static int client_exitflag; 51e9a2d6faSchristos static int client_exitval; 52e9a2d6faSchristos static enum msgtype client_exittype; 53e9a2d6faSchristos static const char *client_exitsession; 54e271dbb8Schristos static char *client_exitmessage; 55e9a2d6faSchristos static const char *client_execshell; 56e9a2d6faSchristos static const char *client_execcmd; 57e9a2d6faSchristos static int client_attached; 5868e6ba84Schristos static struct client_files client_files = RB_INITIALIZER(&client_files); 59698d5317Sjmmv 60e9a2d6faSchristos static __dead void client_exec(const char *,const char *); 61e9a2d6faSchristos static int client_get_lock(char *); 62e271dbb8Schristos static int client_connect(struct event_base *, const char *, 63e271dbb8Schristos uint64_t); 64e271dbb8Schristos static void client_send_identify(const char *, const char *, 65e271dbb8Schristos char **, u_int, const char *, int); 66e9a2d6faSchristos static void client_signal(int); 67e9a2d6faSchristos static void client_dispatch(struct imsg *, void *); 68e9a2d6faSchristos static void client_dispatch_attached(struct imsg *); 69fe99a117Schristos static void client_dispatch_wait(struct imsg *); 70e9a2d6faSchristos static const char *client_exit_message(void); 71928fc495Schristos 72928fc495Schristos /* 73928fc495Schristos * Get server create lock. If already held then server start is happening in 74f26e8bc9Schristos * another client, so block until the lock is released and return -2 to 75f26e8bc9Schristos * retry. Return -1 on failure to continue and start the server anyway. 76928fc495Schristos */ 77e9a2d6faSchristos static int 78928fc495Schristos client_get_lock(char *lockfile) 79928fc495Schristos { 80928fc495Schristos int lockfd; 81928fc495Schristos 8299e242abSchristos log_debug("lock file is %s", lockfile); 83928fc495Schristos 84f26e8bc9Schristos if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { 85f26e8bc9Schristos log_debug("open failed: %s", strerror(errno)); 86f26e8bc9Schristos return (-1); 87f26e8bc9Schristos } 88f26e8bc9Schristos 8999e242abSchristos if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { 9099e242abSchristos log_debug("flock failed: %s", strerror(errno)); 9199e242abSchristos if (errno != EAGAIN) 9299e242abSchristos return (lockfd); 9399e242abSchristos while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) 94928fc495Schristos /* nothing */; 95928fc495Schristos close(lockfd); 96f26e8bc9Schristos return (-2); 97928fc495Schristos } 9899e242abSchristos log_debug("flock succeeded"); 99928fc495Schristos 100928fc495Schristos return (lockfd); 101928fc495Schristos } 102698d5317Sjmmv 103698d5317Sjmmv /* Connect client to server. */ 104e9a2d6faSchristos static int 105e271dbb8Schristos client_connect(struct event_base *base, const char *path, uint64_t flags) 106698d5317Sjmmv { 107698d5317Sjmmv struct sockaddr_un sa; 108698d5317Sjmmv size_t size; 10999e242abSchristos int fd, lockfd = -1, locked = 0; 11099e242abSchristos char *lockfile = NULL; 111698d5317Sjmmv 112698d5317Sjmmv memset(&sa, 0, sizeof sa); 113698d5317Sjmmv sa.sun_family = AF_UNIX; 114698d5317Sjmmv size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); 115698d5317Sjmmv if (size >= sizeof sa.sun_path) { 116698d5317Sjmmv errno = ENAMETOOLONG; 117698d5317Sjmmv return (-1); 118698d5317Sjmmv } 11999e242abSchristos log_debug("socket is %s", path); 120698d5317Sjmmv 121928fc495Schristos retry: 122698d5317Sjmmv if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 123f26e8bc9Schristos return (-1); 124698d5317Sjmmv 12599e242abSchristos log_debug("trying connect"); 126f26e8bc9Schristos if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { 12799e242abSchristos log_debug("connect failed: %s", strerror(errno)); 128928fc495Schristos if (errno != ECONNREFUSED && errno != ENOENT) 129928fc495Schristos goto failed; 130e271dbb8Schristos if (flags & CLIENT_NOSTARTSERVER) 131e271dbb8Schristos goto failed; 132e271dbb8Schristos if (~flags & CLIENT_STARTSERVER) 133698d5317Sjmmv goto failed; 134928fc495Schristos close(fd); 135928fc495Schristos 13699e242abSchristos if (!locked) { 137928fc495Schristos xasprintf(&lockfile, "%s.lock", path); 138f26e8bc9Schristos if ((lockfd = client_get_lock(lockfile)) < 0) { 139f26e8bc9Schristos log_debug("didn't get lock (%d)", lockfd); 140f26e8bc9Schristos 141928fc495Schristos free(lockfile); 142f26e8bc9Schristos lockfile = NULL; 143f26e8bc9Schristos 144f26e8bc9Schristos if (lockfd == -2) 145928fc495Schristos goto retry; 146698d5317Sjmmv } 147f26e8bc9Schristos log_debug("got lock (%d)", lockfd); 14899e242abSchristos 14999e242abSchristos /* 15099e242abSchristos * Always retry at least once, even if we got the lock, 15199e242abSchristos * because another client could have taken the lock, 15299e242abSchristos * started the server and released the lock between our 15399e242abSchristos * connect() and flock(). 15499e242abSchristos */ 15599e242abSchristos locked = 1; 15699e242abSchristos goto retry; 15799e242abSchristos } 15899e242abSchristos 159f26e8bc9Schristos if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { 160928fc495Schristos free(lockfile); 161928fc495Schristos close(lockfd); 162928fc495Schristos return (-1); 163928fc495Schristos } 164e271dbb8Schristos fd = server_start(client_proc, flags, base, lockfd, lockfile); 16599e242abSchristos } 16699e242abSchristos 167f26e8bc9Schristos if (locked && lockfd >= 0) { 168928fc495Schristos free(lockfile); 169928fc495Schristos close(lockfd); 170698d5317Sjmmv } 171d530c4d0Sjmmv setblocking(fd, 0); 172698d5317Sjmmv return (fd); 173698d5317Sjmmv 174698d5317Sjmmv failed: 17599e242abSchristos if (locked) { 17699e242abSchristos free(lockfile); 17799e242abSchristos close(lockfd); 17899e242abSchristos } 179698d5317Sjmmv close(fd); 180698d5317Sjmmv return (-1); 181698d5317Sjmmv } 182698d5317Sjmmv 183928fc495Schristos /* Get exit string from reason number. */ 184928fc495Schristos const char * 185928fc495Schristos client_exit_message(void) 186928fc495Schristos { 187928fc495Schristos static char msg[256]; 188928fc495Schristos 189928fc495Schristos switch (client_exitreason) { 190928fc495Schristos case CLIENT_EXIT_NONE: 191928fc495Schristos break; 192928fc495Schristos case CLIENT_EXIT_DETACHED: 193928fc495Schristos if (client_exitsession != NULL) { 194928fc495Schristos xsnprintf(msg, sizeof msg, "detached " 195928fc495Schristos "(from session %s)", client_exitsession); 196928fc495Schristos return (msg); 197928fc495Schristos } 198928fc495Schristos return ("detached"); 199928fc495Schristos case CLIENT_EXIT_DETACHED_HUP: 200928fc495Schristos if (client_exitsession != NULL) { 201928fc495Schristos xsnprintf(msg, sizeof msg, "detached and SIGHUP " 202928fc495Schristos "(from session %s)", client_exitsession); 203928fc495Schristos return (msg); 204928fc495Schristos } 205928fc495Schristos return ("detached and SIGHUP"); 206928fc495Schristos case CLIENT_EXIT_LOST_TTY: 207928fc495Schristos return ("lost tty"); 208928fc495Schristos case CLIENT_EXIT_TERMINATED: 209928fc495Schristos return ("terminated"); 210928fc495Schristos case CLIENT_EXIT_LOST_SERVER: 21130744affSchristos return ("server exited unexpectedly"); 212928fc495Schristos case CLIENT_EXIT_EXITED: 213928fc495Schristos return ("exited"); 214928fc495Schristos case CLIENT_EXIT_SERVER_EXITED: 215928fc495Schristos return ("server exited"); 216e271dbb8Schristos case CLIENT_EXIT_MESSAGE_PROVIDED: 217e271dbb8Schristos return (client_exitmessage); 218928fc495Schristos } 219928fc495Schristos return ("unknown reason"); 220928fc495Schristos } 221928fc495Schristos 22268e6ba84Schristos /* Exit if all streams flushed. */ 22368e6ba84Schristos static void 22468e6ba84Schristos client_exit(void) 22568e6ba84Schristos { 226e271dbb8Schristos if (!file_write_left(&client_files)) 22768e6ba84Schristos proc_exit(client_proc); 22868e6ba84Schristos } 22968e6ba84Schristos 230698d5317Sjmmv /* Client main loop. */ 231698d5317Sjmmv int 232e271dbb8Schristos client_main(struct event_base *base, int argc, char **argv, uint64_t flags, 233e271dbb8Schristos int feat) 234698d5317Sjmmv { 23530744affSchristos struct cmd_parse_result *pr; 23668e6ba84Schristos struct msg_command *data; 237e271dbb8Schristos int fd, i; 238e271dbb8Schristos const char *ttynam, *termname, *cwd; 239d530c4d0Sjmmv pid_t ppid; 240698d5317Sjmmv enum msgtype msg; 241928fc495Schristos struct termios tio, saved_tio; 242e271dbb8Schristos size_t size, linesize = 0; 243e271dbb8Schristos ssize_t linelen; 244e271dbb8Schristos char *line = NULL, **caps = NULL, *cause; 245e271dbb8Schristos u_int ncaps = 0; 24646548964Swiz struct args_value *values; 247698d5317Sjmmv 248698d5317Sjmmv /* Set up the initial command. */ 249fe99a117Schristos if (shell_command != NULL) { 250698d5317Sjmmv msg = MSG_SHELL; 251e271dbb8Schristos flags |= CLIENT_STARTSERVER; 252698d5317Sjmmv } else if (argc == 0) { 253698d5317Sjmmv msg = MSG_COMMAND; 254e271dbb8Schristos flags |= CLIENT_STARTSERVER; 255698d5317Sjmmv } else { 256698d5317Sjmmv msg = MSG_COMMAND; 257698d5317Sjmmv 258698d5317Sjmmv /* 25946548964Swiz * It's annoying parsing the command string twice (in client 26046548964Swiz * and later in server) but it is necessary to get the start 26146548964Swiz * server flag. 262698d5317Sjmmv */ 26346548964Swiz values = args_from_vector(argc, argv); 26446548964Swiz pr = cmd_parse_from_arguments(values, argc, NULL); 26530744affSchristos if (pr->status == CMD_PARSE_SUCCESS) { 266e271dbb8Schristos if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER)) 267e271dbb8Schristos flags |= CLIENT_STARTSERVER; 26830744affSchristos cmd_list_free(pr->cmdlist); 26930744affSchristos } else 27030744affSchristos free(pr->error); 27146548964Swiz args_free_values(values, argc); 27246548964Swiz free(values); 273e9a2d6faSchristos } 274698d5317Sjmmv 275f26e8bc9Schristos /* Create client process structure (starts logging). */ 276fe99a117Schristos client_proc = proc_start("client"); 277fe99a117Schristos proc_set_signals(client_proc, client_signal); 27899e242abSchristos 279e271dbb8Schristos /* Save the flags. */ 280e271dbb8Schristos client_flags = flags; 281e271dbb8Schristos log_debug("flags are %#llx", (unsigned long long)client_flags); 282e271dbb8Schristos 28399e242abSchristos /* Initialize the client socket and start the server. */ 284f844e94eSwiz #ifdef HAVE_SYSTEMD 285f844e94eSwiz if (systemd_activated()) { 286f844e94eSwiz /* socket-based activation, do not even try to be a client. */ 287f844e94eSwiz fd = server_start(client_proc, flags, base, 0, NULL); 288f844e94eSwiz } else 289f844e94eSwiz #endif 290e271dbb8Schristos fd = client_connect(base, socket_path, client_flags); 29199e242abSchristos if (fd == -1) { 29299e242abSchristos if (errno == ECONNREFUSED) { 29399e242abSchristos fprintf(stderr, "no server running on %s\n", 29499e242abSchristos socket_path); 29599e242abSchristos } else { 29699e242abSchristos fprintf(stderr, "error connecting to %s (%s)\n", 29799e242abSchristos socket_path, strerror(errno)); 29899e242abSchristos } 299698d5317Sjmmv return (1); 300698d5317Sjmmv } 301fe99a117Schristos client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); 302698d5317Sjmmv 30399e242abSchristos /* Save these before pledge(). */ 3040a274e86Schristos if ((cwd = find_cwd()) == NULL && (cwd = find_home()) == NULL) 305f26e8bc9Schristos cwd = "/"; 30699e242abSchristos if ((ttynam = ttyname(STDIN_FILENO)) == NULL) 30799e242abSchristos ttynam = ""; 308e271dbb8Schristos if ((termname = getenv("TERM")) == NULL) 309e271dbb8Schristos termname = ""; 31099e242abSchristos 31199e242abSchristos /* 31299e242abSchristos * Drop privileges for client. "proc exec" is needed for -c and for 31399e242abSchristos * locking (which uses system(3)). 31499e242abSchristos * 31599e242abSchristos * "tty" is needed to restore termios(4) and also for some reason -CC 31699e242abSchristos * does not work properly without it (input is not recognised). 31799e242abSchristos * 31899e242abSchristos * "sendfd" is dropped later in client_dispatch_wait(). 31999e242abSchristos */ 32068e6ba84Schristos if (pledge( 32168e6ba84Schristos "stdio rpath wpath cpath unix sendfd proc exec tty", 32268e6ba84Schristos NULL) != 0) 32399e242abSchristos fatal("pledge failed"); 32499e242abSchristos 325e271dbb8Schristos /* Load terminfo entry if any. */ 326e271dbb8Schristos if (isatty(STDIN_FILENO) && 327e271dbb8Schristos *termname != '\0' && 328e271dbb8Schristos tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps, 329e271dbb8Schristos &cause) != 0) { 330e271dbb8Schristos fprintf(stderr, "%s\n", cause); 331e271dbb8Schristos free(cause); 332e271dbb8Schristos return (1); 333e271dbb8Schristos } 334e271dbb8Schristos 33599e242abSchristos /* Free stuff that is not used in the client. */ 336e9a2d6faSchristos if (ptm_fd != -1) 337e9a2d6faSchristos close(ptm_fd); 338f26e8bc9Schristos options_free(global_options); 339f26e8bc9Schristos options_free(global_s_options); 340f26e8bc9Schristos options_free(global_w_options); 341f26e8bc9Schristos environ_free(global_environ); 342698d5317Sjmmv 34368e6ba84Schristos /* Set up control mode. */ 34499e242abSchristos if (client_flags & CLIENT_CONTROLCONTROL) { 345e9a2d6faSchristos if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { 346e9a2d6faSchristos fprintf(stderr, "tcgetattr failed: %s\n", 347e9a2d6faSchristos strerror(errno)); 348e9a2d6faSchristos return (1); 349e9a2d6faSchristos } 350928fc495Schristos cfmakeraw(&tio); 351928fc495Schristos tio.c_iflag = ICRNL|IXANY; 352928fc495Schristos tio.c_oflag = OPOST|ONLCR; 353928fc495Schristos #ifdef NOKERNINFO 354928fc495Schristos tio.c_lflag = NOKERNINFO; 355928fc495Schristos #endif 356928fc495Schristos tio.c_cflag = CREAD|CS8|HUPCL; 357928fc495Schristos tio.c_cc[VMIN] = 1; 358928fc495Schristos tio.c_cc[VTIME] = 0; 359928fc495Schristos cfsetispeed(&tio, cfgetispeed(&saved_tio)); 360928fc495Schristos cfsetospeed(&tio, cfgetospeed(&saved_tio)); 361928fc495Schristos tcsetattr(STDIN_FILENO, TCSANOW, &tio); 362928fc495Schristos } 363928fc495Schristos 364928fc495Schristos /* Send identify messages. */ 365e271dbb8Schristos client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); 366e271dbb8Schristos tty_term_free_list(caps, ncaps); 36746548964Swiz proc_flush_peer(client_peer); 368698d5317Sjmmv 369698d5317Sjmmv /* Send first command. */ 370698d5317Sjmmv if (msg == MSG_COMMAND) { 371928fc495Schristos /* How big is the command? */ 372928fc495Schristos size = 0; 373928fc495Schristos for (i = 0; i < argc; i++) 374928fc495Schristos size += strlen(argv[i]) + 1; 375c7e17de0Schristos if (size > MAX_IMSGSIZE - (sizeof *data)) { 376c7e17de0Schristos fprintf(stderr, "command too long\n"); 377c7e17de0Schristos return (1); 378c7e17de0Schristos } 379928fc495Schristos data = xmalloc((sizeof *data) + size); 380698d5317Sjmmv 381698d5317Sjmmv /* Prepare command for server. */ 382928fc495Schristos data->argc = argc; 383928fc495Schristos if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { 384928fc495Schristos fprintf(stderr, "command too long\n"); 385928fc495Schristos free(data); 386698d5317Sjmmv return (1); 387698d5317Sjmmv } 388928fc495Schristos size += sizeof *data; 389698d5317Sjmmv 390928fc495Schristos /* Send the command. */ 391f26e8bc9Schristos if (proc_send(client_peer, msg, -1, data, size) != 0) { 392928fc495Schristos fprintf(stderr, "failed to send command\n"); 393928fc495Schristos free(data); 394928fc495Schristos return (1); 395928fc495Schristos } 396928fc495Schristos free(data); 397698d5317Sjmmv } else if (msg == MSG_SHELL) 398f26e8bc9Schristos proc_send(client_peer, msg, -1, NULL, 0); 399698d5317Sjmmv 400f26e8bc9Schristos /* Start main loop. */ 401f26e8bc9Schristos proc_loop(client_proc, NULL); 402698d5317Sjmmv 403e9a2d6faSchristos /* Run command if user requested exec, instead of exiting. */ 404e9a2d6faSchristos if (client_exittype == MSG_EXEC) { 405e9a2d6faSchristos if (client_flags & CLIENT_CONTROLCONTROL) 406e9a2d6faSchristos tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); 407e9a2d6faSchristos client_exec(client_execshell, client_execcmd); 408e9a2d6faSchristos } 409e9a2d6faSchristos 410e271dbb8Schristos /* Restore streams to blocking. */ 411e271dbb8Schristos setblocking(STDIN_FILENO, 1); 412e271dbb8Schristos setblocking(STDOUT_FILENO, 1); 413e271dbb8Schristos setblocking(STDERR_FILENO, 1); 414e271dbb8Schristos 415698d5317Sjmmv /* Print the exit message, if any, and exit. */ 416d530c4d0Sjmmv if (client_attached) { 41799e242abSchristos if (client_exitreason != CLIENT_EXIT_NONE) 418928fc495Schristos printf("[%s]\n", client_exit_message()); 419d530c4d0Sjmmv 420d530c4d0Sjmmv ppid = getppid(); 421d530c4d0Sjmmv if (client_exittype == MSG_DETACHKILL && ppid > 1) 422d530c4d0Sjmmv kill(ppid, SIGHUP); 423e271dbb8Schristos } else if (client_flags & CLIENT_CONTROL) { 424928fc495Schristos if (client_exitreason != CLIENT_EXIT_NONE) 425928fc495Schristos printf("%%exit %s\n", client_exit_message()); 426928fc495Schristos else 427928fc495Schristos printf("%%exit\n"); 428e271dbb8Schristos fflush(stdout); 429e271dbb8Schristos if (client_flags & CLIENT_CONTROL_WAITEXIT) { 430e271dbb8Schristos setvbuf(stdin, NULL, _IOLBF, 0); 431e271dbb8Schristos for (;;) { 432e271dbb8Schristos linelen = getline(&line, &linesize, stdin); 433e271dbb8Schristos if (linelen <= 1) 434e271dbb8Schristos break; 435e271dbb8Schristos } 436e271dbb8Schristos free(line); 437e271dbb8Schristos } 438e271dbb8Schristos if (client_flags & CLIENT_CONTROLCONTROL) { 439928fc495Schristos printf("\033\\"); 440e271dbb8Schristos fflush(stdout); 441928fc495Schristos tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); 442e271dbb8Schristos } 443f26e8bc9Schristos } else if (client_exitreason != CLIENT_EXIT_NONE) 444f26e8bc9Schristos fprintf(stderr, "%s\n", client_exit_message()); 445698d5317Sjmmv return (client_exitval); 446698d5317Sjmmv } 447698d5317Sjmmv 448928fc495Schristos /* Send identify messages to server. */ 449e9a2d6faSchristos static void 450e271dbb8Schristos client_send_identify(const char *ttynam, const char *termname, char **caps, 451e271dbb8Schristos u_int ncaps, const char *cwd, int feat) 452698d5317Sjmmv { 453928fc495Schristos char **ss; 45499e242abSchristos size_t sslen; 455*890b6d91Swiz int fd; 456*890b6d91Swiz uint64_t flags = client_flags; 45799e242abSchristos pid_t pid; 458e271dbb8Schristos u_int i; 459698d5317Sjmmv 460*890b6d91Swiz proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &flags, sizeof flags); 461e271dbb8Schristos proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags, 462e271dbb8Schristos sizeof client_flags); 463698d5317Sjmmv 464e271dbb8Schristos proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname, 465e271dbb8Schristos strlen(termname) + 1); 466e271dbb8Schristos proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); 467698d5317Sjmmv 468f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, 469f26e8bc9Schristos strlen(ttynam) + 1); 470f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); 471698d5317Sjmmv 472e271dbb8Schristos for (i = 0; i < ncaps; i++) { 473e271dbb8Schristos proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1, 474e271dbb8Schristos caps[i], strlen(caps[i]) + 1); 475e271dbb8Schristos } 476e271dbb8Schristos 477698d5317Sjmmv if ((fd = dup(STDIN_FILENO)) == -1) 478698d5317Sjmmv fatal("dup failed"); 479f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); 480e271dbb8Schristos if ((fd = dup(STDOUT_FILENO)) == -1) 481e271dbb8Schristos fatal("dup failed"); 482e271dbb8Schristos proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0); 483698d5317Sjmmv 48499e242abSchristos pid = getpid(); 485f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); 48699e242abSchristos 48799e242abSchristos for (ss = environ; *ss != NULL; ss++) { 48899e242abSchristos sslen = strlen(*ss) + 1; 489f26e8bc9Schristos if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) 490f26e8bc9Schristos continue; 491f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); 49299e242abSchristos } 493698d5317Sjmmv 494f26e8bc9Schristos proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); 495698d5317Sjmmv } 496698d5317Sjmmv 49799e242abSchristos /* Run command in shell; used for -c. */ 498e9a2d6faSchristos static __dead void 499f26e8bc9Schristos client_exec(const char *shell, const char *shellcmd) 50099e242abSchristos { 50199e242abSchristos char *argv0; 50299e242abSchristos 503f26e8bc9Schristos log_debug("shell %s, command %s", shell, shellcmd); 504*890b6d91Swiz argv0 = shell_argv0(shell, !!(client_flags & CLIENT_LOGIN)); 50599e242abSchristos setenv("SHELL", shell, 1); 50699e242abSchristos 507fe99a117Schristos proc_clear_signals(client_proc, 1); 508fe99a117Schristos 50999e242abSchristos setblocking(STDIN_FILENO, 1); 51099e242abSchristos setblocking(STDOUT_FILENO, 1); 51199e242abSchristos setblocking(STDERR_FILENO, 1); 51299e242abSchristos closefrom(STDERR_FILENO + 1); 51399e242abSchristos 514f26e8bc9Schristos execl(shell, argv0, "-c", shellcmd, (char *) NULL); 51599e242abSchristos fatal("execl failed"); 51699e242abSchristos } 51799e242abSchristos 518f26e8bc9Schristos /* Callback to handle signals in the client. */ 519e9a2d6faSchristos static void 520f26e8bc9Schristos client_signal(int sig) 521698d5317Sjmmv { 522f26e8bc9Schristos struct sigaction sigact; 523f26e8bc9Schristos int status; 524f844e94eSwiz pid_t pid; 525f26e8bc9Schristos 526e271dbb8Schristos log_debug("%s: %s", __func__, strsignal(sig)); 527f844e94eSwiz if (sig == SIGCHLD) { 528f844e94eSwiz for (;;) { 529f844e94eSwiz pid = waitpid(WAIT_ANY, &status, WNOHANG); 530f844e94eSwiz if (pid == 0) 531f844e94eSwiz break; 532f844e94eSwiz if (pid == -1) { 533f844e94eSwiz if (errno == ECHILD) 534f844e94eSwiz break; 535f844e94eSwiz log_debug("waitpid failed: %s", 536f844e94eSwiz strerror(errno)); 537f844e94eSwiz } 538f844e94eSwiz } 539f844e94eSwiz } else if (!client_attached) { 54046548964Swiz if (sig == SIGTERM || sig == SIGHUP) 541f26e8bc9Schristos proc_exit(client_proc); 542f26e8bc9Schristos } else { 543f26e8bc9Schristos switch (sig) { 544f26e8bc9Schristos case SIGHUP: 545f26e8bc9Schristos client_exitreason = CLIENT_EXIT_LOST_TTY; 546f26e8bc9Schristos client_exitval = 1; 547f26e8bc9Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 548f26e8bc9Schristos break; 549f26e8bc9Schristos case SIGTERM: 550e271dbb8Schristos if (!client_suspended) 551f26e8bc9Schristos client_exitreason = CLIENT_EXIT_TERMINATED; 552f26e8bc9Schristos client_exitval = 1; 553f26e8bc9Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 554f26e8bc9Schristos break; 555f26e8bc9Schristos case SIGWINCH: 556f26e8bc9Schristos proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); 557f26e8bc9Schristos break; 558f26e8bc9Schristos case SIGCONT: 559f26e8bc9Schristos memset(&sigact, 0, sizeof sigact); 560f26e8bc9Schristos sigemptyset(&sigact.sa_mask); 561f26e8bc9Schristos sigact.sa_flags = SA_RESTART; 562f26e8bc9Schristos sigact.sa_handler = SIG_IGN; 563f26e8bc9Schristos if (sigaction(SIGTSTP, &sigact, NULL) != 0) 564f26e8bc9Schristos fatal("sigaction failed"); 565f26e8bc9Schristos proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); 566e271dbb8Schristos client_suspended = 0; 567f26e8bc9Schristos break; 568f26e8bc9Schristos } 569f26e8bc9Schristos } 570f26e8bc9Schristos } 571f26e8bc9Schristos 572e271dbb8Schristos /* Callback for file write error or close. */ 573e271dbb8Schristos static void 574e271dbb8Schristos client_file_check_cb(__unused struct client *c, __unused const char *path, 575e271dbb8Schristos __unused int error, __unused int closed, __unused struct evbuffer *buffer, 576e271dbb8Schristos __unused void *data) 577e271dbb8Schristos { 578e271dbb8Schristos if (client_exitflag) 579e271dbb8Schristos client_exit(); 580e271dbb8Schristos } 581e271dbb8Schristos 582f26e8bc9Schristos /* Callback for client read events. */ 583e9a2d6faSchristos static void 584fe99a117Schristos client_dispatch(struct imsg *imsg, __unused void *arg) 585f26e8bc9Schristos { 586f26e8bc9Schristos if (imsg == NULL) { 587e271dbb8Schristos if (!client_exitflag) { 588f26e8bc9Schristos client_exitreason = CLIENT_EXIT_LOST_SERVER; 589f26e8bc9Schristos client_exitval = 1; 590e271dbb8Schristos } 591f26e8bc9Schristos proc_exit(client_proc); 592f26e8bc9Schristos return; 593f26e8bc9Schristos } 594f26e8bc9Schristos 595f26e8bc9Schristos if (client_attached) 596f26e8bc9Schristos client_dispatch_attached(imsg); 597f26e8bc9Schristos else 598fe99a117Schristos client_dispatch_wait(imsg); 599f26e8bc9Schristos } 600f26e8bc9Schristos 601e271dbb8Schristos /* Process an exit message. */ 602e271dbb8Schristos static void 603e271dbb8Schristos client_dispatch_exit_message(char *data, size_t datalen) 604e271dbb8Schristos { 605e271dbb8Schristos int retval; 606e271dbb8Schristos 607e271dbb8Schristos if (datalen < sizeof retval && datalen != 0) 608e271dbb8Schristos fatalx("bad MSG_EXIT size"); 609e271dbb8Schristos 610e271dbb8Schristos if (datalen >= sizeof retval) { 611e271dbb8Schristos memcpy(&retval, data, sizeof retval); 612e271dbb8Schristos client_exitval = retval; 613e271dbb8Schristos } 614e271dbb8Schristos 615e271dbb8Schristos if (datalen > sizeof retval) { 616e271dbb8Schristos datalen -= sizeof retval; 617e271dbb8Schristos data += sizeof retval; 618e271dbb8Schristos 619e271dbb8Schristos client_exitmessage = xmalloc(datalen); 620e271dbb8Schristos memcpy(client_exitmessage, data, datalen); 621e271dbb8Schristos client_exitmessage[datalen - 1] = '\0'; 622e271dbb8Schristos 623e271dbb8Schristos client_exitreason = CLIENT_EXIT_MESSAGE_PROVIDED; 624e271dbb8Schristos } 625e271dbb8Schristos } 626e271dbb8Schristos 627f26e8bc9Schristos /* Dispatch imsgs when in wait state (before MSG_READY). */ 628e9a2d6faSchristos static void 629fe99a117Schristos client_dispatch_wait(struct imsg *imsg) 630f26e8bc9Schristos { 631928fc495Schristos char *data; 632f26e8bc9Schristos ssize_t datalen; 63399e242abSchristos static int pledge_applied; 63499e242abSchristos 63599e242abSchristos /* 63699e242abSchristos * "sendfd" is no longer required once all of the identify messages 63799e242abSchristos * have been sent. We know the server won't send us anything until that 63899e242abSchristos * point (because we don't ask it to), so we can drop "sendfd" once we 63999e242abSchristos * get the first message from the server. 64099e242abSchristos */ 64199e242abSchristos if (!pledge_applied) { 64268e6ba84Schristos if (pledge( 64368e6ba84Schristos "stdio rpath wpath cpath unix proc exec tty", 64468e6ba84Schristos NULL) != 0) 64599e242abSchristos fatal("pledge failed"); 64699e242abSchristos pledge_applied = 1; 64768e6ba84Schristos } 648698d5317Sjmmv 649f26e8bc9Schristos data = imsg->data; 650f26e8bc9Schristos datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 651928fc495Schristos 652f26e8bc9Schristos switch (imsg->hdr.type) { 653698d5317Sjmmv case MSG_EXIT: 654698d5317Sjmmv case MSG_SHUTDOWN: 655e271dbb8Schristos client_dispatch_exit_message(data, datalen); 65668e6ba84Schristos client_exitflag = 1; 65768e6ba84Schristos client_exit(); 658f26e8bc9Schristos break; 659698d5317Sjmmv case MSG_READY: 660698d5317Sjmmv if (datalen != 0) 661698d5317Sjmmv fatalx("bad MSG_READY size"); 662698d5317Sjmmv 663698d5317Sjmmv client_attached = 1; 664f26e8bc9Schristos proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); 665928fc495Schristos break; 666698d5317Sjmmv case MSG_VERSION: 667698d5317Sjmmv if (datalen != 0) 668698d5317Sjmmv fatalx("bad MSG_VERSION size"); 669698d5317Sjmmv 670928fc495Schristos fprintf(stderr, "protocol version mismatch " 67199e242abSchristos "(client %d, server %u)\n", PROTOCOL_VERSION, 672f26e8bc9Schristos imsg->hdr.peerid & 0xff); 673698d5317Sjmmv client_exitval = 1; 674f26e8bc9Schristos proc_exit(client_proc); 675f26e8bc9Schristos break; 676e271dbb8Schristos case MSG_FLAGS: 677e271dbb8Schristos if (datalen != sizeof client_flags) 678e271dbb8Schristos fatalx("bad MSG_FLAGS string"); 679e271dbb8Schristos 680e271dbb8Schristos memcpy(&client_flags, data, sizeof client_flags); 681e271dbb8Schristos log_debug("new flags are %#llx", 682e271dbb8Schristos (unsigned long long)client_flags); 683e271dbb8Schristos break; 684698d5317Sjmmv case MSG_SHELL: 685928fc495Schristos if (datalen == 0 || data[datalen - 1] != '\0') 686928fc495Schristos fatalx("bad MSG_SHELL string"); 687698d5317Sjmmv 688fe99a117Schristos client_exec(data, shell_command); 689698d5317Sjmmv /* NOTREACHED */ 690928fc495Schristos case MSG_DETACH: 691928fc495Schristos case MSG_DETACHKILL: 692f26e8bc9Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 693928fc495Schristos break; 694928fc495Schristos case MSG_EXITED: 695f26e8bc9Schristos proc_exit(client_proc); 696f26e8bc9Schristos break; 69768e6ba84Schristos case MSG_READ_OPEN: 698e271dbb8Schristos file_read_open(&client_files, client_peer, imsg, 1, 699e271dbb8Schristos !(client_flags & CLIENT_CONTROL), client_file_check_cb, 700e271dbb8Schristos NULL); 70168e6ba84Schristos break; 702f844e94eSwiz case MSG_READ_CANCEL: 703f844e94eSwiz file_read_cancel(&client_files, imsg); 704f844e94eSwiz break; 70568e6ba84Schristos case MSG_WRITE_OPEN: 706e271dbb8Schristos file_write_open(&client_files, client_peer, imsg, 1, 707e271dbb8Schristos !(client_flags & CLIENT_CONTROL), client_file_check_cb, 708e271dbb8Schristos NULL); 70968e6ba84Schristos break; 71068e6ba84Schristos case MSG_WRITE: 711e271dbb8Schristos file_write_data(&client_files, imsg); 71268e6ba84Schristos break; 71368e6ba84Schristos case MSG_WRITE_CLOSE: 714e271dbb8Schristos file_write_close(&client_files, imsg); 71568e6ba84Schristos break; 71668e6ba84Schristos case MSG_OLDSTDERR: 71768e6ba84Schristos case MSG_OLDSTDIN: 71868e6ba84Schristos case MSG_OLDSTDOUT: 71968e6ba84Schristos fprintf(stderr, "server version is too old for client\n"); 72068e6ba84Schristos proc_exit(client_proc); 72168e6ba84Schristos break; 722698d5317Sjmmv } 723698d5317Sjmmv } 724698d5317Sjmmv 725698d5317Sjmmv /* Dispatch imsgs in attached state (after MSG_READY). */ 726e9a2d6faSchristos static void 727f26e8bc9Schristos client_dispatch_attached(struct imsg *imsg) 728698d5317Sjmmv { 729698d5317Sjmmv struct sigaction sigact; 730928fc495Schristos char *data; 731f26e8bc9Schristos ssize_t datalen; 732698d5317Sjmmv 733f26e8bc9Schristos data = imsg->data; 734f26e8bc9Schristos datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 735928fc495Schristos 736f26e8bc9Schristos switch (imsg->hdr.type) { 737e271dbb8Schristos case MSG_FLAGS: 738e271dbb8Schristos if (datalen != sizeof client_flags) 739e271dbb8Schristos fatalx("bad MSG_FLAGS string"); 740e271dbb8Schristos 741e271dbb8Schristos memcpy(&client_flags, data, sizeof client_flags); 742e271dbb8Schristos log_debug("new flags are %#llx", 743e271dbb8Schristos (unsigned long long)client_flags); 744e271dbb8Schristos break; 745698d5317Sjmmv case MSG_DETACH: 746928fc495Schristos case MSG_DETACHKILL: 747928fc495Schristos if (datalen == 0 || data[datalen - 1] != '\0') 748928fc495Schristos fatalx("bad MSG_DETACH string"); 749698d5317Sjmmv 750928fc495Schristos client_exitsession = xstrdup(data); 751f26e8bc9Schristos client_exittype = imsg->hdr.type; 752f26e8bc9Schristos if (imsg->hdr.type == MSG_DETACHKILL) 753928fc495Schristos client_exitreason = CLIENT_EXIT_DETACHED_HUP; 754d530c4d0Sjmmv else 755928fc495Schristos client_exitreason = CLIENT_EXIT_DETACHED; 756f26e8bc9Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 757698d5317Sjmmv break; 758e9a2d6faSchristos case MSG_EXEC: 759e9a2d6faSchristos if (datalen == 0 || data[datalen - 1] != '\0' || 760e9a2d6faSchristos strlen(data) + 1 == (size_t)datalen) 761e9a2d6faSchristos fatalx("bad MSG_EXEC string"); 762e9a2d6faSchristos client_execcmd = xstrdup(data); 763e9a2d6faSchristos client_execshell = xstrdup(data + strlen(data) + 1); 764e9a2d6faSchristos 765e9a2d6faSchristos client_exittype = imsg->hdr.type; 766e9a2d6faSchristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 767e9a2d6faSchristos break; 768698d5317Sjmmv case MSG_EXIT: 769e271dbb8Schristos client_dispatch_exit_message(data, datalen); 770e271dbb8Schristos if (client_exitreason == CLIENT_EXIT_NONE) 771928fc495Schristos client_exitreason = CLIENT_EXIT_EXITED; 772e271dbb8Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 773698d5317Sjmmv break; 774698d5317Sjmmv case MSG_EXITED: 775698d5317Sjmmv if (datalen != 0) 776698d5317Sjmmv fatalx("bad MSG_EXITED size"); 777698d5317Sjmmv 778f26e8bc9Schristos proc_exit(client_proc); 779f26e8bc9Schristos break; 780698d5317Sjmmv case MSG_SHUTDOWN: 781698d5317Sjmmv if (datalen != 0) 782698d5317Sjmmv fatalx("bad MSG_SHUTDOWN size"); 783698d5317Sjmmv 784f26e8bc9Schristos proc_send(client_peer, MSG_EXITING, -1, NULL, 0); 785928fc495Schristos client_exitreason = CLIENT_EXIT_SERVER_EXITED; 786698d5317Sjmmv client_exitval = 1; 787698d5317Sjmmv break; 788698d5317Sjmmv case MSG_SUSPEND: 789698d5317Sjmmv if (datalen != 0) 790698d5317Sjmmv fatalx("bad MSG_SUSPEND size"); 791698d5317Sjmmv 792698d5317Sjmmv memset(&sigact, 0, sizeof sigact); 793698d5317Sjmmv sigemptyset(&sigact.sa_mask); 794698d5317Sjmmv sigact.sa_flags = SA_RESTART; 795698d5317Sjmmv sigact.sa_handler = SIG_DFL; 796698d5317Sjmmv if (sigaction(SIGTSTP, &sigact, NULL) != 0) 797698d5317Sjmmv fatal("sigaction failed"); 798e271dbb8Schristos client_suspended = 1; 799698d5317Sjmmv kill(getpid(), SIGTSTP); 800698d5317Sjmmv break; 801698d5317Sjmmv case MSG_LOCK: 802928fc495Schristos if (datalen == 0 || data[datalen - 1] != '\0') 803928fc495Schristos fatalx("bad MSG_LOCK string"); 804698d5317Sjmmv 805928fc495Schristos system(data); 806f26e8bc9Schristos proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); 807698d5317Sjmmv break; 808698d5317Sjmmv } 809698d5317Sjmmv } 810