1 /* $Id: tmux.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv 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 <errno.h> 23 #include <event.h> 24 #include <fcntl.h> 25 #include <pwd.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "tmux.h" 31 32 #if defined(DEBUG) && defined(__OpenBSD__) 33 extern char *malloc_options; 34 #endif 35 36 struct options global_options; /* server options */ 37 struct options global_s_options; /* session options */ 38 struct options global_w_options; /* window options */ 39 struct environ global_environ; 40 41 struct event_base *ev_base; 42 43 char *cfg_file; 44 char *shell_cmd; 45 int debug_level; 46 time_t start_time; 47 char socket_path[MAXPATHLEN]; 48 int login_shell; 49 char *environ_path; 50 pid_t environ_pid = -1; 51 int environ_idx = -1; 52 53 __dead void usage(void); 54 void parseenvironment(void); 55 char *makesocketpath(const char *); 56 57 #ifndef HAVE___PROGNAME 58 char *__progname = (char *) "tmux"; 59 #endif 60 61 __dead void 62 usage(void) 63 { 64 fprintf(stderr, 65 "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" 66 " [-S socket-path] [command [flags]]\n", 67 __progname); 68 exit(1); 69 } 70 71 void 72 logfile(const char *name) 73 { 74 char *path; 75 76 log_close(); 77 if (debug_level > 0) { 78 xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); 79 log_open_file(debug_level, path); 80 xfree(path); 81 } 82 } 83 84 const char * 85 getshell(void) 86 { 87 struct passwd *pw; 88 const char *shell; 89 90 shell = getenv("SHELL"); 91 if (checkshell(shell)) 92 return (shell); 93 94 pw = getpwuid(getuid()); 95 if (pw != NULL && checkshell(pw->pw_shell)) 96 return (pw->pw_shell); 97 98 return (_PATH_BSHELL); 99 } 100 101 int 102 checkshell(const char *shell) 103 { 104 if (shell == NULL || *shell == '\0' || areshell(shell)) 105 return (0); 106 if (access(shell, X_OK) != 0) 107 return (0); 108 return (1); 109 } 110 111 int 112 areshell(const char *shell) 113 { 114 const char *progname, *ptr; 115 116 if ((ptr = strrchr(shell, '/')) != NULL) 117 ptr++; 118 else 119 ptr = shell; 120 progname = __progname; 121 if (*progname == '-') 122 progname++; 123 if (strcmp(ptr, progname) == 0) 124 return (1); 125 return (0); 126 } 127 128 void 129 parseenvironment(void) 130 { 131 char *env, path[256]; 132 long pid; 133 int idx; 134 135 if ((env = getenv("TMUX")) == NULL) 136 return; 137 138 if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) 139 return; 140 environ_path = xstrdup(path); 141 environ_pid = pid; 142 environ_idx = idx; 143 } 144 145 char * 146 makesocketpath(const char *label) 147 { 148 char base[MAXPATHLEN], *path, *s; 149 struct stat sb; 150 u_int uid; 151 152 uid = getuid(); 153 if ((s = getenv("TMPDIR")) == NULL || *s == '\0') 154 xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); 155 else 156 xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); 157 158 if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) 159 return (NULL); 160 161 if (lstat(base, &sb) != 0) 162 return (NULL); 163 if (!S_ISDIR(sb.st_mode)) { 164 errno = ENOTDIR; 165 return (NULL); 166 } 167 if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { 168 errno = EACCES; 169 return (NULL); 170 } 171 172 xasprintf(&path, "%s/%s", base, label); 173 return (path); 174 } 175 176 void 177 setblocking(int fd, int state) 178 { 179 int mode; 180 181 if ((mode = fcntl(fd, F_GETFL)) != -1) { 182 if (!state) 183 mode |= O_NONBLOCK; 184 else 185 mode &= ~O_NONBLOCK; 186 fcntl(fd, F_SETFL, mode); 187 } 188 } 189 190 __dead void 191 shell_exec(const char *shell, const char *shellcmd) 192 { 193 const char *shellname, *ptr; 194 char *argv0; 195 196 ptr = strrchr(shell, '/'); 197 if (ptr != NULL && *(ptr + 1) != '\0') 198 shellname = ptr + 1; 199 else 200 shellname = shell; 201 if (login_shell) 202 xasprintf(&argv0, "-%s", shellname); 203 else 204 xasprintf(&argv0, "%s", shellname); 205 setenv("SHELL", shell, 1); 206 207 setblocking(STDIN_FILENO, 1); 208 setblocking(STDOUT_FILENO, 1); 209 setblocking(STDERR_FILENO, 1); 210 closefrom(STDERR_FILENO + 1); 211 212 execl(shell, argv0, "-c", shellcmd, (char *) NULL); 213 fatal("execl failed"); 214 } 215 216 int 217 main(int argc, char **argv) 218 { 219 struct passwd *pw; 220 struct keylist *keylist; 221 char *s, *path, *label, *home, **var; 222 int opt, flags, quiet, keys; 223 224 #if defined(DEBUG) && defined(__OpenBSD__) 225 malloc_options = (char *) "AFGJPX"; 226 #endif 227 228 quiet = flags = 0; 229 label = path = NULL; 230 login_shell = (**argv == '-'); 231 while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) { 232 switch (opt) { 233 case '2': 234 flags |= IDENTIFY_256COLOURS; 235 flags &= ~IDENTIFY_88COLOURS; 236 break; 237 case '8': 238 flags |= IDENTIFY_88COLOURS; 239 flags &= ~IDENTIFY_256COLOURS; 240 break; 241 case 'c': 242 if (shell_cmd != NULL) 243 xfree(shell_cmd); 244 shell_cmd = xstrdup(optarg); 245 break; 246 case 'V': 247 printf("%s %s\n", __progname, VERSION); 248 exit(0); 249 case 'f': 250 if (cfg_file != NULL) 251 xfree(cfg_file); 252 cfg_file = xstrdup(optarg); 253 break; 254 case 'l': 255 login_shell = 1; 256 break; 257 case 'L': 258 if (label != NULL) 259 xfree(label); 260 label = xstrdup(optarg); 261 break; 262 case 'q': 263 quiet = 1; 264 break; 265 case 'S': 266 if (path != NULL) 267 xfree(path); 268 path = xstrdup(optarg); 269 break; 270 case 'u': 271 flags |= IDENTIFY_UTF8; 272 break; 273 case 'v': 274 debug_level++; 275 break; 276 default: 277 usage(); 278 } 279 } 280 argc -= optind; 281 argv += optind; 282 283 if (shell_cmd != NULL && argc != 0) 284 usage(); 285 286 log_open_tty(debug_level); 287 288 if (!(flags & IDENTIFY_UTF8)) { 289 /* 290 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG 291 * exist (in that order) to contain UTF-8, it is a safe 292 * assumption that either they are using a UTF-8 terminal, or 293 * if not they know that output from UTF-8-capable programs may 294 * be wrong. 295 */ 296 if ((s = getenv("LC_ALL")) == NULL) { 297 if ((s = getenv("LC_CTYPE")) == NULL) 298 s = getenv("LANG"); 299 } 300 if (s != NULL && (strcasestr(s, "UTF-8") != NULL || 301 strcasestr(s, "UTF8") != NULL)) 302 flags |= IDENTIFY_UTF8; 303 } 304 305 environ_init(&global_environ); 306 for (var = environ; *var != NULL; var++) 307 environ_put(&global_environ, *var); 308 309 options_init(&global_options, NULL); 310 options_table_populate_tree(server_options_table, &global_options); 311 options_set_number(&global_options, "quiet", quiet); 312 313 options_init(&global_s_options, NULL); 314 options_table_populate_tree(session_options_table, &global_s_options); 315 options_set_string(&global_s_options, "default-shell", "%s", getshell()); 316 317 options_init(&global_w_options, NULL); 318 options_table_populate_tree(window_options_table, &global_w_options); 319 320 /* Set the prefix option (its a list, so not in the table). */ 321 keylist = xmalloc(sizeof *keylist); 322 ARRAY_INIT(keylist); 323 ARRAY_ADD(keylist, '\002'); 324 options_set_data(&global_s_options, "prefix", keylist, xfree); 325 326 /* Enable UTF-8 if the first client is on UTF-8 terminal. */ 327 if (flags & IDENTIFY_UTF8) { 328 options_set_number(&global_s_options, "status-utf8", 1); 329 options_set_number(&global_s_options, "mouse-utf8", 1); 330 options_set_number(&global_w_options, "utf8", 1); 331 } 332 333 /* Override keys to vi if VISUAL or EDITOR are set. */ 334 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 335 if (strrchr(s, '/') != NULL) 336 s = strrchr(s, '/') + 1; 337 if (strstr(s, "vi") != NULL) 338 keys = MODEKEY_VI; 339 else 340 keys = MODEKEY_EMACS; 341 options_set_number(&global_s_options, "status-keys", keys); 342 options_set_number(&global_w_options, "mode-keys", keys); 343 } 344 345 /* Locate the configuration file. */ 346 if (cfg_file == NULL) { 347 home = getenv("HOME"); 348 if (home == NULL || *home == '\0') { 349 pw = getpwuid(getuid()); 350 if (pw != NULL) 351 home = pw->pw_dir; 352 } 353 xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); 354 if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { 355 xfree(cfg_file); 356 cfg_file = NULL; 357 } 358 } 359 360 /* 361 * Figure out the socket path. If specified on the command-line with -S 362 * or -L, use it, otherwise try $TMUX or assume -L default. 363 */ 364 parseenvironment(); 365 if (path == NULL) { 366 /* If no -L, use the environment. */ 367 if (label == NULL) { 368 if (environ_path != NULL) 369 path = xstrdup(environ_path); 370 else 371 label = xstrdup("default"); 372 } 373 374 /* -L or default set. */ 375 if (label != NULL) { 376 if ((path = makesocketpath(label)) == NULL) { 377 log_warn("can't create socket"); 378 exit(1); 379 } 380 } 381 } 382 if (label != NULL) 383 xfree(label); 384 if (realpath(path, socket_path) == NULL) 385 strlcpy(socket_path, path, sizeof socket_path); 386 xfree(path); 387 388 #ifdef HAVE_SETPROCTITLE 389 /* Set process title. */ 390 setproctitle("%s (%s)", __progname, socket_path); 391 #endif 392 393 /* Pass control to the client. */ 394 ev_base = osdep_event_init(); 395 exit(client_main(argc, argv, flags)); 396 } 397