xref: /openbsd/usr.bin/tmux/tmux.c (revision 962d9dda)
1*962d9ddaSnicm /* $OpenBSD: tmux.c,v 1.204 2021/01/17 16:17:41 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm #include <sys/stat.h>
2155bde146Snicm #include <sys/utsname.h>
22311827fbSnicm 
234e7a7adeSnicm #include <err.h>
24311827fbSnicm #include <errno.h>
257724d0b0Snicm #include <event.h>
263808d149Snicm #include <fcntl.h>
273b05404cSnicm #include <langinfo.h>
28622ef518Snicm #include <locale.h>
29311827fbSnicm #include <paths.h>
30311827fbSnicm #include <pwd.h>
31a62a5f61Snicm #include <signal.h>
32311827fbSnicm #include <stdlib.h>
33311827fbSnicm #include <string.h>
345b8ac713Snicm #include <time.h>
35311827fbSnicm #include <unistd.h>
3659538115Snicm #include <util.h>
37311827fbSnicm 
38311827fbSnicm #include "tmux.h"
39311827fbSnicm 
40d89252e5Snicm struct options	*global_options;	/* server options */
41d89252e5Snicm struct options	*global_s_options;	/* session options */
42d89252e5Snicm struct options	*global_w_options;	/* window options */
43fb46cb3dSnicm struct environ	*global_environ;
44311827fbSnicm 
458c1ade38Snicm struct timeval	 start_time;
469c764ad4Snicm const char	*socket_path;
47b6ec3d9fSnicm int		 ptm_fd = -1;
48667452e4Snicm const char	*shell_command;
494ccba3b3Snicm 
509883b791Snicm static __dead void	 usage(void);
51cf04509bSnicm static char		*make_label(const char *, char **);
527724d0b0Snicm 
53e8bf1467Snicm static int		 areshell(const char *);
54ced21769Snicm static const char	*getshell(void);
55ced21769Snicm 
569883b791Snicm static __dead void
57311827fbSnicm usage(void)
58311827fbSnicm {
596ca2ce32Ssobrado 	fprintf(stderr,
60*962d9ddaSnicm 	    "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n"
615a160f88Snicm 	    "            [-S socket-path] [-T features] [command [flags]]\n",
62d3b99136Snicm 	    getprogname());
63311827fbSnicm 	exit(1);
64311827fbSnicm }
65311827fbSnicm 
66ced21769Snicm static const char *
673a5ec08bSnicm getshell(void)
683a5ec08bSnicm {
693a5ec08bSnicm 	struct passwd	*pw;
703a5ec08bSnicm 	const char	*shell;
713a5ec08bSnicm 
723a5ec08bSnicm 	shell = getenv("SHELL");
733a5ec08bSnicm 	if (checkshell(shell))
743a5ec08bSnicm 		return (shell);
753a5ec08bSnicm 
763a5ec08bSnicm 	pw = getpwuid(getuid());
773a5ec08bSnicm 	if (pw != NULL && checkshell(pw->pw_shell))
783a5ec08bSnicm 		return (pw->pw_shell);
793a5ec08bSnicm 
803a5ec08bSnicm 	return (_PATH_BSHELL);
813a5ec08bSnicm }
823a5ec08bSnicm 
83e8bf1467Snicm int
843a5ec08bSnicm checkshell(const char *shell)
853a5ec08bSnicm {
86b81242d0Snicm 	if (shell == NULL || *shell != '/')
87a89a0ceeSnicm 		return (0);
88a89a0ceeSnicm 	if (areshell(shell))
893a5ec08bSnicm 		return (0);
903a5ec08bSnicm 	if (access(shell, X_OK) != 0)
913a5ec08bSnicm 		return (0);
923a5ec08bSnicm 	return (1);
933a5ec08bSnicm }
943a5ec08bSnicm 
95e8bf1467Snicm static int
963a5ec08bSnicm areshell(const char *shell)
973a5ec08bSnicm {
983a5ec08bSnicm 	const char	*progname, *ptr;
993a5ec08bSnicm 
1003a5ec08bSnicm 	if ((ptr = strrchr(shell, '/')) != NULL)
1013a5ec08bSnicm 		ptr++;
1023a5ec08bSnicm 	else
1033a5ec08bSnicm 		ptr = shell;
104d3b99136Snicm 	progname = getprogname();
1053a5ec08bSnicm 	if (*progname == '-')
1063a5ec08bSnicm 		progname++;
1073a5ec08bSnicm 	if (strcmp(ptr, progname) == 0)
1083a5ec08bSnicm 		return (1);
1093a5ec08bSnicm 	return (0);
1103a5ec08bSnicm }
1113a5ec08bSnicm 
1129c764ad4Snicm static char *
1130a580537Snicm expand_path(const char *path, const char *home)
1140a580537Snicm {
1150a580537Snicm 	char			*expanded, *name;
1160a580537Snicm 	const char		*end;
1170a580537Snicm 	struct environ_entry	*value;
1180a580537Snicm 
1190a580537Snicm 	if (strncmp(path, "~/", 2) == 0) {
1200a580537Snicm 		if (home == NULL)
1210a580537Snicm 			return (NULL);
1220a580537Snicm 		xasprintf(&expanded, "%s%s", home, path + 1);
1230a580537Snicm 		return (expanded);
1240a580537Snicm 	}
1250a580537Snicm 
1260a580537Snicm 	if (*path == '$') {
1270a580537Snicm 		end = strchr(path, '/');
1280a580537Snicm 		if (end == NULL)
1290a580537Snicm 			name = xstrdup(path + 1);
1300a580537Snicm 		else
1310a580537Snicm 			name = xstrndup(path + 1, end - path - 1);
1320a580537Snicm 		value = environ_find(global_environ, name);
1330a580537Snicm 		free(name);
1340a580537Snicm 		if (value == NULL)
1350a580537Snicm 			return (NULL);
1360a580537Snicm 		if (end == NULL)
1370a580537Snicm 			end = "";
1380a580537Snicm 		xasprintf(&expanded, "%s%s", value->value, end);
1390a580537Snicm 		return (expanded);
1400a580537Snicm 	}
1410a580537Snicm 
1420a580537Snicm 	return (xstrdup(path));
1430a580537Snicm }
1440a580537Snicm 
1450a580537Snicm void
1460a580537Snicm expand_paths(const char *s, char ***paths, u_int *n)
1470a580537Snicm {
1480a580537Snicm 	const char	*home = find_home();
1490a580537Snicm 	char		*copy, *next, *tmp, resolved[PATH_MAX], *expanded;
1500a580537Snicm 	u_int		 i;
1510a580537Snicm 
1520a580537Snicm 	*paths = NULL;
1530a580537Snicm 	*n = 0;
1540a580537Snicm 
1550a580537Snicm 	copy = tmp = xstrdup(s);
1560a580537Snicm 	while ((next = strsep(&tmp, ":")) != NULL) {
1570a580537Snicm 		expanded = expand_path(next, home);
1580a580537Snicm 		if (expanded == NULL) {
1590a580537Snicm 			log_debug("%s: invalid path: %s", __func__, next);
1600a580537Snicm 			continue;
1610a580537Snicm 		}
1620a580537Snicm 		if (realpath(expanded, resolved) == NULL) {
1630a580537Snicm 			log_debug("%s: realpath(\"%s\") failed: %s", __func__,
1640a580537Snicm 			    expanded, strerror(errno));
1650a580537Snicm 			free(expanded);
1660a580537Snicm 			continue;
1670a580537Snicm 		}
1680a580537Snicm 		free(expanded);
1690a580537Snicm 		for (i = 0; i < *n; i++) {
1700a580537Snicm 			if (strcmp(resolved, (*paths)[i]) == 0)
1710a580537Snicm 				break;
1720a580537Snicm 		}
1730a580537Snicm 		if (i != *n) {
1740a580537Snicm 			log_debug("%s: duplicate path: %s", __func__, resolved);
1750a580537Snicm 			continue;
1760a580537Snicm 		}
1770a580537Snicm 		*paths = xreallocarray(*paths, (*n) + 1, sizeof *paths);
1780a580537Snicm 		(*paths)[(*n)++] = xstrdup(resolved);
1790a580537Snicm 	}
1800a580537Snicm 	free(copy);
1810a580537Snicm }
1820a580537Snicm 
1830a580537Snicm static char *
184cf04509bSnicm make_label(const char *label, char **cause)
185311827fbSnicm {
1860a580537Snicm 	char		**paths, *path, *base;
1870a580537Snicm 	u_int		  i, n;
188311827fbSnicm 	struct stat	  sb;
189da34f2ceSnicm 	uid_t		  uid;
190cf04509bSnicm 
191cf04509bSnicm 	*cause = NULL;
1929c764ad4Snicm 	if (label == NULL)
1939c764ad4Snicm 		label = "default";
194311827fbSnicm 	uid = getuid();
1959c764ad4Snicm 
1960a580537Snicm 	expand_paths(TMUX_SOCK, &paths, &n);
1970a580537Snicm 	if (n == 0) {
1980a580537Snicm 		xasprintf(cause, "no suitable socket path");
1990a580537Snicm 		return (NULL);
200cf04509bSnicm 	}
2010a580537Snicm 	path = paths[0]; /* can only have one socket! */
2020a580537Snicm 	for (i = 1; i < n; i++)
2030a580537Snicm 		free(paths[i]);
2040a580537Snicm 	free(paths);
205311827fbSnicm 
2060a580537Snicm 	xasprintf(&base, "%s/tmux-%ld", path, (long)uid);
2070a580537Snicm 	if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
208cf04509bSnicm 		goto fail;
2090a580537Snicm 	if (lstat(base, &sb) != 0)
2109c764ad4Snicm 		goto fail;
211311827fbSnicm 	if (!S_ISDIR(sb.st_mode)) {
212311827fbSnicm 		errno = ENOTDIR;
2139c764ad4Snicm 		goto fail;
214311827fbSnicm 	}
2159945ad17Snicm 	if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) {
216311827fbSnicm 		errno = EACCES;
2179c764ad4Snicm 		goto fail;
218311827fbSnicm 	}
2190a580537Snicm 	xasprintf(&path, "%s/%s", base, label);
2200a580537Snicm 	free(base);
221311827fbSnicm 	return (path);
2229c764ad4Snicm 
2239c764ad4Snicm fail:
2240a580537Snicm 	xasprintf(cause, "error creating %s (%s)", base, strerror(errno));
2250a580537Snicm 	free(base);
2269c764ad4Snicm 	return (NULL);
227311827fbSnicm }
228311827fbSnicm 
229a0ee4254Snicm void
230a0ee4254Snicm setblocking(int fd, int state)
231a0ee4254Snicm {
232a0ee4254Snicm 	int mode;
233a0ee4254Snicm 
234a0ee4254Snicm 	if ((mode = fcntl(fd, F_GETFL)) != -1) {
235a0ee4254Snicm 		if (!state)
236a0ee4254Snicm 			mode |= O_NONBLOCK;
237a0ee4254Snicm 		else
238a0ee4254Snicm 			mode &= ~O_NONBLOCK;
239a0ee4254Snicm 		fcntl(fd, F_SETFL, mode);
240a0ee4254Snicm 	}
241a0ee4254Snicm }
242a0ee4254Snicm 
243cf8132c5Snicm uint64_t
244cf8132c5Snicm get_timer(void)
245cf8132c5Snicm {
246cf8132c5Snicm 	struct timespec	ts;
247cf8132c5Snicm 
248cf8132c5Snicm 	/*
249cf8132c5Snicm 	 * We want a timestamp in milliseconds suitable for time measurement,
250cf8132c5Snicm 	 * so prefer the monotonic clock.
251cf8132c5Snicm 	 */
252cf8132c5Snicm 	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
253cf8132c5Snicm 		clock_gettime(CLOCK_REALTIME, &ts);
254cf8132c5Snicm 	return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL));
255cf8132c5Snicm }
256cf8132c5Snicm 
257179ef399Snicm const char *
258a62a5f61Snicm sig2name(int signo)
259a62a5f61Snicm {
260a62a5f61Snicm      static char	s[11];
261a62a5f61Snicm 
262a62a5f61Snicm      if (signo > 0 && signo < NSIG)
263a62a5f61Snicm 	     return (sys_signame[signo]);
264a62a5f61Snicm      xsnprintf(s, sizeof s, "%d", signo);
265a62a5f61Snicm      return (s);
266a62a5f61Snicm }
267a62a5f61Snicm 
268a62a5f61Snicm const char *
269ca2738caSnicm find_cwd(void)
270ca2738caSnicm {
271ca2738caSnicm 	char		 resolved1[PATH_MAX], resolved2[PATH_MAX];
272ca2738caSnicm 	static char	 cwd[PATH_MAX];
273ca2738caSnicm 	const char	*pwd;
274ca2738caSnicm 
275ca2738caSnicm 	if (getcwd(cwd, sizeof cwd) == NULL)
276ca2738caSnicm 		return (NULL);
277ca2738caSnicm 	if ((pwd = getenv("PWD")) == NULL || *pwd == '\0')
278ca2738caSnicm 		return (cwd);
279ca2738caSnicm 
280ca2738caSnicm 	/*
281ca2738caSnicm 	 * We want to use PWD so that symbolic links are maintained,
282ca2738caSnicm 	 * but only if it matches the actual working directory.
283ca2738caSnicm 	 */
284ca2738caSnicm 	if (realpath(pwd, resolved1) == NULL)
285ca2738caSnicm 		return (cwd);
286ca2738caSnicm 	if (realpath(cwd, resolved2) == NULL)
287ca2738caSnicm 		return (cwd);
288ca2738caSnicm 	if (strcmp(resolved1, resolved2) != 0)
289ca2738caSnicm 		return (cwd);
290ca2738caSnicm 	return (pwd);
291ca2738caSnicm }
292ca2738caSnicm 
293ca2738caSnicm const char *
294179ef399Snicm find_home(void)
295179ef399Snicm {
296179ef399Snicm 	struct passwd		*pw;
297f038d384Snicm 	static const char	*home;
298f038d384Snicm 
299f038d384Snicm 	if (home != NULL)
300f038d384Snicm 		return (home);
301179ef399Snicm 
302179ef399Snicm 	home = getenv("HOME");
303179ef399Snicm 	if (home == NULL || *home == '\0') {
304179ef399Snicm 		pw = getpwuid(getuid());
305179ef399Snicm 		if (pw != NULL)
306179ef399Snicm 			home = pw->pw_dir;
307179ef399Snicm 		else
308179ef399Snicm 			home = NULL;
309179ef399Snicm 	}
310179ef399Snicm 
31182bdfec0Snicm 	return (home);
312179ef399Snicm }
313179ef399Snicm 
31455bde146Snicm const char *
31555bde146Snicm getversion(void)
31655bde146Snicm {
31755bde146Snicm 	static char	*version;
31855bde146Snicm 	struct utsname	 u;
31955bde146Snicm 
32055bde146Snicm 	if (version == NULL) {
32155bde146Snicm 		if (uname(&u) < 0)
32255bde146Snicm 			fatalx("uname failed");
32355bde146Snicm 		xasprintf(&version, "openbsd-%s", u.release);
32455bde146Snicm 	}
325acf6cf7cSnicm 	return (version);
32655bde146Snicm }
32755bde146Snicm 
328311827fbSnicm int
329311827fbSnicm main(int argc, char **argv)
330311827fbSnicm {
3315a160f88Snicm 	char					*path = NULL, *label = NULL;
3325a160f88Snicm 	char					*cause, **var;
33338672859Snicm 	const char				*s, *shell, *cwd;
334d337e2efSnicm 	int					 opt, keys, feat = 0;
335d337e2efSnicm 	uint64_t				 flags = 0;
33658eb4b5dSnicm 	const struct options_table_entry	*oe;
337311827fbSnicm 
338662faea6Snicm 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
339662faea6Snicm 	    setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
3403b05404cSnicm 		if (setlocale(LC_CTYPE, "") == NULL)
3413b05404cSnicm 			errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
3423b05404cSnicm 		s = nl_langinfo(CODESET);
3435fcc80c9Snicm 		if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0)
3443b05404cSnicm 			errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s);
3453b05404cSnicm 	}
346f5015ed6Snicm 
34731a85510Snicm 	setlocale(LC_TIME, "");
3485396add0Snicm 	tzset();
349622ef518Snicm 
3506b014674Snicm 	if (**argv == '-')
3516b014674Snicm 		flags = CLIENT_LOGIN;
3526b014674Snicm 
353*962d9ddaSnicm 	while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
354311827fbSnicm 		switch (opt) {
355311827fbSnicm 		case '2':
3565a160f88Snicm 			tty_add_features(&feat, "256", ":,");
357311827fbSnicm 			break;
358ef8ef121Snicm 		case 'c':
359667452e4Snicm 			shell_command = optarg;
360ef8ef121Snicm 			break;
3619e0637eaSnicm 		case 'D':
3629e0637eaSnicm 			flags |= CLIENT_NOFORK;
3639e0637eaSnicm 			break;
36490896992Snicm 		case 'C':
365cafa7ad7Snicm 			if (flags & CLIENT_CONTROL)
366cafa7ad7Snicm 				flags |= CLIENT_CONTROLCONTROL;
36790896992Snicm 			else
368cafa7ad7Snicm 				flags |= CLIENT_CONTROL;
36990896992Snicm 			break;
370311827fbSnicm 		case 'f':
371f038d384Snicm 			set_cfg_file(optarg);
372311827fbSnicm 			break;
37355bde146Snicm  		case 'V':
37455bde146Snicm 			printf("%s %s\n", getprogname(), getversion());
37555bde146Snicm  			exit(0);
37665b1f011Snicm 		case 'l':
3776b014674Snicm 			flags |= CLIENT_LOGIN;
37865b1f011Snicm 			break;
379311827fbSnicm 		case 'L':
3807d053cf9Snicm 			free(label);
381311827fbSnicm 			label = xstrdup(optarg);
382311827fbSnicm 			break;
383*962d9ddaSnicm 		case 'N':
384*962d9ddaSnicm 			flags |= CLIENT_NOSTARTSERVER;
385*962d9ddaSnicm 			break;
386481abfa9Snicm 		case 'q':
387481abfa9Snicm 			break;
388311827fbSnicm 		case 'S':
3897d053cf9Snicm 			free(path);
390311827fbSnicm 			path = xstrdup(optarg);
391311827fbSnicm 			break;
3925a160f88Snicm 		case 'T':
3935a160f88Snicm 			tty_add_features(&feat, optarg, ":,");
3945a160f88Snicm 			break;
395311827fbSnicm 		case 'u':
396cafa7ad7Snicm 			flags |= CLIENT_UTF8;
397311827fbSnicm 			break;
398311827fbSnicm 		case 'v':
3990a607e68Snicm 			log_add_level();
400311827fbSnicm 			break;
401311827fbSnicm 		default:
402311827fbSnicm 			usage();
403311827fbSnicm 		}
404311827fbSnicm 	}
405311827fbSnicm 	argc -= optind;
406311827fbSnicm 	argv += optind;
407311827fbSnicm 
408667452e4Snicm 	if (shell_command != NULL && argc != 0)
409ef8ef121Snicm 		usage();
4109e0637eaSnicm 	if ((flags & CLIENT_NOFORK) && argc != 0)
4119e0637eaSnicm 		usage();
412ef8ef121Snicm 
41359538115Snicm 	if ((ptm_fd = getptmfd()) == -1)
41459538115Snicm 		err(1, "getptmfd");
415cb19d99cSnicm 	if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
416cb19d99cSnicm 	    "recvfd proc exec tty ps", NULL) != 0)
4174e7a7adeSnicm 		err(1, "pledge");
4184e7a7adeSnicm 
419461e0674Snicm 	/*
420f6fb1232Snicm 	 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
421f6fb1232Snicm 	 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
422f6fb1232Snicm 	 * UTF-8, it is a safe assumption that either they are using a UTF-8
423f6fb1232Snicm 	 * terminal, or if not they know that output from UTF-8-capable
424f6fb1232Snicm 	 * programs may be wrong.
425461e0674Snicm 	 */
426f6fb1232Snicm 	if (getenv("TMUX") != NULL)
427f6fb1232Snicm 		flags |= CLIENT_UTF8;
428f6fb1232Snicm 	else {
429f6fb1232Snicm 		s = getenv("LC_ALL");
430f6fb1232Snicm 		if (s == NULL || *s == '\0')
431f6fb1232Snicm 			s = getenv("LC_CTYPE");
432f6fb1232Snicm 		if (s == NULL || *s == '\0')
433461e0674Snicm 			s = getenv("LANG");
434f6fb1232Snicm 		if (s == NULL || *s == '\0')
435f6fb1232Snicm 			s = "";
436f6fb1232Snicm 		if (strcasestr(s, "UTF-8") != NULL ||
437f6fb1232Snicm 		    strcasestr(s, "UTF8") != NULL)
438cafa7ad7Snicm 			flags |= CLIENT_UTF8;
439461e0674Snicm 	}
440461e0674Snicm 
441fb46cb3dSnicm 	global_environ = environ_create();
442bd0e97c4Snicm 	for (var = environ; *var != NULL; var++)
443d6f6a5d2Snicm 		environ_put(global_environ, *var, 0);
444ca2738caSnicm 	if ((cwd = find_cwd()) != NULL)
445d6f6a5d2Snicm 		environ_set(global_environ, "PWD", 0, "%s", cwd);
446bd0e97c4Snicm 
447d89252e5Snicm 	global_options = options_create(NULL);
448d89252e5Snicm 	global_s_options = options_create(NULL);
449d89252e5Snicm 	global_w_options = options_create(NULL);
45058eb4b5dSnicm 	for (oe = options_table; oe->name != NULL; oe++) {
4516e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_SERVER)
45258eb4b5dSnicm 			options_default(global_options, oe);
4536e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_SESSION)
45458eb4b5dSnicm 			options_default(global_s_options, oe);
4556e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_WINDOW)
45658eb4b5dSnicm 			options_default(global_w_options, oe);
45758eb4b5dSnicm 	}
45858eb4b5dSnicm 
45958eb4b5dSnicm 	/*
46058eb4b5dSnicm 	 * The default shell comes from SHELL or from the user's passwd entry
46158eb4b5dSnicm 	 * if available.
46258eb4b5dSnicm 	 */
46358eb4b5dSnicm 	shell = getshell();
46458eb4b5dSnicm 	options_set_string(global_s_options, "default-shell", 0, "%s", shell);
4653affa6cbSnicm 
4663affa6cbSnicm 	/* Override keys to vi if VISUAL or EDITOR are set. */
467c9347692Snicm 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
468a6c9106fSnicm 		options_set_string(global_options, "editor", 0, "%s", s);
469c9347692Snicm 		if (strrchr(s, '/') != NULL)
470c9347692Snicm 			s = strrchr(s, '/') + 1;
471c9347692Snicm 		if (strstr(s, "vi") != NULL)
472c9347692Snicm 			keys = MODEKEY_VI;
4733affa6cbSnicm 		else
4743affa6cbSnicm 			keys = MODEKEY_EMACS;
475d89252e5Snicm 		options_set_number(global_s_options, "status-keys", keys);
476d89252e5Snicm 		options_set_number(global_w_options, "mode-keys", keys);
477c9347692Snicm 	}
478c9347692Snicm 
4792fa50cfcSnicm 	/*
4809c764ad4Snicm 	 * If socket is specified on the command-line with -S or -L, it is
4819c764ad4Snicm 	 * used. Otherwise, $TMUX is checked and if that fails "default" is
4829c764ad4Snicm 	 * used.
4832fa50cfcSnicm 	 */
4849c764ad4Snicm 	if (path == NULL && label == NULL) {
4854fa31997Snicm 		s = getenv("TMUX");
4869c764ad4Snicm 		if (s != NULL && *s != '\0' && *s != ',') {
4874fa31997Snicm 			path = xstrdup(s);
4884fa31997Snicm 			path[strcspn(path, ",")] = '\0';
4894fa31997Snicm 		}
4902fa50cfcSnicm 	}
491b6d7e0c7Snicm 	if (path == NULL) {
492b6d7e0c7Snicm 		if ((path = make_label(label, &cause)) == NULL) {
493cf04509bSnicm 			if (cause != NULL) {
494cf04509bSnicm 				fprintf(stderr, "%s\n", cause);
495cf04509bSnicm 				free(cause);
496cf04509bSnicm 			}
497311827fbSnicm 			exit(1);
498311827fbSnicm 		}
499b6d7e0c7Snicm 		flags |= CLIENT_DEFAULTSOCKET;
500b6d7e0c7Snicm 	}
5019c764ad4Snicm 	socket_path = path;
5027d053cf9Snicm 	free(label);
5036f631021Snicm 
50465439d22Snicm 	/* Pass control to the client. */
5055a160f88Snicm 	exit(client_main(event_init(), argc, argv, flags, feat));
506fd234c13Snicm }
507