xref: /openbsd/usr.bin/tmux/tmux.c (revision 611df774)
1*611df774Snicm /* $OpenBSD: tmux.c,v 1.212 2024/05/15 09:59:12 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
usage(void)57311827fbSnicm usage(void)
58311827fbSnicm {
596ca2ce32Ssobrado 	fprintf(stderr,
60b303ddd0Sjmc 	    "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 *
getshell(void)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
checkshell(const char * shell)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
areshell(const char * shell)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 *
expand_path(const char * path,const char * home)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 
145b8e6286cSnicm static void
expand_paths(const char * s,char *** paths,u_int * n,int ignore_errors)146b8e6286cSnicm expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors)
1470a580537Snicm {
1480a580537Snicm 	const char	*home = find_home();
1490a580537Snicm 	char		*copy, *next, *tmp, resolved[PATH_MAX], *expanded;
150b8e6286cSnicm 	char		*path;
1510a580537Snicm 	u_int		 i;
1520a580537Snicm 
1530a580537Snicm 	*paths = NULL;
1540a580537Snicm 	*n = 0;
1550a580537Snicm 
1560a580537Snicm 	copy = tmp = xstrdup(s);
1570a580537Snicm 	while ((next = strsep(&tmp, ":")) != NULL) {
1580a580537Snicm 		expanded = expand_path(next, home);
1590a580537Snicm 		if (expanded == NULL) {
1600a580537Snicm 			log_debug("%s: invalid path: %s", __func__, next);
1610a580537Snicm 			continue;
1620a580537Snicm 		}
1630a580537Snicm 		if (realpath(expanded, resolved) == NULL) {
1640a580537Snicm 			log_debug("%s: realpath(\"%s\") failed: %s", __func__,
1650a580537Snicm 			    expanded, strerror(errno));
166b8e6286cSnicm 			if (ignore_errors) {
1670a580537Snicm 				free(expanded);
1680a580537Snicm 				continue;
1690a580537Snicm 			}
170b8e6286cSnicm 			path = expanded;
171b8e6286cSnicm 		} else {
172b8e6286cSnicm 			path = xstrdup(resolved);
1730a580537Snicm 			free(expanded);
174b8e6286cSnicm 		}
1750a580537Snicm 		for (i = 0; i < *n; i++) {
176b8e6286cSnicm 			if (strcmp(path, (*paths)[i]) == 0)
1770a580537Snicm 				break;
1780a580537Snicm 		}
1790a580537Snicm 		if (i != *n) {
180b8e6286cSnicm 			log_debug("%s: duplicate path: %s", __func__, path);
181b8e6286cSnicm 			free(path);
1820a580537Snicm 			continue;
1830a580537Snicm 		}
1840a580537Snicm 		*paths = xreallocarray(*paths, (*n) + 1, sizeof *paths);
185b8e6286cSnicm 		(*paths)[(*n)++] = path;
1860a580537Snicm 	}
1870a580537Snicm 	free(copy);
1880a580537Snicm }
1890a580537Snicm 
1900a580537Snicm static char *
make_label(const char * label,char ** cause)191cf04509bSnicm make_label(const char *label, char **cause)
192311827fbSnicm {
1930a580537Snicm 	char		**paths, *path, *base;
1940a580537Snicm 	u_int		  i, n;
195311827fbSnicm 	struct stat	  sb;
196da34f2ceSnicm 	uid_t		  uid;
197cf04509bSnicm 
198cf04509bSnicm 	*cause = NULL;
1999c764ad4Snicm 	if (label == NULL)
2009c764ad4Snicm 		label = "default";
201311827fbSnicm 	uid = getuid();
2029c764ad4Snicm 
203b8e6286cSnicm 	expand_paths(TMUX_SOCK, &paths, &n, 1);
2040a580537Snicm 	if (n == 0) {
2050a580537Snicm 		xasprintf(cause, "no suitable socket path");
2060a580537Snicm 		return (NULL);
207cf04509bSnicm 	}
2080a580537Snicm 	path = paths[0]; /* can only have one socket! */
2090a580537Snicm 	for (i = 1; i < n; i++)
2100a580537Snicm 		free(paths[i]);
2110a580537Snicm 	free(paths);
212311827fbSnicm 
2130a580537Snicm 	xasprintf(&base, "%s/tmux-%ld", path, (long)uid);
2141d297f78Snicm 	free(path);
215b860381cSnicm 	if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) {
216b860381cSnicm 		xasprintf(cause, "couldn't create directory %s (%s)", base,
217b860381cSnicm 		    strerror(errno));
218cf04509bSnicm 		goto fail;
219b860381cSnicm 	}
220b860381cSnicm 	if (lstat(base, &sb) != 0) {
221b860381cSnicm 		xasprintf(cause, "couldn't read directory %s (%s)", base,
222b860381cSnicm 		    strerror(errno));
2239c764ad4Snicm 		goto fail;
224b860381cSnicm 	}
225311827fbSnicm 	if (!S_ISDIR(sb.st_mode)) {
226b860381cSnicm 		xasprintf(cause, "%s is not a directory", base);
2279c764ad4Snicm 		goto fail;
228311827fbSnicm 	}
2299945ad17Snicm 	if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) {
230b860381cSnicm 		xasprintf(cause, "directory %s has unsafe permissions", base);
2319c764ad4Snicm 		goto fail;
232311827fbSnicm 	}
2330a580537Snicm 	xasprintf(&path, "%s/%s", base, label);
2340a580537Snicm 	free(base);
235311827fbSnicm 	return (path);
2369c764ad4Snicm 
2379c764ad4Snicm fail:
2380a580537Snicm 	free(base);
2399c764ad4Snicm 	return (NULL);
240311827fbSnicm }
241311827fbSnicm 
242*611df774Snicm char *
shell_argv0(const char * shell,int is_login)243*611df774Snicm shell_argv0(const char *shell, int is_login)
244*611df774Snicm {
245*611df774Snicm 	const char	*slash, *name;
246*611df774Snicm 	char		*argv0;
247*611df774Snicm 
248*611df774Snicm 	slash = strrchr(shell, '/');
249*611df774Snicm 	if (slash != NULL && slash[1] != '\0')
250*611df774Snicm 		name = slash + 1;
251*611df774Snicm 	else
252*611df774Snicm 		name = shell;
253*611df774Snicm 	if (is_login)
254*611df774Snicm 		xasprintf(&argv0, "-%s", name);
255*611df774Snicm 	else
256*611df774Snicm 		xasprintf(&argv0, "%s", name);
257*611df774Snicm 	return (argv0);
258*611df774Snicm }
259*611df774Snicm 
260a0ee4254Snicm void
setblocking(int fd,int state)261a0ee4254Snicm setblocking(int fd, int state)
262a0ee4254Snicm {
263a0ee4254Snicm 	int mode;
264a0ee4254Snicm 
265a0ee4254Snicm 	if ((mode = fcntl(fd, F_GETFL)) != -1) {
266a0ee4254Snicm 		if (!state)
267a0ee4254Snicm 			mode |= O_NONBLOCK;
268a0ee4254Snicm 		else
269a0ee4254Snicm 			mode &= ~O_NONBLOCK;
270a0ee4254Snicm 		fcntl(fd, F_SETFL, mode);
271a0ee4254Snicm 	}
272a0ee4254Snicm }
273a0ee4254Snicm 
274cf8132c5Snicm uint64_t
get_timer(void)275cf8132c5Snicm get_timer(void)
276cf8132c5Snicm {
277cf8132c5Snicm 	struct timespec	ts;
278cf8132c5Snicm 
279cf8132c5Snicm 	/*
280cf8132c5Snicm 	 * We want a timestamp in milliseconds suitable for time measurement,
281cf8132c5Snicm 	 * so prefer the monotonic clock.
282cf8132c5Snicm 	 */
283cf8132c5Snicm 	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
284cf8132c5Snicm 		clock_gettime(CLOCK_REALTIME, &ts);
285cf8132c5Snicm 	return ((ts.tv_sec * 1000ULL) + (ts.tv_nsec / 1000000ULL));
286cf8132c5Snicm }
287cf8132c5Snicm 
288179ef399Snicm const char *
sig2name(int signo)289a62a5f61Snicm sig2name(int signo)
290a62a5f61Snicm {
291a62a5f61Snicm      static char	s[11];
292a62a5f61Snicm 
293a62a5f61Snicm      if (signo > 0 && signo < NSIG)
294a62a5f61Snicm 	     return (sys_signame[signo]);
295a62a5f61Snicm      xsnprintf(s, sizeof s, "%d", signo);
296a62a5f61Snicm      return (s);
297a62a5f61Snicm }
298a62a5f61Snicm 
299a62a5f61Snicm const char *
find_cwd(void)300ca2738caSnicm find_cwd(void)
301ca2738caSnicm {
302ca2738caSnicm 	char		 resolved1[PATH_MAX], resolved2[PATH_MAX];
303ca2738caSnicm 	static char	 cwd[PATH_MAX];
304ca2738caSnicm 	const char	*pwd;
305ca2738caSnicm 
306ca2738caSnicm 	if (getcwd(cwd, sizeof cwd) == NULL)
307ca2738caSnicm 		return (NULL);
308ca2738caSnicm 	if ((pwd = getenv("PWD")) == NULL || *pwd == '\0')
309ca2738caSnicm 		return (cwd);
310ca2738caSnicm 
311ca2738caSnicm 	/*
312ca2738caSnicm 	 * We want to use PWD so that symbolic links are maintained,
313ca2738caSnicm 	 * but only if it matches the actual working directory.
314ca2738caSnicm 	 */
315ca2738caSnicm 	if (realpath(pwd, resolved1) == NULL)
316ca2738caSnicm 		return (cwd);
317ca2738caSnicm 	if (realpath(cwd, resolved2) == NULL)
318ca2738caSnicm 		return (cwd);
319ca2738caSnicm 	if (strcmp(resolved1, resolved2) != 0)
320ca2738caSnicm 		return (cwd);
321ca2738caSnicm 	return (pwd);
322ca2738caSnicm }
323ca2738caSnicm 
324ca2738caSnicm const char *
find_home(void)325179ef399Snicm find_home(void)
326179ef399Snicm {
327179ef399Snicm 	struct passwd		*pw;
328f038d384Snicm 	static const char	*home;
329f038d384Snicm 
330f038d384Snicm 	if (home != NULL)
331f038d384Snicm 		return (home);
332179ef399Snicm 
333179ef399Snicm 	home = getenv("HOME");
334179ef399Snicm 	if (home == NULL || *home == '\0') {
335179ef399Snicm 		pw = getpwuid(getuid());
336179ef399Snicm 		if (pw != NULL)
337179ef399Snicm 			home = pw->pw_dir;
338179ef399Snicm 		else
339179ef399Snicm 			home = NULL;
340179ef399Snicm 	}
341179ef399Snicm 
34282bdfec0Snicm 	return (home);
343179ef399Snicm }
344179ef399Snicm 
34555bde146Snicm const char *
getversion(void)34655bde146Snicm getversion(void)
34755bde146Snicm {
34855bde146Snicm 	static char	*version;
34955bde146Snicm 	struct utsname	 u;
35055bde146Snicm 
35155bde146Snicm 	if (version == NULL) {
35255bde146Snicm 		if (uname(&u) < 0)
35355bde146Snicm 			fatalx("uname failed");
35455bde146Snicm 		xasprintf(&version, "openbsd-%s", u.release);
35555bde146Snicm 	}
356acf6cf7cSnicm 	return (version);
35755bde146Snicm }
35855bde146Snicm 
359311827fbSnicm int
main(int argc,char ** argv)360311827fbSnicm main(int argc, char **argv)
361311827fbSnicm {
3625a160f88Snicm 	char					*path = NULL, *label = NULL;
3635a160f88Snicm 	char					*cause, **var;
364b8e6286cSnicm 	const char				*s, *cwd;
3651e189e66Snicm 	int					 opt, keys, feat = 0, fflag = 0;
366d337e2efSnicm 	uint64_t				 flags = 0;
36758eb4b5dSnicm 	const struct options_table_entry	*oe;
368b8e6286cSnicm 	u_int					 i;
369311827fbSnicm 
370662faea6Snicm 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
371662faea6Snicm 	    setlocale(LC_CTYPE, "C.UTF-8") == NULL) {
3723b05404cSnicm 		if (setlocale(LC_CTYPE, "") == NULL)
3733b05404cSnicm 			errx(1, "invalid LC_ALL, LC_CTYPE or LANG");
3743b05404cSnicm 		s = nl_langinfo(CODESET);
3755fcc80c9Snicm 		if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0)
3763b05404cSnicm 			errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s);
3773b05404cSnicm 	}
378f5015ed6Snicm 
37931a85510Snicm 	setlocale(LC_TIME, "");
3805396add0Snicm 	tzset();
381622ef518Snicm 
3826b014674Snicm 	if (**argv == '-')
3836b014674Snicm 		flags = CLIENT_LOGIN;
384cc79f9fdSnicm 
385cc79f9fdSnicm 	global_environ = environ_create();
386cc79f9fdSnicm 	for (var = environ; *var != NULL; var++)
387cc79f9fdSnicm 		environ_put(global_environ, *var, 0);
388cc79f9fdSnicm 	if ((cwd = find_cwd()) != NULL)
389cc79f9fdSnicm 		environ_set(global_environ, "PWD", 0, "%s", cwd);
390b8e6286cSnicm 	expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1);
3916b014674Snicm 
392962d9ddaSnicm 	while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) {
393311827fbSnicm 		switch (opt) {
394311827fbSnicm 		case '2':
3955a160f88Snicm 			tty_add_features(&feat, "256", ":,");
396311827fbSnicm 			break;
397ef8ef121Snicm 		case 'c':
398667452e4Snicm 			shell_command = optarg;
399ef8ef121Snicm 			break;
4009e0637eaSnicm 		case 'D':
4019e0637eaSnicm 			flags |= CLIENT_NOFORK;
4029e0637eaSnicm 			break;
40390896992Snicm 		case 'C':
404cafa7ad7Snicm 			if (flags & CLIENT_CONTROL)
405cafa7ad7Snicm 				flags |= CLIENT_CONTROLCONTROL;
40690896992Snicm 			else
407cafa7ad7Snicm 				flags |= CLIENT_CONTROL;
40890896992Snicm 			break;
409311827fbSnicm 		case 'f':
4101e189e66Snicm 			if (!fflag) {
4111e189e66Snicm 				fflag = 1;
412b8e6286cSnicm 				for (i = 0; i < cfg_nfiles; i++)
413b8e6286cSnicm 					free(cfg_files[i]);
4141e189e66Snicm 				cfg_nfiles = 0;
4151e189e66Snicm 			}
4161e189e66Snicm 			cfg_files = xreallocarray(cfg_files, cfg_nfiles + 1,
4171e189e66Snicm 			    sizeof *cfg_files);
4181e189e66Snicm 			cfg_files[cfg_nfiles++] = xstrdup(optarg);
419b8e6286cSnicm 			cfg_quiet = 0;
420311827fbSnicm 			break;
42155bde146Snicm  		case 'V':
422df816d48Snicm 			printf("tmux %s\n", getversion());
42355bde146Snicm  			exit(0);
42465b1f011Snicm 		case 'l':
4256b014674Snicm 			flags |= CLIENT_LOGIN;
42665b1f011Snicm 			break;
427311827fbSnicm 		case 'L':
4287d053cf9Snicm 			free(label);
429311827fbSnicm 			label = xstrdup(optarg);
430311827fbSnicm 			break;
431962d9ddaSnicm 		case 'N':
432962d9ddaSnicm 			flags |= CLIENT_NOSTARTSERVER;
433962d9ddaSnicm 			break;
434481abfa9Snicm 		case 'q':
435481abfa9Snicm 			break;
436311827fbSnicm 		case 'S':
4377d053cf9Snicm 			free(path);
438311827fbSnicm 			path = xstrdup(optarg);
439311827fbSnicm 			break;
4405a160f88Snicm 		case 'T':
4415a160f88Snicm 			tty_add_features(&feat, optarg, ":,");
4425a160f88Snicm 			break;
443311827fbSnicm 		case 'u':
444cafa7ad7Snicm 			flags |= CLIENT_UTF8;
445311827fbSnicm 			break;
446311827fbSnicm 		case 'v':
4470a607e68Snicm 			log_add_level();
448311827fbSnicm 			break;
449311827fbSnicm 		default:
450311827fbSnicm 			usage();
451311827fbSnicm 		}
452311827fbSnicm 	}
453311827fbSnicm 	argc -= optind;
454311827fbSnicm 	argv += optind;
455311827fbSnicm 
456667452e4Snicm 	if (shell_command != NULL && argc != 0)
457ef8ef121Snicm 		usage();
4589e0637eaSnicm 	if ((flags & CLIENT_NOFORK) && argc != 0)
4599e0637eaSnicm 		usage();
460ef8ef121Snicm 
46159538115Snicm 	if ((ptm_fd = getptmfd()) == -1)
46259538115Snicm 		err(1, "getptmfd");
463cb19d99cSnicm 	if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd "
464cb19d99cSnicm 	    "recvfd proc exec tty ps", NULL) != 0)
4654e7a7adeSnicm 		err(1, "pledge");
4664e7a7adeSnicm 
467461e0674Snicm 	/*
468f6fb1232Snicm 	 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8.
469f6fb1232Snicm 	 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain
470f6fb1232Snicm 	 * UTF-8, it is a safe assumption that either they are using a UTF-8
471f6fb1232Snicm 	 * terminal, or if not they know that output from UTF-8-capable
472f6fb1232Snicm 	 * programs may be wrong.
473461e0674Snicm 	 */
474f6fb1232Snicm 	if (getenv("TMUX") != NULL)
475f6fb1232Snicm 		flags |= CLIENT_UTF8;
476f6fb1232Snicm 	else {
477f6fb1232Snicm 		s = getenv("LC_ALL");
478f6fb1232Snicm 		if (s == NULL || *s == '\0')
479f6fb1232Snicm 			s = getenv("LC_CTYPE");
480f6fb1232Snicm 		if (s == NULL || *s == '\0')
481461e0674Snicm 			s = getenv("LANG");
482f6fb1232Snicm 		if (s == NULL || *s == '\0')
483f6fb1232Snicm 			s = "";
484f6fb1232Snicm 		if (strcasestr(s, "UTF-8") != NULL ||
485f6fb1232Snicm 		    strcasestr(s, "UTF8") != NULL)
486cafa7ad7Snicm 			flags |= CLIENT_UTF8;
487461e0674Snicm 	}
488461e0674Snicm 
489d89252e5Snicm 	global_options = options_create(NULL);
490d89252e5Snicm 	global_s_options = options_create(NULL);
491d89252e5Snicm 	global_w_options = options_create(NULL);
49258eb4b5dSnicm 	for (oe = options_table; oe->name != NULL; oe++) {
4936e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_SERVER)
49458eb4b5dSnicm 			options_default(global_options, oe);
4956e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_SESSION)
49658eb4b5dSnicm 			options_default(global_s_options, oe);
4976e0f28f8Snicm 		if (oe->scope & OPTIONS_TABLE_WINDOW)
49858eb4b5dSnicm 			options_default(global_w_options, oe);
49958eb4b5dSnicm 	}
50058eb4b5dSnicm 
50158eb4b5dSnicm 	/*
50258eb4b5dSnicm 	 * The default shell comes from SHELL or from the user's passwd entry
50358eb4b5dSnicm 	 * if available.
50458eb4b5dSnicm 	 */
505b8e6286cSnicm 	options_set_string(global_s_options, "default-shell", 0, "%s",
506b8e6286cSnicm 	    getshell());
5073affa6cbSnicm 
5083affa6cbSnicm 	/* Override keys to vi if VISUAL or EDITOR are set. */
509c9347692Snicm 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
510a6c9106fSnicm 		options_set_string(global_options, "editor", 0, "%s", s);
511c9347692Snicm 		if (strrchr(s, '/') != NULL)
512c9347692Snicm 			s = strrchr(s, '/') + 1;
513c9347692Snicm 		if (strstr(s, "vi") != NULL)
514c9347692Snicm 			keys = MODEKEY_VI;
5153affa6cbSnicm 		else
5163affa6cbSnicm 			keys = MODEKEY_EMACS;
517d89252e5Snicm 		options_set_number(global_s_options, "status-keys", keys);
518d89252e5Snicm 		options_set_number(global_w_options, "mode-keys", keys);
519c9347692Snicm 	}
520c9347692Snicm 
5212fa50cfcSnicm 	/*
5229c764ad4Snicm 	 * If socket is specified on the command-line with -S or -L, it is
5239c764ad4Snicm 	 * used. Otherwise, $TMUX is checked and if that fails "default" is
5249c764ad4Snicm 	 * used.
5252fa50cfcSnicm 	 */
5269c764ad4Snicm 	if (path == NULL && label == NULL) {
5274fa31997Snicm 		s = getenv("TMUX");
5289c764ad4Snicm 		if (s != NULL && *s != '\0' && *s != ',') {
5294fa31997Snicm 			path = xstrdup(s);
5304fa31997Snicm 			path[strcspn(path, ",")] = '\0';
5314fa31997Snicm 		}
5322fa50cfcSnicm 	}
533b6d7e0c7Snicm 	if (path == NULL) {
534b6d7e0c7Snicm 		if ((path = make_label(label, &cause)) == NULL) {
535cf04509bSnicm 			if (cause != NULL) {
536cf04509bSnicm 				fprintf(stderr, "%s\n", cause);
537cf04509bSnicm 				free(cause);
538cf04509bSnicm 			}
539311827fbSnicm 			exit(1);
540311827fbSnicm 		}
541b6d7e0c7Snicm 		flags |= CLIENT_DEFAULTSOCKET;
542b6d7e0c7Snicm 	}
5439c764ad4Snicm 	socket_path = path;
5447d053cf9Snicm 	free(label);
5456f631021Snicm 
54665439d22Snicm 	/* Pass control to the client. */
5475a160f88Snicm 	exit(client_main(event_init(), argc, argv, flags, feat));
548fd234c13Snicm }
549