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