1*7d053cf9Snicm /* $OpenBSD: tmux.c,v 1.112 2012/07/10 11:53:01 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> 25311827fbSnicm #include <paths.h> 26311827fbSnicm #include <pwd.h> 27311827fbSnicm #include <stdlib.h> 28311827fbSnicm #include <string.h> 29311827fbSnicm #include <unistd.h> 30311827fbSnicm 31311827fbSnicm #include "tmux.h" 32311827fbSnicm 33311827fbSnicm #ifdef DEBUG 34bbc36c02Snicm extern char *malloc_options; 35311827fbSnicm #endif 36311827fbSnicm 37f79ec119Snicm struct options global_options; /* server options */ 38eaecedb2Snicm struct options global_s_options; /* session options */ 39eaecedb2Snicm struct options global_w_options; /* window options */ 406f7d62ebSnicm struct environ global_environ; 41311827fbSnicm 424291359cSnicm struct event_base *ev_base; 434291359cSnicm 4465439d22Snicm char *cfg_file; 4565439d22Snicm char *shell_cmd; 46311827fbSnicm int debug_level; 47311827fbSnicm time_t start_time; 4865439d22Snicm char socket_path[MAXPATHLEN]; 4965b1f011Snicm int login_shell; 5065439d22Snicm char *environ_path; 5107bd7befSnicm pid_t environ_pid = -1; 5207bd7befSnicm int environ_idx = -1; 534ccba3b3Snicm 54311827fbSnicm __dead void usage(void); 5565439d22Snicm void parseenvironment(void); 5665439d22Snicm char *makesocketpath(const char *); 577724d0b0Snicm 58311827fbSnicm __dead void 59311827fbSnicm usage(void) 60311827fbSnicm { 616ca2ce32Ssobrado fprintf(stderr, 6204924df5Snicm "usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n" 6365b1f011Snicm " [-S socket-path] [command [flags]]\n", 64311827fbSnicm __progname); 65311827fbSnicm exit(1); 66311827fbSnicm } 67311827fbSnicm 68311827fbSnicm void 69311827fbSnicm logfile(const char *name) 70311827fbSnicm { 71311827fbSnicm char *path; 72311827fbSnicm 73311827fbSnicm if (debug_level > 0) { 743a4fea3bSnicm xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 7536fa3172Snicm log_open(debug_level, path); 76*7d053cf9Snicm free(path); 77311827fbSnicm } 78311827fbSnicm } 79311827fbSnicm 803a5ec08bSnicm const char * 813a5ec08bSnicm getshell(void) 823a5ec08bSnicm { 833a5ec08bSnicm struct passwd *pw; 843a5ec08bSnicm const char *shell; 853a5ec08bSnicm 863a5ec08bSnicm shell = getenv("SHELL"); 873a5ec08bSnicm if (checkshell(shell)) 883a5ec08bSnicm return (shell); 893a5ec08bSnicm 903a5ec08bSnicm pw = getpwuid(getuid()); 913a5ec08bSnicm if (pw != NULL && checkshell(pw->pw_shell)) 923a5ec08bSnicm return (pw->pw_shell); 933a5ec08bSnicm 943a5ec08bSnicm return (_PATH_BSHELL); 953a5ec08bSnicm } 963a5ec08bSnicm 973a5ec08bSnicm int 983a5ec08bSnicm checkshell(const char *shell) 993a5ec08bSnicm { 100a89a0ceeSnicm if (shell == NULL || *shell == '\0' || *shell != '/') 101a89a0ceeSnicm return (0); 102a89a0ceeSnicm if (areshell(shell)) 1033a5ec08bSnicm return (0); 1043a5ec08bSnicm if (access(shell, X_OK) != 0) 1053a5ec08bSnicm return (0); 1063a5ec08bSnicm return (1); 1073a5ec08bSnicm } 1083a5ec08bSnicm 1093a5ec08bSnicm int 1103a5ec08bSnicm areshell(const char *shell) 1113a5ec08bSnicm { 1123a5ec08bSnicm const char *progname, *ptr; 1133a5ec08bSnicm 1143a5ec08bSnicm if ((ptr = strrchr(shell, '/')) != NULL) 1153a5ec08bSnicm ptr++; 1163a5ec08bSnicm else 1173a5ec08bSnicm ptr = shell; 1183a5ec08bSnicm progname = __progname; 1193a5ec08bSnicm if (*progname == '-') 1203a5ec08bSnicm progname++; 1213a5ec08bSnicm if (strcmp(ptr, progname) == 0) 1223a5ec08bSnicm return (1); 1233a5ec08bSnicm return (0); 1243a5ec08bSnicm } 1253a5ec08bSnicm 12635a092d6Snicm const char* 12735a092d6Snicm get_full_path(const char *wd, const char *path) 12835a092d6Snicm { 12935a092d6Snicm static char newpath[MAXPATHLEN]; 13035a092d6Snicm char oldpath[MAXPATHLEN]; 13135a092d6Snicm 13235a092d6Snicm if (getcwd(oldpath, sizeof oldpath) == NULL) 13335a092d6Snicm return (NULL); 13435a092d6Snicm if (chdir(wd) != 0) 13535a092d6Snicm return (NULL); 13635a092d6Snicm if (realpath(path, newpath) != 0) 13735a092d6Snicm return (NULL); 13835a092d6Snicm chdir(oldpath); 13935a092d6Snicm return (newpath); 14035a092d6Snicm } 14135a092d6Snicm 142a2f71d82Snicm void 14365439d22Snicm parseenvironment(void) 144a2f71d82Snicm { 14507bd7befSnicm char *env, path[256]; 14607bd7befSnicm long pid; 14707bd7befSnicm int idx; 148a2f71d82Snicm 149a2f71d82Snicm if ((env = getenv("TMUX")) == NULL) 150a2f71d82Snicm return; 151a2f71d82Snicm 152019abd7fSnicm if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) 153a2f71d82Snicm return; 15407bd7befSnicm environ_path = xstrdup(path); 15507bd7befSnicm environ_pid = pid; 15607bd7befSnicm environ_idx = idx; 157a2f71d82Snicm } 158a2f71d82Snicm 159311827fbSnicm char * 16065439d22Snicm makesocketpath(const char *label) 161311827fbSnicm { 162e3b883c9Snicm char base[MAXPATHLEN], *path, *s; 163311827fbSnicm struct stat sb; 164311827fbSnicm u_int uid; 165311827fbSnicm 166311827fbSnicm uid = getuid(); 167e3b883c9Snicm if ((s = getenv("TMPDIR")) == NULL || *s == '\0') 168e3b883c9Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 169e3b883c9Snicm else 170e3b883c9Snicm xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 171311827fbSnicm 172311827fbSnicm if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 173311827fbSnicm return (NULL); 174311827fbSnicm 175311827fbSnicm if (lstat(base, &sb) != 0) 176311827fbSnicm return (NULL); 177311827fbSnicm if (!S_ISDIR(sb.st_mode)) { 178311827fbSnicm errno = ENOTDIR; 179311827fbSnicm return (NULL); 180311827fbSnicm } 181311827fbSnicm if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { 182311827fbSnicm errno = EACCES; 183311827fbSnicm return (NULL); 184311827fbSnicm } 185311827fbSnicm 186311827fbSnicm xasprintf(&path, "%s/%s", base, label); 187311827fbSnicm return (path); 188311827fbSnicm } 189311827fbSnicm 190a0ee4254Snicm void 191a0ee4254Snicm setblocking(int fd, int state) 192a0ee4254Snicm { 193a0ee4254Snicm int mode; 194a0ee4254Snicm 195a0ee4254Snicm if ((mode = fcntl(fd, F_GETFL)) != -1) { 196a0ee4254Snicm if (!state) 197a0ee4254Snicm mode |= O_NONBLOCK; 198a0ee4254Snicm else 199a0ee4254Snicm mode &= ~O_NONBLOCK; 200a0ee4254Snicm fcntl(fd, F_SETFL, mode); 201a0ee4254Snicm } 202a0ee4254Snicm } 203a0ee4254Snicm 2047724d0b0Snicm __dead void 2057724d0b0Snicm shell_exec(const char *shell, const char *shellcmd) 2067724d0b0Snicm { 2077724d0b0Snicm const char *shellname, *ptr; 2087724d0b0Snicm char *argv0; 2097724d0b0Snicm 2107724d0b0Snicm ptr = strrchr(shell, '/'); 2117724d0b0Snicm if (ptr != NULL && *(ptr + 1) != '\0') 2127724d0b0Snicm shellname = ptr + 1; 2137724d0b0Snicm else 2147724d0b0Snicm shellname = shell; 2157724d0b0Snicm if (login_shell) 2167724d0b0Snicm xasprintf(&argv0, "-%s", shellname); 2177724d0b0Snicm else 2187724d0b0Snicm xasprintf(&argv0, "%s", shellname); 2197724d0b0Snicm setenv("SHELL", shell, 1); 2207724d0b0Snicm 221a0ee4254Snicm setblocking(STDIN_FILENO, 1); 222a0ee4254Snicm setblocking(STDOUT_FILENO, 1); 223a0ee4254Snicm setblocking(STDERR_FILENO, 1); 224498ee386Snicm closefrom(STDERR_FILENO + 1); 225498ee386Snicm 2267724d0b0Snicm execl(shell, argv0, "-c", shellcmd, (char *) NULL); 2277724d0b0Snicm fatal("execl failed"); 2287724d0b0Snicm } 2297724d0b0Snicm 230311827fbSnicm int 231311827fbSnicm main(int argc, char **argv) 232311827fbSnicm { 233311827fbSnicm struct passwd *pw; 23465439d22Snicm char *s, *path, *label, *home, **var; 235c9347692Snicm int opt, flags, quiet, keys; 236311827fbSnicm 237bbc36c02Snicm #ifdef DEBUG 238bbc36c02Snicm malloc_options = (char *) "AFGJPX"; 239bbc36c02Snicm #endif 240bbc36c02Snicm 241c9347692Snicm quiet = flags = 0; 24265439d22Snicm label = path = NULL; 24365b1f011Snicm login_shell = (**argv == '-'); 24490896992Snicm while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUv")) != -1) { 245311827fbSnicm switch (opt) { 246311827fbSnicm case '2': 247311827fbSnicm flags |= IDENTIFY_256COLOURS; 248311827fbSnicm flags &= ~IDENTIFY_88COLOURS; 249311827fbSnicm break; 250311827fbSnicm case '8': 251311827fbSnicm flags |= IDENTIFY_88COLOURS; 252311827fbSnicm flags &= ~IDENTIFY_256COLOURS; 253311827fbSnicm break; 254ef8ef121Snicm case 'c': 255*7d053cf9Snicm free(shell_cmd); 25665439d22Snicm shell_cmd = xstrdup(optarg); 257ef8ef121Snicm break; 25890896992Snicm case 'C': 25990896992Snicm if (flags & IDENTIFY_CONTROL) 26090896992Snicm flags |= IDENTIFY_TERMIOS; 26190896992Snicm else 26290896992Snicm flags |= IDENTIFY_CONTROL; 26390896992Snicm break; 264311827fbSnicm case 'f': 265*7d053cf9Snicm free(cfg_file); 266311827fbSnicm cfg_file = xstrdup(optarg); 267311827fbSnicm break; 26865b1f011Snicm case 'l': 26965b1f011Snicm login_shell = 1; 27065b1f011Snicm break; 271311827fbSnicm case 'L': 272*7d053cf9Snicm free(label); 273311827fbSnicm label = xstrdup(optarg); 274311827fbSnicm break; 275481abfa9Snicm case 'q': 276f79ec119Snicm quiet = 1; 277481abfa9Snicm break; 278311827fbSnicm case 'S': 279*7d053cf9Snicm free(path); 280311827fbSnicm path = xstrdup(optarg); 281311827fbSnicm break; 282311827fbSnicm case 'u': 283311827fbSnicm flags |= IDENTIFY_UTF8; 284311827fbSnicm break; 285311827fbSnicm case 'v': 286311827fbSnicm debug_level++; 287311827fbSnicm break; 288311827fbSnicm default: 289311827fbSnicm usage(); 290311827fbSnicm } 291311827fbSnicm } 292311827fbSnicm argc -= optind; 293311827fbSnicm argv += optind; 294311827fbSnicm 29565439d22Snicm if (shell_cmd != NULL && argc != 0) 296ef8ef121Snicm usage(); 297ef8ef121Snicm 298461e0674Snicm if (!(flags & IDENTIFY_UTF8)) { 299461e0674Snicm /* 300461e0674Snicm * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 301461e0674Snicm * exist (in that order) to contain UTF-8, it is a safe 302461e0674Snicm * assumption that either they are using a UTF-8 terminal, or 303461e0674Snicm * if not they know that output from UTF-8-capable programs may 304461e0674Snicm * be wrong. 305461e0674Snicm */ 30654810b57Snicm if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { 30754810b57Snicm if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') 308461e0674Snicm s = getenv("LANG"); 309461e0674Snicm } 310a7a86d62Snicm if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 311a7a86d62Snicm strcasestr(s, "UTF8") != NULL)) 312461e0674Snicm flags |= IDENTIFY_UTF8; 313461e0674Snicm } 314461e0674Snicm 315bd0e97c4Snicm environ_init(&global_environ); 316bd0e97c4Snicm for (var = environ; *var != NULL; var++) 317bd0e97c4Snicm environ_put(&global_environ, *var); 318bd0e97c4Snicm 319f79ec119Snicm options_init(&global_options, NULL); 3203affa6cbSnicm options_table_populate_tree(server_options_table, &global_options); 3213affa6cbSnicm options_set_number(&global_options, "quiet", quiet); 322f79ec119Snicm 323eaecedb2Snicm options_init(&global_s_options, NULL); 3243affa6cbSnicm options_table_populate_tree(session_options_table, &global_s_options); 3253affa6cbSnicm options_set_string(&global_s_options, "default-shell", "%s", getshell()); 326311827fbSnicm 3273affa6cbSnicm options_init(&global_w_options, NULL); 3283affa6cbSnicm options_table_populate_tree(window_options_table, &global_w_options); 3293affa6cbSnicm 3303affa6cbSnicm /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 331bd0e97c4Snicm if (flags & IDENTIFY_UTF8) { 3323affa6cbSnicm options_set_number(&global_s_options, "status-utf8", 1); 333699995ddSnicm options_set_number(&global_s_options, "mouse-utf8", 1); 3343affa6cbSnicm options_set_number(&global_w_options, "utf8", 1); 335bd0e97c4Snicm } 336bd0e97c4Snicm 3373affa6cbSnicm /* Override keys to vi if VISUAL or EDITOR are set. */ 338c9347692Snicm if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 339c9347692Snicm if (strrchr(s, '/') != NULL) 340c9347692Snicm s = strrchr(s, '/') + 1; 341c9347692Snicm if (strstr(s, "vi") != NULL) 342c9347692Snicm keys = MODEKEY_VI; 3433affa6cbSnicm else 3443affa6cbSnicm keys = MODEKEY_EMACS; 3453affa6cbSnicm options_set_number(&global_s_options, "status-keys", keys); 3463affa6cbSnicm options_set_number(&global_w_options, "mode-keys", keys); 347c9347692Snicm } 348c9347692Snicm 34965439d22Snicm /* Locate the configuration file. */ 350311827fbSnicm if (cfg_file == NULL) { 351311827fbSnicm home = getenv("HOME"); 352311827fbSnicm if (home == NULL || *home == '\0') { 353311827fbSnicm pw = getpwuid(getuid()); 354311827fbSnicm if (pw != NULL) 355311827fbSnicm home = pw->pw_dir; 356311827fbSnicm } 357311827fbSnicm xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); 3589a53e128Snicm if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { 359*7d053cf9Snicm free(cfg_file); 360311827fbSnicm cfg_file = NULL; 361311827fbSnicm } 362311827fbSnicm } 363311827fbSnicm 3642fa50cfcSnicm /* 36565439d22Snicm * Figure out the socket path. If specified on the command-line with -S 36665439d22Snicm * or -L, use it, otherwise try $TMUX or assume -L default. 3672fa50cfcSnicm */ 36865439d22Snicm parseenvironment(); 3692fa50cfcSnicm if (path == NULL) { 37065439d22Snicm /* If no -L, use the environment. */ 3712fa50cfcSnicm if (label == NULL) { 37265439d22Snicm if (environ_path != NULL) 37365439d22Snicm path = xstrdup(environ_path); 37465439d22Snicm else 375311827fbSnicm label = xstrdup("default"); 3762fa50cfcSnicm } 3772fa50cfcSnicm 3782fa50cfcSnicm /* -L or default set. */ 3792fa50cfcSnicm if (label != NULL) { 38065439d22Snicm if ((path = makesocketpath(label)) == NULL) { 38136fa3172Snicm fprintf(stderr, "can't create socket\n"); 382311827fbSnicm exit(1); 383311827fbSnicm } 3842fa50cfcSnicm } 3852fa50cfcSnicm } 386*7d053cf9Snicm free(label); 38765439d22Snicm if (realpath(path, socket_path) == NULL) 38865439d22Snicm strlcpy(socket_path, path, sizeof socket_path); 389*7d053cf9Snicm free(path); 390311827fbSnicm 39165439d22Snicm /* Set process title. */ 39265439d22Snicm setproctitle("%s (%s)", __progname, socket_path); 393311827fbSnicm 39465439d22Snicm /* Pass control to the client. */ 39565439d22Snicm ev_base = event_init(); 39665439d22Snicm exit(client_main(argc, argv, flags)); 397fd234c13Snicm } 398