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