1*f038d384Snicm /* $OpenBSD: tmux.c,v 1.142 2015/09/01 10:10:59 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> 2526529d6fSnicm #include <getopt.h> 26622ef518Snicm #include <locale.h> 27311827fbSnicm #include <paths.h> 28311827fbSnicm #include <pwd.h> 29311827fbSnicm #include <stdlib.h> 30311827fbSnicm #include <string.h> 31311827fbSnicm #include <unistd.h> 32311827fbSnicm 33311827fbSnicm #include "tmux.h" 34311827fbSnicm 35311827fbSnicm #ifdef DEBUG 36bbc36c02Snicm extern char *malloc_options; 37311827fbSnicm #endif 38311827fbSnicm 39f79ec119Snicm struct options global_options; /* server options */ 40eaecedb2Snicm struct options global_s_options; /* session options */ 41eaecedb2Snicm struct options global_w_options; /* window options */ 426f7d62ebSnicm struct environ global_environ; 43311827fbSnicm 4465439d22Snicm char *shell_cmd; 45311827fbSnicm int debug_level; 46311827fbSnicm time_t start_time; 4726529d6fSnicm char socket_path[PATH_MAX]; 484ccba3b3Snicm 49311827fbSnicm __dead void usage(void); 5065439d22Snicm char *makesocketpath(const char *); 517724d0b0Snicm 52311827fbSnicm __dead void 53311827fbSnicm usage(void) 54311827fbSnicm { 556ca2ce32Ssobrado fprintf(stderr, 563da585bcSjmc "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 5765b1f011Snicm " [-S socket-path] [command [flags]]\n", 58311827fbSnicm __progname); 59311827fbSnicm exit(1); 60311827fbSnicm } 61311827fbSnicm 62311827fbSnicm void 63311827fbSnicm logfile(const char *name) 64311827fbSnicm { 65311827fbSnicm char *path; 66311827fbSnicm 67311827fbSnicm if (debug_level > 0) { 683a4fea3bSnicm xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 6946b1c757Snicm log_open(path); 707d053cf9Snicm free(path); 71311827fbSnicm } 72311827fbSnicm } 73311827fbSnicm 743a5ec08bSnicm const char * 753a5ec08bSnicm getshell(void) 763a5ec08bSnicm { 773a5ec08bSnicm struct passwd *pw; 783a5ec08bSnicm const char *shell; 793a5ec08bSnicm 803a5ec08bSnicm shell = getenv("SHELL"); 813a5ec08bSnicm if (checkshell(shell)) 823a5ec08bSnicm return (shell); 833a5ec08bSnicm 843a5ec08bSnicm pw = getpwuid(getuid()); 853a5ec08bSnicm if (pw != NULL && checkshell(pw->pw_shell)) 863a5ec08bSnicm return (pw->pw_shell); 873a5ec08bSnicm 883a5ec08bSnicm return (_PATH_BSHELL); 893a5ec08bSnicm } 903a5ec08bSnicm 913a5ec08bSnicm int 923a5ec08bSnicm checkshell(const char *shell) 933a5ec08bSnicm { 94a89a0ceeSnicm if (shell == NULL || *shell == '\0' || *shell != '/') 95a89a0ceeSnicm return (0); 96a89a0ceeSnicm if (areshell(shell)) 973a5ec08bSnicm return (0); 983a5ec08bSnicm if (access(shell, X_OK) != 0) 993a5ec08bSnicm return (0); 1003a5ec08bSnicm return (1); 1013a5ec08bSnicm } 1023a5ec08bSnicm 1033a5ec08bSnicm int 1043a5ec08bSnicm areshell(const char *shell) 1053a5ec08bSnicm { 1063a5ec08bSnicm const char *progname, *ptr; 1073a5ec08bSnicm 1083a5ec08bSnicm if ((ptr = strrchr(shell, '/')) != NULL) 1093a5ec08bSnicm ptr++; 1103a5ec08bSnicm else 1113a5ec08bSnicm ptr = shell; 1123a5ec08bSnicm progname = __progname; 1133a5ec08bSnicm if (*progname == '-') 1143a5ec08bSnicm progname++; 1153a5ec08bSnicm if (strcmp(ptr, progname) == 0) 1163a5ec08bSnicm return (1); 1173a5ec08bSnicm return (0); 1183a5ec08bSnicm } 1193a5ec08bSnicm 120311827fbSnicm char * 12165439d22Snicm makesocketpath(const char *label) 122311827fbSnicm { 12326529d6fSnicm char base[PATH_MAX], realbase[PATH_MAX], *path, *s; 124311827fbSnicm struct stat sb; 125311827fbSnicm u_int uid; 126311827fbSnicm 127311827fbSnicm uid = getuid(); 12840b64c41Snicm if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 129cb12d36bSnicm xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 13040b64c41Snicm else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') 131e3b883c9Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 13240b64c41Snicm else 13340b64c41Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 134311827fbSnicm 135311827fbSnicm if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 136311827fbSnicm return (NULL); 137311827fbSnicm 138311827fbSnicm if (lstat(base, &sb) != 0) 139311827fbSnicm return (NULL); 140311827fbSnicm if (!S_ISDIR(sb.st_mode)) { 141311827fbSnicm errno = ENOTDIR; 142311827fbSnicm return (NULL); 143311827fbSnicm } 1449945ad17Snicm if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 145311827fbSnicm errno = EACCES; 146311827fbSnicm return (NULL); 147311827fbSnicm } 148311827fbSnicm 14935194355Snicm if (realpath(base, realbase) == NULL) 15035194355Snicm strlcpy(realbase, base, sizeof realbase); 15135194355Snicm 15235194355Snicm xasprintf(&path, "%s/%s", realbase, label); 153311827fbSnicm return (path); 154311827fbSnicm } 155311827fbSnicm 156a0ee4254Snicm void 157a0ee4254Snicm setblocking(int fd, int state) 158a0ee4254Snicm { 159a0ee4254Snicm int mode; 160a0ee4254Snicm 161a0ee4254Snicm if ((mode = fcntl(fd, F_GETFL)) != -1) { 162a0ee4254Snicm if (!state) 163a0ee4254Snicm mode |= O_NONBLOCK; 164a0ee4254Snicm else 165a0ee4254Snicm mode &= ~O_NONBLOCK; 166a0ee4254Snicm fcntl(fd, F_SETFL, mode); 167a0ee4254Snicm } 168a0ee4254Snicm } 169a0ee4254Snicm 170179ef399Snicm const char * 171179ef399Snicm find_home(void) 172179ef399Snicm { 173179ef399Snicm struct passwd *pw; 174*f038d384Snicm static const char *home; 175*f038d384Snicm 176*f038d384Snicm if (home != NULL) 177*f038d384Snicm return (home); 178179ef399Snicm 179179ef399Snicm home = getenv("HOME"); 180179ef399Snicm if (home == NULL || *home == '\0') { 181179ef399Snicm pw = getpwuid(getuid()); 182179ef399Snicm if (pw != NULL) 183179ef399Snicm home = pw->pw_dir; 184179ef399Snicm else 185179ef399Snicm home = NULL; 186179ef399Snicm } 187179ef399Snicm 18882bdfec0Snicm return (home); 189179ef399Snicm } 190179ef399Snicm 191311827fbSnicm int 192311827fbSnicm main(int argc, char **argv) 193311827fbSnicm { 19426529d6fSnicm char *s, *path, *label, **var, tmp[PATH_MAX]; 1954fa31997Snicm int opt, flags, keys; 196311827fbSnicm 197bbc36c02Snicm #ifdef DEBUG 198bbc36c02Snicm malloc_options = (char *) "AFGJPX"; 199bbc36c02Snicm #endif 200bbc36c02Snicm 201622ef518Snicm setlocale(LC_TIME, ""); 202622ef518Snicm 2036b014674Snicm if (**argv == '-') 2046b014674Snicm flags = CLIENT_LOGIN; 2056b014674Snicm else 2067f6133c3Snicm flags = 0; 2076b014674Snicm 20865439d22Snicm label = path = NULL; 2090e3eb495Snicm while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 210311827fbSnicm switch (opt) { 211311827fbSnicm case '2': 212cafa7ad7Snicm flags |= CLIENT_256COLOURS; 213311827fbSnicm break; 214ef8ef121Snicm case 'c': 2157d053cf9Snicm free(shell_cmd); 21665439d22Snicm shell_cmd = xstrdup(optarg); 217ef8ef121Snicm break; 21890896992Snicm case 'C': 219cafa7ad7Snicm if (flags & CLIENT_CONTROL) 220cafa7ad7Snicm flags |= CLIENT_CONTROLCONTROL; 22190896992Snicm else 222cafa7ad7Snicm flags |= CLIENT_CONTROL; 22390896992Snicm break; 224311827fbSnicm case 'f': 225*f038d384Snicm set_cfg_file(optarg); 226311827fbSnicm break; 22765b1f011Snicm case 'l': 2286b014674Snicm flags |= CLIENT_LOGIN; 22965b1f011Snicm break; 230311827fbSnicm case 'L': 2317d053cf9Snicm free(label); 232311827fbSnicm label = xstrdup(optarg); 233311827fbSnicm break; 234481abfa9Snicm case 'q': 235481abfa9Snicm break; 236311827fbSnicm case 'S': 2377d053cf9Snicm free(path); 238311827fbSnicm path = xstrdup(optarg); 239311827fbSnicm break; 240311827fbSnicm case 'u': 241cafa7ad7Snicm flags |= CLIENT_UTF8; 242311827fbSnicm break; 243311827fbSnicm case 'v': 244311827fbSnicm debug_level++; 245311827fbSnicm break; 246311827fbSnicm default: 247311827fbSnicm usage(); 248311827fbSnicm } 249311827fbSnicm } 250311827fbSnicm argc -= optind; 251311827fbSnicm argv += optind; 252311827fbSnicm 25365439d22Snicm if (shell_cmd != NULL && argc != 0) 254ef8ef121Snicm usage(); 255ef8ef121Snicm 256cafa7ad7Snicm if (!(flags & CLIENT_UTF8)) { 257461e0674Snicm /* 258461e0674Snicm * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 259461e0674Snicm * exist (in that order) to contain UTF-8, it is a safe 260461e0674Snicm * assumption that either they are using a UTF-8 terminal, or 261461e0674Snicm * if not they know that output from UTF-8-capable programs may 262461e0674Snicm * be wrong. 263461e0674Snicm */ 26454810b57Snicm if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { 26554810b57Snicm if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') 266461e0674Snicm s = getenv("LANG"); 267461e0674Snicm } 268a7a86d62Snicm if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 269a7a86d62Snicm strcasestr(s, "UTF8") != NULL)) 270cafa7ad7Snicm flags |= CLIENT_UTF8; 271461e0674Snicm } 272461e0674Snicm 273bd0e97c4Snicm environ_init(&global_environ); 274bd0e97c4Snicm for (var = environ; *var != NULL; var++) 275bd0e97c4Snicm environ_put(&global_environ, *var); 276e1803d63Snicm if (getcwd(tmp, sizeof tmp) != NULL) 277e1803d63Snicm environ_set(&global_environ, "PWD", tmp); 278bd0e97c4Snicm 279f79ec119Snicm options_init(&global_options, NULL); 2803affa6cbSnicm options_table_populate_tree(server_options_table, &global_options); 281f79ec119Snicm 282eaecedb2Snicm options_init(&global_s_options, NULL); 2833affa6cbSnicm options_table_populate_tree(session_options_table, &global_s_options); 2847f6133c3Snicm options_set_string(&global_s_options, "default-shell", "%s", 2857f6133c3Snicm getshell()); 286311827fbSnicm 2873affa6cbSnicm options_init(&global_w_options, NULL); 2883affa6cbSnicm options_table_populate_tree(window_options_table, &global_w_options); 2893affa6cbSnicm 2903affa6cbSnicm /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 291cafa7ad7Snicm if (flags & CLIENT_UTF8) { 2923affa6cbSnicm options_set_number(&global_s_options, "status-utf8", 1); 293699995ddSnicm options_set_number(&global_s_options, "mouse-utf8", 1); 2943affa6cbSnicm options_set_number(&global_w_options, "utf8", 1); 295bd0e97c4Snicm } 296bd0e97c4Snicm 2973affa6cbSnicm /* Override keys to vi if VISUAL or EDITOR are set. */ 298c9347692Snicm if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 299c9347692Snicm if (strrchr(s, '/') != NULL) 300c9347692Snicm s = strrchr(s, '/') + 1; 301c9347692Snicm if (strstr(s, "vi") != NULL) 302c9347692Snicm keys = MODEKEY_VI; 3033affa6cbSnicm else 3043affa6cbSnicm keys = MODEKEY_EMACS; 3053affa6cbSnicm options_set_number(&global_s_options, "status-keys", keys); 3063affa6cbSnicm options_set_number(&global_w_options, "mode-keys", keys); 307c9347692Snicm } 308c9347692Snicm 3092fa50cfcSnicm /* 31065439d22Snicm * Figure out the socket path. If specified on the command-line with -S 31165439d22Snicm * or -L, use it, otherwise try $TMUX or assume -L default. 3122fa50cfcSnicm */ 3132fa50cfcSnicm if (path == NULL) { 31465439d22Snicm /* If no -L, use the environment. */ 3152fa50cfcSnicm if (label == NULL) { 3164fa31997Snicm s = getenv("TMUX"); 3174fa31997Snicm if (s != NULL) { 3184fa31997Snicm path = xstrdup(s); 3194fa31997Snicm path[strcspn (path, ",")] = '\0'; 3204fa31997Snicm if (*path == '\0') { 3214fa31997Snicm free(path); 3224fa31997Snicm label = xstrdup("default"); 3234fa31997Snicm } 3244fa31997Snicm } 32565439d22Snicm else 326311827fbSnicm label = xstrdup("default"); 3272fa50cfcSnicm } 3282fa50cfcSnicm 3292fa50cfcSnicm /* -L or default set. */ 3302fa50cfcSnicm if (label != NULL) { 33165439d22Snicm if ((path = makesocketpath(label)) == NULL) { 3322b80f9f7Snicm fprintf(stderr, "can't create socket: %s\n", 3332b80f9f7Snicm strerror(errno)); 334311827fbSnicm exit(1); 335311827fbSnicm } 3362fa50cfcSnicm } 3372fa50cfcSnicm } 3387d053cf9Snicm free(label); 3396f631021Snicm 3404fa31997Snicm if (strlcpy(socket_path, path, sizeof socket_path) >= 3414fa31997Snicm sizeof socket_path) { 3426f631021Snicm fprintf(stderr, "socket path too long: %s\n", path); 3436f631021Snicm exit(1); 3446f631021Snicm } 3457d053cf9Snicm free(path); 346311827fbSnicm 34765439d22Snicm /* Set process title. */ 34865439d22Snicm setproctitle("%s (%s)", __progname, socket_path); 349311827fbSnicm 35065439d22Snicm /* Pass control to the client. */ 3517b560ed5Snicm exit(client_main(event_init(), argc, argv, flags)); 352fd234c13Snicm } 353