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