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