1*ced21769Snicm /* $OpenBSD: tmux.c,v 1.172 2016/10/11 13:21:59 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> 21311827fbSnicm 224e7a7adeSnicm #include <err.h> 23311827fbSnicm #include <errno.h> 247724d0b0Snicm #include <event.h> 253808d149Snicm #include <fcntl.h> 2626529d6fSnicm #include <getopt.h> 273b05404cSnicm #include <langinfo.h> 28622ef518Snicm #include <locale.h> 29311827fbSnicm #include <paths.h> 30311827fbSnicm #include <pwd.h> 31311827fbSnicm #include <stdlib.h> 32311827fbSnicm #include <string.h> 335b8ac713Snicm #include <time.h> 34311827fbSnicm #include <unistd.h> 35311827fbSnicm 36311827fbSnicm #include "tmux.h" 37311827fbSnicm 38d89252e5Snicm struct options *global_options; /* server options */ 39d89252e5Snicm struct options *global_s_options; /* session options */ 40d89252e5Snicm struct options *global_w_options; /* window options */ 41fb46cb3dSnicm struct environ *global_environ; 421443aefcSnicm struct hooks *global_hooks; 43311827fbSnicm 448c1ade38Snicm struct timeval start_time; 459c764ad4Snicm const char *socket_path; 464ccba3b3Snicm 479883b791Snicm static __dead void usage(void); 489c764ad4Snicm static char *make_label(const char *); 497724d0b0Snicm 50*ced21769Snicm static const char *getshell(void); 51*ced21769Snicm static int checkshell(const char *); 52*ced21769Snicm 539883b791Snicm static __dead void 54311827fbSnicm usage(void) 55311827fbSnicm { 566ca2ce32Ssobrado fprintf(stderr, 573da585bcSjmc "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 5865b1f011Snicm " [-S socket-path] [command [flags]]\n", 59d3b99136Snicm getprogname()); 60311827fbSnicm exit(1); 61311827fbSnicm } 62311827fbSnicm 63*ced21769Snicm static const char * 643a5ec08bSnicm getshell(void) 653a5ec08bSnicm { 663a5ec08bSnicm struct passwd *pw; 673a5ec08bSnicm const char *shell; 683a5ec08bSnicm 693a5ec08bSnicm shell = getenv("SHELL"); 703a5ec08bSnicm if (checkshell(shell)) 713a5ec08bSnicm return (shell); 723a5ec08bSnicm 733a5ec08bSnicm pw = getpwuid(getuid()); 743a5ec08bSnicm if (pw != NULL && checkshell(pw->pw_shell)) 753a5ec08bSnicm return (pw->pw_shell); 763a5ec08bSnicm 773a5ec08bSnicm return (_PATH_BSHELL); 783a5ec08bSnicm } 793a5ec08bSnicm 80*ced21769Snicm static int 813a5ec08bSnicm checkshell(const char *shell) 823a5ec08bSnicm { 83a89a0ceeSnicm if (shell == NULL || *shell == '\0' || *shell != '/') 84a89a0ceeSnicm return (0); 85a89a0ceeSnicm if (areshell(shell)) 863a5ec08bSnicm return (0); 873a5ec08bSnicm if (access(shell, X_OK) != 0) 883a5ec08bSnicm return (0); 893a5ec08bSnicm return (1); 903a5ec08bSnicm } 913a5ec08bSnicm 923a5ec08bSnicm int 933a5ec08bSnicm areshell(const char *shell) 943a5ec08bSnicm { 953a5ec08bSnicm const char *progname, *ptr; 963a5ec08bSnicm 973a5ec08bSnicm if ((ptr = strrchr(shell, '/')) != NULL) 983a5ec08bSnicm ptr++; 993a5ec08bSnicm else 1003a5ec08bSnicm ptr = shell; 101d3b99136Snicm progname = getprogname(); 1023a5ec08bSnicm if (*progname == '-') 1033a5ec08bSnicm progname++; 1043a5ec08bSnicm if (strcmp(ptr, progname) == 0) 1053a5ec08bSnicm return (1); 1063a5ec08bSnicm return (0); 1073a5ec08bSnicm } 1083a5ec08bSnicm 1099c764ad4Snicm static char * 1109c764ad4Snicm make_label(const char *label) 111311827fbSnicm { 1129c764ad4Snicm char *base, resolved[PATH_MAX], *path, *s; 113311827fbSnicm struct stat sb; 114311827fbSnicm u_int uid; 1159c764ad4Snicm int saved_errno; 1169c764ad4Snicm 1179c764ad4Snicm if (label == NULL) 1189c764ad4Snicm label = "default"; 119311827fbSnicm 120311827fbSnicm uid = getuid(); 1219c764ad4Snicm 12240b64c41Snicm if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 1239c764ad4Snicm xasprintf(&base, "%s/tmux-%u", s, uid); 12440b64c41Snicm else 1259c764ad4Snicm xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); 126311827fbSnicm 127311827fbSnicm if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 1289c764ad4Snicm goto fail; 129311827fbSnicm 130311827fbSnicm if (lstat(base, &sb) != 0) 1319c764ad4Snicm goto fail; 132311827fbSnicm if (!S_ISDIR(sb.st_mode)) { 133311827fbSnicm errno = ENOTDIR; 1349c764ad4Snicm goto fail; 135311827fbSnicm } 1369945ad17Snicm if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 137311827fbSnicm errno = EACCES; 1389c764ad4Snicm goto fail; 139311827fbSnicm } 140311827fbSnicm 1419c764ad4Snicm if (realpath(base, resolved) == NULL) 1429c764ad4Snicm strlcpy(resolved, base, sizeof resolved); 1439c764ad4Snicm xasprintf(&path, "%s/%s", resolved, label); 144311827fbSnicm return (path); 1459c764ad4Snicm 1469c764ad4Snicm fail: 1479c764ad4Snicm saved_errno = errno; 1489c764ad4Snicm free(base); 1499c764ad4Snicm errno = saved_errno; 1509c764ad4Snicm return (NULL); 151311827fbSnicm } 152311827fbSnicm 153a0ee4254Snicm void 154a0ee4254Snicm setblocking(int fd, int state) 155a0ee4254Snicm { 156a0ee4254Snicm int mode; 157a0ee4254Snicm 158a0ee4254Snicm if ((mode = fcntl(fd, F_GETFL)) != -1) { 159a0ee4254Snicm if (!state) 160a0ee4254Snicm mode |= O_NONBLOCK; 161a0ee4254Snicm else 162a0ee4254Snicm mode &= ~O_NONBLOCK; 163a0ee4254Snicm fcntl(fd, F_SETFL, mode); 164a0ee4254Snicm } 165a0ee4254Snicm } 166a0ee4254Snicm 167179ef399Snicm const char * 168179ef399Snicm find_home(void) 169179ef399Snicm { 170179ef399Snicm struct passwd *pw; 171f038d384Snicm static const char *home; 172f038d384Snicm 173f038d384Snicm if (home != NULL) 174f038d384Snicm return (home); 175179ef399Snicm 176179ef399Snicm home = getenv("HOME"); 177179ef399Snicm if (home == NULL || *home == '\0') { 178179ef399Snicm pw = getpwuid(getuid()); 179179ef399Snicm if (pw != NULL) 180179ef399Snicm home = pw->pw_dir; 181179ef399Snicm else 182179ef399Snicm home = NULL; 183179ef399Snicm } 184179ef399Snicm 18582bdfec0Snicm return (home); 186179ef399Snicm } 187179ef399Snicm 188311827fbSnicm int 189311827fbSnicm main(int argc, char **argv) 190311827fbSnicm { 191e192934cSnicm char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; 192f6fb1232Snicm const char *s; 1934fa31997Snicm int opt, flags, keys; 194311827fbSnicm 1953b05404cSnicm if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { 1963b05404cSnicm if (setlocale(LC_CTYPE, "") == NULL) 1973b05404cSnicm errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); 1983b05404cSnicm s = nl_langinfo(CODESET); 1995fcc80c9Snicm if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0) 2003b05404cSnicm errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); 2013b05404cSnicm } 202f5015ed6Snicm 20331a85510Snicm setlocale(LC_TIME, ""); 2045396add0Snicm tzset(); 205622ef518Snicm 2066b014674Snicm if (**argv == '-') 2076b014674Snicm flags = CLIENT_LOGIN; 2086b014674Snicm else 2097f6133c3Snicm flags = 0; 2106b014674Snicm 21165439d22Snicm label = path = NULL; 2120e3eb495Snicm while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 213311827fbSnicm switch (opt) { 214311827fbSnicm case '2': 215cafa7ad7Snicm flags |= CLIENT_256COLOURS; 216311827fbSnicm break; 217ef8ef121Snicm case 'c': 218e192934cSnicm free(shellcmd); 219e192934cSnicm shellcmd = xstrdup(optarg); 220ef8ef121Snicm break; 22190896992Snicm case 'C': 222cafa7ad7Snicm if (flags & CLIENT_CONTROL) 223cafa7ad7Snicm flags |= CLIENT_CONTROLCONTROL; 22490896992Snicm else 225cafa7ad7Snicm flags |= CLIENT_CONTROL; 22690896992Snicm break; 227311827fbSnicm case 'f': 228f038d384Snicm set_cfg_file(optarg); 229311827fbSnicm break; 23065b1f011Snicm case 'l': 2316b014674Snicm flags |= CLIENT_LOGIN; 23265b1f011Snicm break; 233311827fbSnicm case 'L': 2347d053cf9Snicm free(label); 235311827fbSnicm label = xstrdup(optarg); 236311827fbSnicm break; 237481abfa9Snicm case 'q': 238481abfa9Snicm break; 239311827fbSnicm case 'S': 2407d053cf9Snicm free(path); 241311827fbSnicm path = xstrdup(optarg); 242311827fbSnicm break; 243311827fbSnicm case 'u': 244cafa7ad7Snicm flags |= CLIENT_UTF8; 245311827fbSnicm break; 246311827fbSnicm case 'v': 2470a607e68Snicm log_add_level(); 248311827fbSnicm break; 249311827fbSnicm default: 250311827fbSnicm usage(); 251311827fbSnicm } 252311827fbSnicm } 253311827fbSnicm argc -= optind; 254311827fbSnicm argv += optind; 255311827fbSnicm 256e192934cSnicm if (shellcmd != NULL && argc != 0) 257ef8ef121Snicm usage(); 258ef8ef121Snicm 259cb19d99cSnicm if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " 260cb19d99cSnicm "recvfd proc exec tty ps", NULL) != 0) 2614e7a7adeSnicm err(1, "pledge"); 2624e7a7adeSnicm 263461e0674Snicm /* 264f6fb1232Snicm * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. 265f6fb1232Snicm * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain 266f6fb1232Snicm * UTF-8, it is a safe assumption that either they are using a UTF-8 267f6fb1232Snicm * terminal, or if not they know that output from UTF-8-capable 268f6fb1232Snicm * programs may be wrong. 269461e0674Snicm */ 270f6fb1232Snicm if (getenv("TMUX") != NULL) 271f6fb1232Snicm flags |= CLIENT_UTF8; 272f6fb1232Snicm else { 273f6fb1232Snicm s = getenv("LC_ALL"); 274f6fb1232Snicm if (s == NULL || *s == '\0') 275f6fb1232Snicm s = getenv("LC_CTYPE"); 276f6fb1232Snicm if (s == NULL || *s == '\0') 277461e0674Snicm s = getenv("LANG"); 278f6fb1232Snicm if (s == NULL || *s == '\0') 279f6fb1232Snicm s = ""; 280f6fb1232Snicm if (strcasestr(s, "UTF-8") != NULL || 281f6fb1232Snicm strcasestr(s, "UTF8") != NULL) 282cafa7ad7Snicm flags |= CLIENT_UTF8; 283461e0674Snicm } 284461e0674Snicm 2851443aefcSnicm global_hooks = hooks_create(NULL); 2861443aefcSnicm 287fb46cb3dSnicm global_environ = environ_create(); 288bd0e97c4Snicm for (var = environ; *var != NULL; var++) 289fb46cb3dSnicm environ_put(global_environ, *var); 290e1803d63Snicm if (getcwd(tmp, sizeof tmp) != NULL) 291c4ce9d7aSnicm environ_set(global_environ, "PWD", "%s", tmp); 292bd0e97c4Snicm 293d89252e5Snicm global_options = options_create(NULL); 2941a5f7318Snicm options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); 295f79ec119Snicm 296d89252e5Snicm global_s_options = options_create(NULL); 2971a5f7318Snicm options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); 298d89252e5Snicm options_set_string(global_s_options, "default-shell", "%s", getshell()); 299311827fbSnicm 300d89252e5Snicm global_w_options = options_create(NULL); 3011a5f7318Snicm options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); 3023affa6cbSnicm 3033affa6cbSnicm /* Override keys to vi if VISUAL or EDITOR are set. */ 304c9347692Snicm if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 305c9347692Snicm if (strrchr(s, '/') != NULL) 306c9347692Snicm s = strrchr(s, '/') + 1; 307c9347692Snicm if (strstr(s, "vi") != NULL) 308c9347692Snicm keys = MODEKEY_VI; 3093affa6cbSnicm else 3103affa6cbSnicm keys = MODEKEY_EMACS; 311d89252e5Snicm options_set_number(global_s_options, "status-keys", keys); 312d89252e5Snicm options_set_number(global_w_options, "mode-keys", keys); 313c9347692Snicm } 314c9347692Snicm 3152fa50cfcSnicm /* 3169c764ad4Snicm * If socket is specified on the command-line with -S or -L, it is 3179c764ad4Snicm * used. Otherwise, $TMUX is checked and if that fails "default" is 3189c764ad4Snicm * used. 3192fa50cfcSnicm */ 3209c764ad4Snicm if (path == NULL && label == NULL) { 3214fa31997Snicm s = getenv("TMUX"); 3229c764ad4Snicm if (s != NULL && *s != '\0' && *s != ',') { 3234fa31997Snicm path = xstrdup(s); 3244fa31997Snicm path[strcspn (path, ",")] = '\0'; 3254fa31997Snicm } 3262fa50cfcSnicm } 3279c764ad4Snicm if (path == NULL && (path = make_label(label)) == NULL) { 3289c764ad4Snicm fprintf(stderr, "can't create socket: %s\n", strerror(errno)); 329311827fbSnicm exit(1); 330311827fbSnicm } 3319c764ad4Snicm socket_path = path; 3327d053cf9Snicm free(label); 3336f631021Snicm 33465439d22Snicm /* Pass control to the client. */ 335e192934cSnicm exit(client_main(event_init(), argc, argv, flags, shellcmd)); 336fd234c13Snicm } 337