1 /* $OpenBSD: tmux.c,v 1.157 2015/11/24 21:19:46 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <fcntl.h> 26 #include <getopt.h> 27 #include <locale.h> 28 #include <paths.h> 29 #include <pwd.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <time.h> 33 #include <unistd.h> 34 35 #include "tmux.h" 36 37 #ifdef DEBUG 38 extern char *malloc_options; 39 #endif 40 41 struct options *global_options; /* server options */ 42 struct options *global_s_options; /* session options */ 43 struct options *global_w_options; /* window options */ 44 struct environ *global_environ; 45 46 char *shell_cmd; 47 time_t start_time; 48 char socket_path[PATH_MAX]; 49 50 __dead void usage(void); 51 char *makesocketpath(const char *); 52 53 __dead void 54 usage(void) 55 { 56 fprintf(stderr, 57 "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 58 " [-S socket-path] [command [flags]]\n", 59 __progname); 60 exit(1); 61 } 62 63 const char * 64 getshell(void) 65 { 66 struct passwd *pw; 67 const char *shell; 68 69 shell = getenv("SHELL"); 70 if (checkshell(shell)) 71 return (shell); 72 73 pw = getpwuid(getuid()); 74 if (pw != NULL && checkshell(pw->pw_shell)) 75 return (pw->pw_shell); 76 77 return (_PATH_BSHELL); 78 } 79 80 int 81 checkshell(const char *shell) 82 { 83 if (shell == NULL || *shell == '\0' || *shell != '/') 84 return (0); 85 if (areshell(shell)) 86 return (0); 87 if (access(shell, X_OK) != 0) 88 return (0); 89 return (1); 90 } 91 92 int 93 areshell(const char *shell) 94 { 95 const char *progname, *ptr; 96 97 if ((ptr = strrchr(shell, '/')) != NULL) 98 ptr++; 99 else 100 ptr = shell; 101 progname = __progname; 102 if (*progname == '-') 103 progname++; 104 if (strcmp(ptr, progname) == 0) 105 return (1); 106 return (0); 107 } 108 109 char * 110 makesocketpath(const char *label) 111 { 112 char base[PATH_MAX], realbase[PATH_MAX], *path, *s; 113 struct stat sb; 114 u_int uid; 115 116 uid = getuid(); 117 if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 118 xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 119 else 120 xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 121 122 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 123 return (NULL); 124 125 if (lstat(base, &sb) != 0) 126 return (NULL); 127 if (!S_ISDIR(sb.st_mode)) { 128 errno = ENOTDIR; 129 return (NULL); 130 } 131 if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 132 errno = EACCES; 133 return (NULL); 134 } 135 136 if (realpath(base, realbase) == NULL) 137 strlcpy(realbase, base, sizeof realbase); 138 139 xasprintf(&path, "%s/%s", realbase, label); 140 return (path); 141 } 142 143 void 144 setblocking(int fd, int state) 145 { 146 int mode; 147 148 if ((mode = fcntl(fd, F_GETFL)) != -1) { 149 if (!state) 150 mode |= O_NONBLOCK; 151 else 152 mode &= ~O_NONBLOCK; 153 fcntl(fd, F_SETFL, mode); 154 } 155 } 156 157 const char * 158 find_home(void) 159 { 160 struct passwd *pw; 161 static const char *home; 162 163 if (home != NULL) 164 return (home); 165 166 home = getenv("HOME"); 167 if (home == NULL || *home == '\0') { 168 pw = getpwuid(getuid()); 169 if (pw != NULL) 170 home = pw->pw_dir; 171 else 172 home = NULL; 173 } 174 175 return (home); 176 } 177 178 int 179 main(int argc, char **argv) 180 { 181 char *path, *label, **var, tmp[PATH_MAX]; 182 const char *s; 183 int opt, flags, keys; 184 185 #ifdef DEBUG 186 malloc_options = (char *) "AFGJPX"; 187 #endif 188 189 setlocale(LC_TIME, ""); 190 tzset(); 191 192 if (**argv == '-') 193 flags = CLIENT_LOGIN; 194 else 195 flags = 0; 196 197 label = path = NULL; 198 while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 199 switch (opt) { 200 case '2': 201 flags |= CLIENT_256COLOURS; 202 break; 203 case 'c': 204 free(shell_cmd); 205 shell_cmd = xstrdup(optarg); 206 break; 207 case 'C': 208 if (flags & CLIENT_CONTROL) 209 flags |= CLIENT_CONTROLCONTROL; 210 else 211 flags |= CLIENT_CONTROL; 212 break; 213 case 'f': 214 set_cfg_file(optarg); 215 break; 216 case 'l': 217 flags |= CLIENT_LOGIN; 218 break; 219 case 'L': 220 free(label); 221 label = xstrdup(optarg); 222 break; 223 case 'q': 224 break; 225 case 'S': 226 free(path); 227 path = xstrdup(optarg); 228 break; 229 case 'u': 230 flags |= CLIENT_UTF8; 231 break; 232 case 'v': 233 log_add_level(); 234 break; 235 default: 236 usage(); 237 } 238 } 239 argc -= optind; 240 argv += optind; 241 242 if (shell_cmd != NULL && argc != 0) 243 usage(); 244 245 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " 246 "recvfd proc exec tty ps", NULL) != 0) 247 err(1, "pledge"); 248 249 /* 250 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. 251 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain 252 * UTF-8, it is a safe assumption that either they are using a UTF-8 253 * terminal, or if not they know that output from UTF-8-capable 254 * programs may be wrong. 255 */ 256 if (getenv("TMUX") != NULL) 257 flags |= CLIENT_UTF8; 258 else { 259 s = getenv("LC_ALL"); 260 if (s == NULL || *s == '\0') 261 s = getenv("LC_CTYPE"); 262 if (s == NULL || *s == '\0') 263 s = getenv("LANG"); 264 if (s == NULL || *s == '\0') 265 s = ""; 266 if (strcasestr(s, "UTF-8") != NULL || 267 strcasestr(s, "UTF8") != NULL) 268 flags |= CLIENT_UTF8; 269 } 270 271 global_environ = environ_create(); 272 for (var = environ; *var != NULL; var++) 273 environ_put(global_environ, *var); 274 if (getcwd(tmp, sizeof tmp) != NULL) 275 environ_set(global_environ, "PWD", tmp); 276 277 global_options = options_create(NULL); 278 options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); 279 280 global_s_options = options_create(NULL); 281 options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); 282 options_set_string(global_s_options, "default-shell", "%s", getshell()); 283 284 global_w_options = options_create(NULL); 285 options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); 286 287 /* Override keys to vi if VISUAL or EDITOR are set. */ 288 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 289 if (strrchr(s, '/') != NULL) 290 s = strrchr(s, '/') + 1; 291 if (strstr(s, "vi") != NULL) 292 keys = MODEKEY_VI; 293 else 294 keys = MODEKEY_EMACS; 295 options_set_number(global_s_options, "status-keys", keys); 296 options_set_number(global_w_options, "mode-keys", keys); 297 } 298 299 /* 300 * Figure out the socket path. If specified on the command-line with -S 301 * or -L, use it, otherwise try $TMUX or assume -L default. 302 */ 303 if (path == NULL) { 304 /* If no -L, use the environment. */ 305 if (label == NULL) { 306 s = getenv("TMUX"); 307 if (s != NULL) { 308 path = xstrdup(s); 309 path[strcspn (path, ",")] = '\0'; 310 if (*path == '\0') { 311 free(path); 312 label = xstrdup("default"); 313 } 314 } else 315 label = xstrdup("default"); 316 } 317 318 /* -L or default set. */ 319 if (label != NULL) { 320 if ((path = makesocketpath(label)) == NULL) { 321 fprintf(stderr, "can't create socket: %s\n", 322 strerror(errno)); 323 exit(1); 324 } 325 } 326 } 327 free(label); 328 329 if (strlcpy(socket_path, path, sizeof socket_path) >= 330 sizeof socket_path) { 331 fprintf(stderr, "socket path too long: %s\n", path); 332 exit(1); 333 } 334 free(path); 335 336 /* Pass control to the client. */ 337 exit(client_main(event_init(), argc, argv, flags)); 338 } 339