tmux.c revision 1.160
1/* $OpenBSD: tmux.c,v 1.160 2015/11/24 22:27:22 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 37struct options *global_options; /* server options */ 38struct options *global_s_options; /* session options */ 39struct options *global_w_options; /* window options */ 40struct environ *global_environ; 41 42char *shell_cmd; 43struct timeval start_time; 44const char *socket_path; 45 46__dead void usage(void); 47static char *make_label(const char *); 48 49__dead void 50usage(void) 51{ 52 fprintf(stderr, 53 "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" 54 " [-S socket-path] [command [flags]]\n", 55 __progname); 56 exit(1); 57} 58 59const char * 60getshell(void) 61{ 62 struct passwd *pw; 63 const char *shell; 64 65 shell = getenv("SHELL"); 66 if (checkshell(shell)) 67 return (shell); 68 69 pw = getpwuid(getuid()); 70 if (pw != NULL && checkshell(pw->pw_shell)) 71 return (pw->pw_shell); 72 73 return (_PATH_BSHELL); 74} 75 76int 77checkshell(const char *shell) 78{ 79 if (shell == NULL || *shell == '\0' || *shell != '/') 80 return (0); 81 if (areshell(shell)) 82 return (0); 83 if (access(shell, X_OK) != 0) 84 return (0); 85 return (1); 86} 87 88int 89areshell(const char *shell) 90{ 91 const char *progname, *ptr; 92 93 if ((ptr = strrchr(shell, '/')) != NULL) 94 ptr++; 95 else 96 ptr = shell; 97 progname = __progname; 98 if (*progname == '-') 99 progname++; 100 if (strcmp(ptr, progname) == 0) 101 return (1); 102 return (0); 103} 104 105static char * 106make_label(const char *label) 107{ 108 char *base, resolved[PATH_MAX], *path, *s; 109 struct stat sb; 110 u_int uid; 111 int saved_errno; 112 113 if (label == NULL) 114 label = "default"; 115 116 uid = getuid(); 117 118 if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') 119 xasprintf(&base, "%s/tmux-%u", s, uid); 120 else 121 xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); 122 123 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 124 goto fail; 125 126 if (lstat(base, &sb) != 0) 127 goto fail; 128 if (!S_ISDIR(sb.st_mode)) { 129 errno = ENOTDIR; 130 goto fail; 131 } 132 if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { 133 errno = EACCES; 134 goto fail; 135 } 136 137 if (realpath(base, resolved) == NULL) 138 strlcpy(resolved, base, sizeof resolved); 139 xasprintf(&path, "%s/%s", resolved, label); 140 return (path); 141 142fail: 143 saved_errno = errno; 144 free(base); 145 errno = saved_errno; 146 return (NULL); 147} 148 149void 150setblocking(int fd, int state) 151{ 152 int mode; 153 154 if ((mode = fcntl(fd, F_GETFL)) != -1) { 155 if (!state) 156 mode |= O_NONBLOCK; 157 else 158 mode &= ~O_NONBLOCK; 159 fcntl(fd, F_SETFL, mode); 160 } 161} 162 163const char * 164find_home(void) 165{ 166 struct passwd *pw; 167 static const char *home; 168 169 if (home != NULL) 170 return (home); 171 172 home = getenv("HOME"); 173 if (home == NULL || *home == '\0') { 174 pw = getpwuid(getuid()); 175 if (pw != NULL) 176 home = pw->pw_dir; 177 else 178 home = NULL; 179 } 180 181 return (home); 182} 183 184int 185main(int argc, char **argv) 186{ 187 char *path, *label, **var, tmp[PATH_MAX]; 188 const char *s; 189 int opt, flags, keys; 190 191 setlocale(LC_TIME, ""); 192 tzset(); 193 194 if (**argv == '-') 195 flags = CLIENT_LOGIN; 196 else 197 flags = 0; 198 199 label = path = NULL; 200 while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { 201 switch (opt) { 202 case '2': 203 flags |= CLIENT_256COLOURS; 204 break; 205 case 'c': 206 free(shell_cmd); 207 shell_cmd = xstrdup(optarg); 208 break; 209 case 'C': 210 if (flags & CLIENT_CONTROL) 211 flags |= CLIENT_CONTROLCONTROL; 212 else 213 flags |= CLIENT_CONTROL; 214 break; 215 case 'f': 216 set_cfg_file(optarg); 217 break; 218 case 'l': 219 flags |= CLIENT_LOGIN; 220 break; 221 case 'L': 222 free(label); 223 label = xstrdup(optarg); 224 break; 225 case 'q': 226 break; 227 case 'S': 228 free(path); 229 path = xstrdup(optarg); 230 break; 231 case 'u': 232 flags |= CLIENT_UTF8; 233 break; 234 case 'v': 235 log_add_level(); 236 break; 237 default: 238 usage(); 239 } 240 } 241 argc -= optind; 242 argv += optind; 243 244 if (shell_cmd != NULL && argc != 0) 245 usage(); 246 247 if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " 248 "recvfd proc exec tty ps", NULL) != 0) 249 err(1, "pledge"); 250 251 /* 252 * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. 253 * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain 254 * UTF-8, it is a safe assumption that either they are using a UTF-8 255 * terminal, or if not they know that output from UTF-8-capable 256 * programs may be wrong. 257 */ 258 if (getenv("TMUX") != NULL) 259 flags |= CLIENT_UTF8; 260 else { 261 s = getenv("LC_ALL"); 262 if (s == NULL || *s == '\0') 263 s = getenv("LC_CTYPE"); 264 if (s == NULL || *s == '\0') 265 s = getenv("LANG"); 266 if (s == NULL || *s == '\0') 267 s = ""; 268 if (strcasestr(s, "UTF-8") != NULL || 269 strcasestr(s, "UTF8") != NULL) 270 flags |= CLIENT_UTF8; 271 } 272 273 global_environ = environ_create(); 274 for (var = environ; *var != NULL; var++) 275 environ_put(global_environ, *var); 276 if (getcwd(tmp, sizeof tmp) != NULL) 277 environ_set(global_environ, "PWD", tmp); 278 279 global_options = options_create(NULL); 280 options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); 281 282 global_s_options = options_create(NULL); 283 options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); 284 options_set_string(global_s_options, "default-shell", "%s", getshell()); 285 286 global_w_options = options_create(NULL); 287 options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); 288 289 /* Override keys to vi if VISUAL or EDITOR are set. */ 290 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 291 if (strrchr(s, '/') != NULL) 292 s = strrchr(s, '/') + 1; 293 if (strstr(s, "vi") != NULL) 294 keys = MODEKEY_VI; 295 else 296 keys = MODEKEY_EMACS; 297 options_set_number(global_s_options, "status-keys", keys); 298 options_set_number(global_w_options, "mode-keys", keys); 299 } 300 301 /* 302 * If socket is specified on the command-line with -S or -L, it is 303 * used. Otherwise, $TMUX is checked and if that fails "default" is 304 * used. 305 */ 306 if (path == NULL && label == NULL) { 307 s = getenv("TMUX"); 308 if (s != NULL && *s != '\0' && *s != ',') { 309 path = xstrdup(s); 310 path[strcspn (path, ",")] = '\0'; 311 } 312 } 313 if (path == NULL && (path = make_label(label)) == NULL) { 314 fprintf(stderr, "can't create socket: %s\n", strerror(errno)); 315 exit(1); 316 } 317 socket_path = path; 318 free(label); 319 320 /* Pass control to the client. */ 321 exit(client_main(event_init(), argc, argv, flags)); 322} 323