1*6f631021Snicm /* $OpenBSD: tmux.c,v 1.127 2014/01/09 14:05:55 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 4311827fbSnicm * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 22311827fbSnicm #include <errno.h> 237724d0b0Snicm #include <event.h> 243808d149Snicm #include <fcntl.h> 25622ef518Snicm #include <locale.h> 26311827fbSnicm #include <paths.h> 27311827fbSnicm #include <pwd.h> 28311827fbSnicm #include <stdlib.h> 29311827fbSnicm #include <string.h> 30311827fbSnicm #include <unistd.h> 31311827fbSnicm 32311827fbSnicm #include "tmux.h" 33311827fbSnicm 34311827fbSnicm #ifdef DEBUG 35bbc36c02Snicm extern char *malloc_options; 36311827fbSnicm #endif 37311827fbSnicm 38f79ec119Snicm struct options global_options; /* server options */ 39eaecedb2Snicm struct options global_s_options; /* session options */ 40eaecedb2Snicm struct options global_w_options; /* window options */ 416f7d62ebSnicm struct environ global_environ; 42311827fbSnicm 434291359cSnicm struct event_base *ev_base; 444291359cSnicm 4565439d22Snicm char *cfg_file; 4665439d22Snicm char *shell_cmd; 47311827fbSnicm int debug_level; 48311827fbSnicm time_t start_time; 4965439d22Snicm char socket_path[MAXPATHLEN]; 5065b1f011Snicm int login_shell; 5165439d22Snicm char *environ_path; 524ccba3b3Snicm 53311827fbSnicm __dead void usage(void); 5465439d22Snicm char *makesocketpath(const char *); 557724d0b0Snicm 56311827fbSnicm __dead void 57311827fbSnicm usage(void) 58311827fbSnicm { 596ca2ce32Ssobrado fprintf(stderr, 6004924df5Snicm "usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n" 6165b1f011Snicm " [-S socket-path] [command [flags]]\n", 62311827fbSnicm __progname); 63311827fbSnicm exit(1); 64311827fbSnicm } 65311827fbSnicm 66311827fbSnicm void 67311827fbSnicm logfile(const char *name) 68311827fbSnicm { 69311827fbSnicm char *path; 70311827fbSnicm 71311827fbSnicm if (debug_level > 0) { 723a4fea3bSnicm xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 7336fa3172Snicm log_open(debug_level, path); 747d053cf9Snicm free(path); 75311827fbSnicm } 76311827fbSnicm } 77311827fbSnicm 783a5ec08bSnicm const char * 793a5ec08bSnicm getshell(void) 803a5ec08bSnicm { 813a5ec08bSnicm struct passwd *pw; 823a5ec08bSnicm const char *shell; 833a5ec08bSnicm 843a5ec08bSnicm shell = getenv("SHELL"); 853a5ec08bSnicm if (checkshell(shell)) 863a5ec08bSnicm return (shell); 873a5ec08bSnicm 883a5ec08bSnicm pw = getpwuid(getuid()); 893a5ec08bSnicm if (pw != NULL && checkshell(pw->pw_shell)) 903a5ec08bSnicm return (pw->pw_shell); 913a5ec08bSnicm 923a5ec08bSnicm return (_PATH_BSHELL); 933a5ec08bSnicm } 943a5ec08bSnicm 953a5ec08bSnicm int 963a5ec08bSnicm checkshell(const char *shell) 973a5ec08bSnicm { 98a89a0ceeSnicm if (shell == NULL || *shell == '\0' || *shell != '/') 99a89a0ceeSnicm return (0); 100a89a0ceeSnicm if (areshell(shell)) 1013a5ec08bSnicm return (0); 1023a5ec08bSnicm if (access(shell, X_OK) != 0) 1033a5ec08bSnicm return (0); 1043a5ec08bSnicm return (1); 1053a5ec08bSnicm } 1063a5ec08bSnicm 1073a5ec08bSnicm int 1083a5ec08bSnicm areshell(const char *shell) 1093a5ec08bSnicm { 1103a5ec08bSnicm const char *progname, *ptr; 1113a5ec08bSnicm 1123a5ec08bSnicm if ((ptr = strrchr(shell, '/')) != NULL) 1133a5ec08bSnicm ptr++; 1143a5ec08bSnicm else 1153a5ec08bSnicm ptr = shell; 1163a5ec08bSnicm progname = __progname; 1173a5ec08bSnicm if (*progname == '-') 1183a5ec08bSnicm progname++; 1193a5ec08bSnicm if (strcmp(ptr, progname) == 0) 1203a5ec08bSnicm return (1); 1213a5ec08bSnicm return (0); 1223a5ec08bSnicm } 1233a5ec08bSnicm 124311827fbSnicm char * 12565439d22Snicm makesocketpath(const char *label) 126311827fbSnicm { 12735194355Snicm char base[MAXPATHLEN], realbase[MAXPATHLEN], *path, *s; 128311827fbSnicm struct stat sb; 129311827fbSnicm u_int uid; 130311827fbSnicm 131311827fbSnicm uid = getuid(); 13240b64c41Snicm if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 13340b64c41Snicm xsnprintf(base, sizeof base, "%s/", s); 13440b64c41Snicm else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') 135e3b883c9Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 13640b64c41Snicm else 13740b64c41Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 138311827fbSnicm 139311827fbSnicm if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 140311827fbSnicm return (NULL); 141311827fbSnicm 142311827fbSnicm if (lstat(base, &sb) != 0) 143311827fbSnicm return (NULL); 144311827fbSnicm if (!S_ISDIR(sb.st_mode)) { 145311827fbSnicm errno = ENOTDIR; 146311827fbSnicm return (NULL); 147311827fbSnicm } 1482b80f9f7Snicm if (sb.st_uid != uid || (!S_ISDIR(sb.st_mode) && 1492b80f9f7Snicm sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { 150311827fbSnicm errno = EACCES; 151311827fbSnicm return (NULL); 152311827fbSnicm } 153311827fbSnicm 15435194355Snicm if (realpath(base, realbase) == NULL) 15535194355Snicm strlcpy(realbase, base, sizeof realbase); 15635194355Snicm 15735194355Snicm xasprintf(&path, "%s/%s", realbase, label); 158311827fbSnicm return (path); 159311827fbSnicm } 160311827fbSnicm 161a0ee4254Snicm void 162a0ee4254Snicm setblocking(int fd, int state) 163a0ee4254Snicm { 164a0ee4254Snicm int mode; 165a0ee4254Snicm 166a0ee4254Snicm if ((mode = fcntl(fd, F_GETFL)) != -1) { 167a0ee4254Snicm if (!state) 168a0ee4254Snicm mode |= O_NONBLOCK; 169a0ee4254Snicm else 170a0ee4254Snicm mode &= ~O_NONBLOCK; 171a0ee4254Snicm fcntl(fd, F_SETFL, mode); 172a0ee4254Snicm } 173a0ee4254Snicm } 174a0ee4254Snicm 1757724d0b0Snicm __dead void 1767724d0b0Snicm shell_exec(const char *shell, const char *shellcmd) 1777724d0b0Snicm { 1787724d0b0Snicm const char *shellname, *ptr; 1797724d0b0Snicm char *argv0; 1807724d0b0Snicm 1817724d0b0Snicm ptr = strrchr(shell, '/'); 1827724d0b0Snicm if (ptr != NULL && *(ptr + 1) != '\0') 1837724d0b0Snicm shellname = ptr + 1; 1847724d0b0Snicm else 1857724d0b0Snicm shellname = shell; 1867724d0b0Snicm if (login_shell) 1877724d0b0Snicm xasprintf(&argv0, "-%s", shellname); 1887724d0b0Snicm else 1897724d0b0Snicm xasprintf(&argv0, "%s", shellname); 1907724d0b0Snicm setenv("SHELL", shell, 1); 1917724d0b0Snicm 192a0ee4254Snicm setblocking(STDIN_FILENO, 1); 193a0ee4254Snicm setblocking(STDOUT_FILENO, 1); 194a0ee4254Snicm setblocking(STDERR_FILENO, 1); 195498ee386Snicm closefrom(STDERR_FILENO + 1); 196498ee386Snicm 1977724d0b0Snicm execl(shell, argv0, "-c", shellcmd, (char *) NULL); 1987724d0b0Snicm fatal("execl failed"); 1997724d0b0Snicm } 2007724d0b0Snicm 201311827fbSnicm int 202311827fbSnicm main(int argc, char **argv) 203311827fbSnicm { 204311827fbSnicm struct passwd *pw; 205e1803d63Snicm char *s, *path, *label, *home, **var, tmp[MAXPATHLEN]; 206d87b01fbSnicm char in[256]; 207d87b01fbSnicm long long pid; 208d87b01fbSnicm int opt, flags, quiet, keys, session; 209311827fbSnicm 210bbc36c02Snicm #ifdef DEBUG 211bbc36c02Snicm malloc_options = (char *) "AFGJPX"; 212bbc36c02Snicm #endif 213bbc36c02Snicm 214622ef518Snicm setlocale(LC_TIME, ""); 215622ef518Snicm 216c9347692Snicm quiet = flags = 0; 21765439d22Snicm label = path = NULL; 21865b1f011Snicm login_shell = (**argv == '-'); 2190e3eb495Snicm while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 220311827fbSnicm switch (opt) { 221311827fbSnicm case '2': 222cafa7ad7Snicm flags |= CLIENT_256COLOURS; 223311827fbSnicm break; 224ef8ef121Snicm case 'c': 2257d053cf9Snicm free(shell_cmd); 22665439d22Snicm shell_cmd = xstrdup(optarg); 227ef8ef121Snicm break; 22890896992Snicm case 'C': 229cafa7ad7Snicm if (flags & CLIENT_CONTROL) 230cafa7ad7Snicm flags |= CLIENT_CONTROLCONTROL; 23190896992Snicm else 232cafa7ad7Snicm flags |= CLIENT_CONTROL; 23390896992Snicm break; 234311827fbSnicm case 'f': 2357d053cf9Snicm free(cfg_file); 236311827fbSnicm cfg_file = xstrdup(optarg); 237311827fbSnicm break; 23865b1f011Snicm case 'l': 23965b1f011Snicm login_shell = 1; 24065b1f011Snicm break; 241311827fbSnicm case 'L': 2427d053cf9Snicm free(label); 243311827fbSnicm label = xstrdup(optarg); 244311827fbSnicm break; 245481abfa9Snicm case 'q': 246f79ec119Snicm quiet = 1; 247481abfa9Snicm break; 248311827fbSnicm case 'S': 2497d053cf9Snicm free(path); 250311827fbSnicm path = xstrdup(optarg); 251311827fbSnicm break; 252311827fbSnicm case 'u': 253cafa7ad7Snicm flags |= CLIENT_UTF8; 254311827fbSnicm break; 255311827fbSnicm case 'v': 256311827fbSnicm debug_level++; 257311827fbSnicm break; 258311827fbSnicm default: 259311827fbSnicm usage(); 260311827fbSnicm } 261311827fbSnicm } 262311827fbSnicm argc -= optind; 263311827fbSnicm argv += optind; 264311827fbSnicm 26565439d22Snicm if (shell_cmd != NULL && argc != 0) 266ef8ef121Snicm usage(); 267ef8ef121Snicm 268cafa7ad7Snicm if (!(flags & CLIENT_UTF8)) { 269461e0674Snicm /* 270461e0674Snicm * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 271461e0674Snicm * exist (in that order) to contain UTF-8, it is a safe 272461e0674Snicm * assumption that either they are using a UTF-8 terminal, or 273461e0674Snicm * if not they know that output from UTF-8-capable programs may 274461e0674Snicm * be wrong. 275461e0674Snicm */ 27654810b57Snicm if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { 27754810b57Snicm if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') 278461e0674Snicm s = getenv("LANG"); 279461e0674Snicm } 280a7a86d62Snicm if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 281a7a86d62Snicm strcasestr(s, "UTF8") != NULL)) 282cafa7ad7Snicm flags |= CLIENT_UTF8; 283461e0674Snicm } 284461e0674Snicm 285bd0e97c4Snicm environ_init(&global_environ); 286bd0e97c4Snicm for (var = environ; *var != NULL; var++) 287bd0e97c4Snicm environ_put(&global_environ, *var); 288e1803d63Snicm if (getcwd(tmp, sizeof tmp) != NULL) 289e1803d63Snicm environ_set(&global_environ, "PWD", tmp); 290bd0e97c4Snicm 291f79ec119Snicm options_init(&global_options, NULL); 2923affa6cbSnicm options_table_populate_tree(server_options_table, &global_options); 2933affa6cbSnicm options_set_number(&global_options, "quiet", quiet); 294f79ec119Snicm 295eaecedb2Snicm options_init(&global_s_options, NULL); 2963affa6cbSnicm options_table_populate_tree(session_options_table, &global_s_options); 2973affa6cbSnicm options_set_string(&global_s_options, "default-shell", "%s", getshell()); 298311827fbSnicm 2993affa6cbSnicm options_init(&global_w_options, NULL); 3003affa6cbSnicm options_table_populate_tree(window_options_table, &global_w_options); 3013affa6cbSnicm 3023affa6cbSnicm /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 303cafa7ad7Snicm if (flags & CLIENT_UTF8) { 3043affa6cbSnicm options_set_number(&global_s_options, "status-utf8", 1); 305699995ddSnicm options_set_number(&global_s_options, "mouse-utf8", 1); 3063affa6cbSnicm options_set_number(&global_w_options, "utf8", 1); 307bd0e97c4Snicm } 308bd0e97c4Snicm 3093affa6cbSnicm /* Override keys to vi if VISUAL or EDITOR are set. */ 310c9347692Snicm if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 311c9347692Snicm if (strrchr(s, '/') != NULL) 312c9347692Snicm s = strrchr(s, '/') + 1; 313c9347692Snicm if (strstr(s, "vi") != NULL) 314c9347692Snicm keys = MODEKEY_VI; 3153affa6cbSnicm else 3163affa6cbSnicm keys = MODEKEY_EMACS; 3173affa6cbSnicm options_set_number(&global_s_options, "status-keys", keys); 3183affa6cbSnicm options_set_number(&global_w_options, "mode-keys", keys); 319c9347692Snicm } 320c9347692Snicm 32165439d22Snicm /* Locate the configuration file. */ 322311827fbSnicm if (cfg_file == NULL) { 323311827fbSnicm home = getenv("HOME"); 324311827fbSnicm if (home == NULL || *home == '\0') { 325311827fbSnicm pw = getpwuid(getuid()); 326311827fbSnicm if (pw != NULL) 327311827fbSnicm home = pw->pw_dir; 328311827fbSnicm } 329ebf82b56Snicm xasprintf(&cfg_file, "%s/.tmux.conf", home); 3309a53e128Snicm if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { 3317d053cf9Snicm free(cfg_file); 332311827fbSnicm cfg_file = NULL; 333311827fbSnicm } 334311827fbSnicm } 335311827fbSnicm 336d87b01fbSnicm /* Get path from environment. */ 337d87b01fbSnicm s = getenv("TMUX"); 338d87b01fbSnicm if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3) 339d87b01fbSnicm environ_path = xstrdup(in); 340d87b01fbSnicm 3412fa50cfcSnicm /* 34265439d22Snicm * Figure out the socket path. If specified on the command-line with -S 34365439d22Snicm * or -L, use it, otherwise try $TMUX or assume -L default. 3442fa50cfcSnicm */ 3452fa50cfcSnicm if (path == NULL) { 34665439d22Snicm /* If no -L, use the environment. */ 3472fa50cfcSnicm if (label == NULL) { 34865439d22Snicm if (environ_path != NULL) 34965439d22Snicm path = xstrdup(environ_path); 35065439d22Snicm else 351311827fbSnicm label = xstrdup("default"); 3522fa50cfcSnicm } 3532fa50cfcSnicm 3542fa50cfcSnicm /* -L or default set. */ 3552fa50cfcSnicm if (label != NULL) { 35665439d22Snicm if ((path = makesocketpath(label)) == NULL) { 3572b80f9f7Snicm fprintf(stderr, "can't create socket: %s\n", 3582b80f9f7Snicm strerror(errno)); 359311827fbSnicm exit(1); 360311827fbSnicm } 3612fa50cfcSnicm } 3622fa50cfcSnicm } 3637d053cf9Snicm free(label); 364*6f631021Snicm 365*6f631021Snicm if (strlcpy(socket_path, path, sizeof socket_path) >= sizeof socket_path) { 366*6f631021Snicm fprintf(stderr, "socket path too long: %s\n", path); 367*6f631021Snicm exit(1); 368*6f631021Snicm } 3697d053cf9Snicm free(path); 370311827fbSnicm 37165439d22Snicm /* Set process title. */ 37265439d22Snicm setproctitle("%s (%s)", __progname, socket_path); 373311827fbSnicm 37465439d22Snicm /* Pass control to the client. */ 37565439d22Snicm ev_base = event_init(); 37665439d22Snicm exit(client_main(argc, argv, flags)); 377fd234c13Snicm } 378